diff --git a/.github/workflows/mapbase_build-base.yml b/.github/workflows/mapbase_build-base.yml index 220facae44c..a7351411d9c 100644 --- a/.github/workflows/mapbase_build-base.yml +++ b/.github/workflows/mapbase_build-base.yml @@ -54,7 +54,7 @@ jobs: - uses: actions/checkout@v4 - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v1.1 + uses: compnerd/gha-setup-vsdevenv@v6 - name: Enable VS2022 working-directory: '${{inputs.branch}}/src/vpc_scripts' @@ -67,7 +67,7 @@ jobs: - name: Checkout Gamepad UI if: inputs.project-group == 'game' || inputs.project-group == 'all' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: entropy-zero/HL2-GamepadUI path: ${{inputs.branch}}/gamepadui @@ -104,108 +104,40 @@ jobs: # -------------------------------------------------------------------- - # "I'm invoking msbuild for each project individually, which looks a bit odd considering there is a solution file which should be able to invoke the builds in their proper order automatically, but passing the solution to msbuild doesn't seem to work." - # https://github.com/mapbase-source/source-sdk-2013/pull/162 - - - name: Build mathlib + - name: Build #if: steps.filter.outputs.game == 'true' working-directory: '${{inputs.branch}}/src' shell: cmd run: | - msbuild -m -p:Configuration=${{inputs.configuration}} mathlib\mathlib.vcxproj - - - name: Build Base Libraries - if: inputs.project-group == 'all' || inputs.project-group == 'game' || inputs.project-group == 'maptools' - working-directory: '${{inputs.branch}}/src' - shell: cmd - run: | - msbuild -m -p:Configuration=${{inputs.configuration}} raytrace\raytrace.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} tier1\tier1.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} vgui2\vgui_controls\vgui_controls.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} vscript\vscript.vcxproj - - - name: Build Map Tools - if: inputs.project-group == 'all' || inputs.project-group == 'maptools' - working-directory: '${{inputs.branch}}/src' - shell: cmd - run: | - msbuild -m -p:Configuration=${{inputs.configuration}} fgdlib\fgdlib.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} utils\vbsp\vbsp.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} utils\vvis\vvis_dll.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} utils\vvis_launcher\vvis_launcher.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} utils\vrad\vrad_dll.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} utils\vrad_launcher\vrad_launcher.vcxproj - - - name: Build Shaders - if: inputs.project-group == 'shaders' - working-directory: '${{inputs.branch}}/src' - shell: cmd - run: | - msbuild -m -p:Configuration=${{inputs.configuration}} materialsystem\stdshaders\game_shader_dx9_${{inputs.game}}.vcxproj - - - name: Build Game - if: inputs.project-group == 'game' - working-directory: '${{inputs.branch}}/src' - shell: cmd - run: | - msbuild -m -p:Configuration=${{inputs.configuration}} responserules\runtime\responserules.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} game\client\client_${{inputs.game}}.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} game\server\server_${{inputs.game}}.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} game\gamepadui\gamepadui_${{inputs.game}}.vcxproj - - # TODO: Hook to game naming? - - name: Build everything - if: inputs.project-group == 'all' - working-directory: '${{inputs.branch}}/src' - shell: cmd - run: | - msbuild -m -p:Configuration=${{inputs.configuration}} responserules\runtime\responserules.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} materialsystem\stdshaders\game_shader_dx9_episodic.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} materialsystem\stdshaders\game_shader_dx9_hl2.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} game\client\client_ez2.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} game\server\server_ez2.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} game\gamepadui\gamepadui_ez2.vcxproj + devenv ${{inputs.solution-name}}.sln /upgrade + msbuild -m -t:Rebuild -p:Configuration=${{inputs.configuration}};Platform=x86 ${{inputs.solution-name}}.sln # -------------------------------------------------------------------- - - name: Publish Windows game DLLs - if: inputs.project-group == 'game' - uses: actions/upload-artifact@v3 - with: - name: 'Windows Game DLLs (server & client.dll) [${{ inputs.configuration }}]' - path: | - ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/client.dll - ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/server.dll - ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/gamepadui.dll - if-no-files-found: error - - - name: Publish Windows shader DLL - if: inputs.project-group == 'shaders' - uses: actions/upload-artifact@v3 + - name: Publish game binaries + if: inputs.project-group == 'game' || inputs.project-group == 'shaders' + uses: actions/upload-artifact@v4 with: - name: 'Windows Shader DLL (game_shader_dx9.dll) [${{ inputs.configuration }}]' + name: '${{inputs.project-group}}_${{inputs.game}}_win32_${{ inputs.configuration }}' path: | - ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/game_shader_dx9.dll + ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/*.dll if-no-files-found: error - - name: Publish Windows map tools + - name: Publish map tools if: inputs.project-group == 'maptools' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: 'Windows Map Tools [${{ inputs.configuration }}]' + name: '${{inputs.project-group}}_win32_${{ inputs.configuration }}' path: | - ${{inputs.branch}}/game/bin/vbsp.exe - ${{inputs.branch}}/game/bin/vvis.exe - ${{inputs.branch}}/game/bin/vvis_dll.dll - ${{inputs.branch}}/game/bin/vrad.exe - ${{inputs.branch}}/game/bin/vrad_dll.dll + ${{inputs.branch}}/game/bin/*.exe + ${{inputs.branch}}/game/bin/*.dll if-no-files-found: error - - name: Publish everything (Windows) + - name: Publish everything if: inputs.project-group == 'all' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: 'Everything (Windows) [${{ inputs.configuration }}]' + name: 'everything_win32_${{ inputs.configuration }}' path: | ${{inputs.branch}}/game/bin ${{inputs.branch}}/game/mod_*/bin @@ -287,31 +219,21 @@ jobs: # -------------------------------------------------------------------- - - name: Publish Linux game SOs - if: inputs.project-group == 'game' - uses: actions/upload-artifact@v3 - with: - name: 'Linux Game SOs (server & client.so) [${{ inputs.configuration }}]' - path: | - ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/client.so - ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/server.so - ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/gamepadui.so - if-no-files-found: error - - - name: Publish Linux shader SO - if: inputs.project-group == 'shaders' - uses: actions/upload-artifact@v3 + - name: Publish game binaries + if: inputs.project-group == 'game' || inputs.project-group == 'shaders' + uses: actions/upload-artifact@v4 with: - name: 'Linux Shader SO (game_shader_dx9.so) [${{ inputs.configuration }}]' + name: '${{inputs.project-group}}_${{inputs.game}}_linux32_${{ inputs.configuration }}' path: | - ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/game_shader_dx9.so + ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/*.so + !${{inputs.branch}}/game/mod_${{inputs.game}}/bin/*_srv.so if-no-files-found: error - #- name: Publish Linux map tools + #- name: Publish map tools # if: inputs.project-group == 'maptools' - # uses: actions/upload-artifact@v3 + # uses: actions/upload-artifact@v4 # with: - # name: 'Linux Map Tools [${{ inputs.configuration }}]' + # name: '${{inputs.project-group}}_linux32_${{ inputs.configuration }}' # path: | # ${{inputs.branch}}/game/bin/vbsp # ${{inputs.branch}}/game/bin/vvis @@ -322,11 +244,11 @@ jobs: # For now, don't publish the .dbg files even though we publish .pdb files on Windows # (they're too big) - - name: Publish everything (Linux) + - name: Publish everything if: inputs.project-group == 'all' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: 'Everything (Linux) [${{ inputs.configuration }}]' + name: 'everything_linux32_${{ inputs.configuration }}' path: | ${{inputs.branch}}/game/bin/*.so !${{inputs.branch}}/game/bin/*_srv.so diff --git a/README b/README index 28b7c27270f..8a0616c9588 100644 --- a/README +++ b/README @@ -15,7 +15,7 @@ Compiled mod binaries will be placed in game/mod_ez1/bin. To install your E:Z mo //========================================================================================================================= - Mapbase v7.2 - Source 2013 + Mapbase v7.3 - Source 2013 https://github.com/mapbase-source/source-sdk-2013 https://www.moddb.com/mods/mapbase @@ -132,7 +132,18 @@ Direct contributions: - https://github.com/mapbase-source/source-sdk-2013/pull/237 (Commander goal trace fix by Agrimar) - https://github.com/mapbase-source/source-sdk-2013/pull/245 (ViewPunch random fix by Mr0maks) - https://github.com/mapbase-source/source-sdk-2013/pull/248 (soundlevel_t conversation warning fix by Mechami) +- https://github.com/mapbase-source/source-sdk-2013/pull/266 ("OnPhysGunPull" output in CPhysicsProp by rlenhub) +- https://github.com/mapbase-source/source-sdk-2013/pull/292 (env_headcrabcanister random spawn type by arbabf) +- https://github.com/mapbase-source/source-sdk-2013/pull/294 (SDK_LightmappedGeneric editor blend swap fix by azzyr) +- https://github.com/mapbase-source/source-sdk-2013/pull/308 (BlurFilterY fix by Wikot235) +- https://github.com/mapbase-source/source-sdk-2013/pull/312 (Zombie improvements by Wikot235) +- https://github.com/mapbase-source/source-sdk-2013/pull/315 (env_flare crash fix by Wikot235) +- https://github.com/mapbase-source/source-sdk-2013/pull/324 (server-only info/func_null by SirYodaJedi) +- https://github.com/mapbase-source/source-sdk-2013/pull/333 (Cvar to transition levels while in MOVETYPE_NOCLIP by Wikot235) +- https://github.com/mapbase-source/source-sdk-2013/pull/342 (NaN particle cull radius fix by celisej567) - https://github.com/mapbase-source/mapbase-game-src/pull/1 (Advanced video options duplicate field name fix by arbabf; This is asset-based and not reflected in the code) +- https://github.com/mapbase-source/mapbase-game-src/pull/2 (gameinfo.txt typo fix by CarePackage17; This is asset-based and not reflected in the code) +- https://github.com/mapbase-source/mapbase-game-src/pull/3 (HudMessage cutoff fix by arbabf; This is asset-based and not reflected in the code) - Demo autorecord code provided by Klems - cc_emit crash fix provided by 1upD - Custom HL2 ammo crate models created by Rykah (Textures created by Blixibon; This is asset-based and, aside from the SLAM crate, not reflected in the code) @@ -159,8 +170,10 @@ Direct contributions: =-- https://github.com/mapbase-source/source-sdk-2013/pull/206 (Fix CScriptNetMsgHelper::WriteEntity()) =-- https://github.com/mapbase-source/source-sdk-2013/pull/213 (VScript HUD visibility control, optimizations for 3D skybox angles/fake worldportals) =-- https://github.com/mapbase-source/source-sdk-2013/pull/229 (VScript VGUI HUD viewport parenting, game_text and vgui_text_display VScript font fallback) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/260 (CScriptNetPropManager rewrite) =-- https://github.com/mapbase-source/source-sdk-2013/pull/261 (Misc VScript additions) =-- https://github.com/mapbase-source/source-sdk-2013/pull/279 (weapon_custom_scripted fixes) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/332 (Fix OOB access) == Contributions from z33ky: =-- https://github.com/mapbase-source/source-sdk-2013/pull/21 (Various GCC/Linux compilation fixes) @@ -174,6 +187,7 @@ Direct contributions: =-- https://github.com/mapbase-source/source-sdk-2013/pull/159 (Additional GCC/Linux compilation fixes) =-- https://github.com/mapbase-source/source-sdk-2013/pull/162 (VS2019 exception specification fix) =-- https://github.com/mapbase-source/source-sdk-2013/pull/170 (HL2 non-Episodic build fix) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/322 (Small Mapbase fixes) == Contributions from Petercov: =-- https://github.com/mapbase-source/source-sdk-2013/pull/182 (NPCs load dynamic interactions from all animation MDLs) @@ -183,6 +197,7 @@ Direct contributions: =-- https://github.com/mapbase-source/source-sdk-2013/pull/183 (Enhanced custom weapons support) =-- https://github.com/mapbase-source/source-sdk-2013/pull/230 (Caption fixes) =-- https://github.com/mapbase-source/source-sdk-2013/pull/231 (Sentence source bug fix) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/264 (Outputs for vgui_screen) //------------------------------------------------------------------------------------------------------------------------- diff --git a/mp/src/game/client/particlemgr.cpp b/mp/src/game/client/particlemgr.cpp index 0eb09a43a9c..615b7cc43e6 100644 --- a/mp/src/game/client/particlemgr.cpp +++ b/mp/src/game/client/particlemgr.cpp @@ -146,6 +146,8 @@ CParticleEffectBinding::CParticleEffectBinding() m_LastMin = m_Min; m_LastMax = m_Max; + m_flParticleCullRadius = 0.0f; + SetParticleCullRadius( 0.0f ); m_nActiveParticles = 0; diff --git a/mp/src/game/server/subs.cpp b/mp/src/game/server/subs.cpp index b2bf003f444..e7ad735453c 100644 --- a/mp/src/game/server/subs.cpp +++ b/mp/src/game/server/subs.cpp @@ -22,10 +22,10 @@ void CPointEntity::Spawn( void ) } -class CNullEntity : public CBaseEntity +class CNullEntity : public CServerOnlyEntity { public: - DECLARE_CLASS( CNullEntity, CBaseEntity ); + DECLARE_CLASS( CNullEntity, CServerOnlyEntity ); void Spawn( void ); }; diff --git a/sp/src/game/client/c_basecombatcharacter.cpp b/sp/src/game/client/c_basecombatcharacter.cpp index 46574523def..85dbb9d52e0 100644 --- a/sp/src/game/client/c_basecombatcharacter.cpp +++ b/sp/src/game/client/c_basecombatcharacter.cpp @@ -117,7 +117,8 @@ void C_BaseCombatCharacter::GetGlowEffectColor( float *r, float *g, float *b, fl *r = m_GlowColor.x; *g = m_GlowColor.y; *b = m_GlowColor.z; - *a = m_GlowAlpha; + if (a) + *a = m_GlowAlpha; } //----------------------------------------------------------------------------- diff --git a/sp/src/game/client/c_basecombatcharacter.h b/sp/src/game/client/c_basecombatcharacter.h index fad61bacb16..4941b1369bb 100644 --- a/sp/src/game/client/c_basecombatcharacter.h +++ b/sp/src/game/client/c_basecombatcharacter.h @@ -99,7 +99,7 @@ class C_BaseCombatCharacter : public C_BaseFlex #ifdef GLOWS_ENABLE CGlowObject *GetGlowObject( void ){ return m_pGlowEffect; } - virtual void GetGlowEffectColor( float *r, float *g, float *b, float *a ); + virtual void GetGlowEffectColor( float *r, float *g, float *b, float *a = NULL ); #endif // GLOWS_ENABLE #ifdef MAPBASE_VSCRIPT diff --git a/sp/src/game/client/c_baseentity.cpp b/sp/src/game/client/c_baseentity.cpp index 1d3de004b03..4a72bdb709e 100644 --- a/sp/src/game/client/c_baseentity.cpp +++ b/sp/src/game/client/c_baseentity.cpp @@ -485,6 +485,8 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities DEFINE_SCRIPTFUNC_NAMED( ScriptEntityToWorldTransform, "EntityToWorldTransform", "Get the entity's transform" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetPhysicsObject, "GetPhysicsObject", "Get the entity's physics object if it has one" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptPhysicsInitNormal, "PhysicsInitNormal", "Initializes the entity's physics object with the specified solid type, solid flags, and whether to start asleep" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptPhysicsDestroyObject, "PhysicsDestroyObject", "Destroys the entity's physics object" ) DEFINE_SCRIPTFUNC( GetWaterLevel, "Get current level of water submergence" ) diff --git a/sp/src/game/client/c_baseentity.h b/sp/src/game/client/c_baseentity.h index 422a59d48f1..aa383c73992 100644 --- a/sp/src/game/client/c_baseentity.h +++ b/sp/src/game/client/c_baseentity.h @@ -1206,6 +1206,8 @@ class C_BaseEntity : public IClientEntity HSCRIPT ScriptEntityToWorldTransform( void ); HSCRIPT ScriptGetPhysicsObject( void ); + void ScriptPhysicsInitNormal( int nSolidType, int nSolidFlags, bool createAsleep ); + void ScriptPhysicsDestroyObject() { VPhysicsDestroyObject(); } void ScriptSetParent( HSCRIPT hParent, const char *szAttachment ); HSCRIPT ScriptGetMoveParent( void ); diff --git a/sp/src/game/client/c_baseplayer.cpp b/sp/src/game/client/c_baseplayer.cpp index 7a296ad05c0..6435511e9fb 100644 --- a/sp/src/game/client/c_baseplayer.cpp +++ b/sp/src/game/client/c_baseplayer.cpp @@ -1136,6 +1136,9 @@ void C_BasePlayer::DetermineVguiInputMode( CUserCmd *pCmd ) // Kill all attack inputs if we're in vgui screen mode pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2 | IN_VALIDVGUIINPUT); +#ifdef MAPBASE + pCmd->buttons |= IN_VGUIMODE; +#endif // MAPBASE return; } #else @@ -1145,6 +1148,10 @@ void C_BasePlayer::DetermineVguiInputMode( CUserCmd *pCmd ) // Kill all attack inputs if we're in vgui screen mode pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2); +#ifdef MAPBASE + pCmd->buttons &= ~(IN_USE | IN_ATTACK3); + pCmd->buttons |= IN_VGUIMODE; +#endif // MAPBASE return; } #endif @@ -1209,6 +1216,10 @@ void C_BasePlayer::DetermineVguiInputMode( CUserCmd *pCmd ) // Kill all attack inputs if we're in vgui screen mode pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2); +#ifdef MAPBASE + pCmd->buttons &= ~(IN_USE | IN_ATTACK3); + pCmd->buttons |= IN_VGUIMODE; +#endif // MAPBASE } } diff --git a/sp/src/game/client/c_env_screenoverlay.cpp b/sp/src/game/client/c_env_screenoverlay.cpp index a31258829d0..e6d7e553219 100644 --- a/sp/src/game/client/c_env_screenoverlay.cpp +++ b/sp/src/game/client/c_env_screenoverlay.cpp @@ -219,6 +219,11 @@ enum SCREENEFFECT_EP2_ADVISOR_STUN, SCREENEFFECT_EP1_INTRO, SCREENEFFECT_EP2_GROGGY, + +#ifdef MAPBASE + SCREENEFFECT_MAPBASE_CHROMATIC_BLUR = 100, // Overlays 3 different frames of red, green, and blue tints respectively with different offsets + SCREENEFFECT_MAPBASE_CHROMATIC_ABERRATION, // Similar to above, except it stretches frames in addition to offsetting them +#endif }; // ============================================================================ @@ -303,6 +308,21 @@ void C_EnvScreenEffect::ReceiveMessage( int classID, bf_read &msg ) g_pScreenSpaceEffects->SetScreenSpaceEffectParams( "ep2_groggy", pKeys ); g_pScreenSpaceEffects->EnableScreenSpaceEffect( "ep2_groggy" ); } +#ifdef MAPBASE + else if ( m_nType == SCREENEFFECT_MAPBASE_CHROMATIC_BLUR || m_nType == SCREENEFFECT_MAPBASE_CHROMATIC_ABERRATION ) + { + if( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80 ) + return; + + // Set our keys + pKeys->SetFloat( "duration", m_flDuration ); + pKeys->SetInt( "fadeout", 0 ); + pKeys->SetInt( "stretch", m_nType == SCREENEFFECT_MAPBASE_CHROMATIC_ABERRATION ); + + g_pScreenSpaceEffects->SetScreenSpaceEffectParams( "mapbase_chromatic_aberration", pKeys ); + g_pScreenSpaceEffects->EnableScreenSpaceEffect( "mapbase_chromatic_aberration" ); + } +#endif pKeys->deleteThis(); } @@ -349,6 +369,25 @@ void C_EnvScreenEffect::ReceiveMessage( int classID, bf_read &msg ) g_pScreenSpaceEffects->SetScreenSpaceEffectParams( "ep2_groggy", pKeys ); } +#ifdef MAPBASE + else if ( m_nType == SCREENEFFECT_MAPBASE_CHROMATIC_BLUR || m_nType == SCREENEFFECT_MAPBASE_CHROMATIC_ABERRATION ) + { + if( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80 ) + return; + + // Create a keyvalue block to set these params + KeyValues *pKeys = new KeyValues( "keys" ); + if ( pKeys == NULL ) + return; + + // Set our keys + pKeys->SetFloat( "duration", m_flDuration ); + pKeys->SetInt( "fadeout", 1 ); + pKeys->SetInt( "stretch", m_nType == SCREENEFFECT_MAPBASE_CHROMATIC_ABERRATION ); + + g_pScreenSpaceEffects->SetScreenSpaceEffectParams( "mapbase_chromatic_aberration", pKeys ); + } +#endif break; } diff --git a/sp/src/game/client/c_impact_effects.cpp b/sp/src/game/client/c_impact_effects.cpp index c103c0d3451..77a108c8e95 100644 --- a/sp/src/game/client/c_impact_effects.cpp +++ b/sp/src/game/client/c_impact_effects.cpp @@ -105,18 +105,23 @@ extern PMaterialHandle g_Material_Spark; //----------------------------------------------------------------------------- void GetColorForSurface( trace_t *trace, Vector *color ) { - Vector baseColor, diffuseColor; - Vector end = trace->startpos + ( ( Vector )trace->endpos - ( Vector )trace->startpos ) * 1.1f; - + Vector baseColor = vec3_invalid, diffuseColor; + const char *kind; + if ( trace->DidHitWorld() ) { if ( trace->hitbox == 0 ) { + kind = "World"; + Vector end = trace->startpos + ( trace->endpos - trace->startpos ) * 1.1f; // If we hit the world, then ask the world for the fleck color - engine->TraceLineMaterialAndLighting( trace->startpos, end, diffuseColor, baseColor ); + if ( !engine->TraceLineMaterialAndLighting( trace->startpos, end, diffuseColor, baseColor ) ) { + baseColor = vec3_invalid; // Make sure this wasn't modified + } } else { + kind = "Static Prop"; // In this case we hit a static prop. staticpropmgr->GetStaticPropMaterialColorAndLighting( trace, trace->hitbox - 1, diffuseColor, baseColor ); } @@ -127,20 +132,24 @@ void GetColorForSurface( trace_t *trace, Vector *color ) C_BaseEntity *pEnt = trace->m_pEnt; if ( !pEnt ) { - Msg("Couldn't find surface in GetColorForSurface()\n"); - color->x = 255; - color->y = 255; - color->z = 255; - return; + kind = "Null-Entity"; + } else { + kind = "Entity"; + ICollideable *pCollide = pEnt->GetCollideable(); + int modelIndex = pCollide->GetCollisionModelIndex(); + model_t* pModel = const_cast(modelinfo->GetModel( modelIndex )); + + // Ask the model info about what we need to know + modelinfo->GetModelMaterialColorAndLighting( pModel, pCollide->GetCollisionOrigin(), + pCollide->GetCollisionAngles(), trace, diffuseColor, baseColor ); } + } - ICollideable *pCollide = pEnt->GetCollideable(); - int modelIndex = pCollide->GetCollisionModelIndex(); - model_t* pModel = const_cast(modelinfo->GetModel( modelIndex )); - - // Ask the model info about what we need to know - modelinfo->GetModelMaterialColorAndLighting( pModel, pCollide->GetCollisionOrigin(), - pCollide->GetCollisionAngles(), trace, diffuseColor, baseColor ); + if ( baseColor == vec3_invalid ) + { + Warning( "Couldn't find surface color of %s\n", kind ); + baseColor = Vector( .5f, .5f, .5f ); + diffuseColor = engine->GetLightForPoint( trace->endpos, true ); } //Get final light value diff --git a/sp/src/game/client/c_vguiscreen.cpp b/sp/src/game/client/c_vguiscreen.cpp index adf8830c22c..7695afd848e 100644 --- a/sp/src/game/client/c_vguiscreen.cpp +++ b/sp/src/game/client/c_vguiscreen.cpp @@ -38,10 +38,14 @@ extern vgui::IInputInternal *g_InputInternal; #define VGUI_SCREEN_MODE_RADIUS 80 //Precache the materials -CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectVGuiScreen ) -CLIENTEFFECT_MATERIAL( "engine/writez" ) +CLIENTEFFECT_REGISTER_BEGIN(PrecacheEffectVGuiScreen) +CLIENTEFFECT_MATERIAL("engine/writez") CLIENTEFFECT_REGISTER_END() +#ifdef MAPBASE +C_EntityClassList g_VGUIScreenList; +template <> C_VGuiScreen* C_EntityClassList::m_pClassList = NULL; +#endif // MAPBASE // ----------------------------------------------------------------------------- // // This is a cache of preloaded keyvalues. @@ -102,11 +106,19 @@ C_VGuiScreen::C_VGuiScreen() m_WriteZMaterial.Init( "engine/writez", TEXTURE_GROUP_VGUI ); m_OverlayMaterial.Init( m_WriteZMaterial ); + +#ifdef MAPBASE + g_VGUIScreenList.Insert(this); +#endif // MAPBASE } C_VGuiScreen::~C_VGuiScreen() { DestroyVguiScreen(); + +#ifdef MAPBASE + g_VGUIScreenList.Remove(this); +#endif // MAPBASE } //----------------------------------------------------------------------------- @@ -416,35 +428,70 @@ void C_VGuiScreen::ClientThink( void ) int px = (int)(u * m_nPixelWidth + 0.5f); int py = (int)(v * m_nPixelHeight + 0.5f); +#ifndef MAPBASE // Generate mouse input commands if ((px != m_nOldPx) || (py != m_nOldPy)) { - g_InputInternal->InternalCursorMoved( px, py ); + g_InputInternal->InternalCursorMoved(px, py); + m_nOldPx = px; m_nOldPy = py; } if (m_nButtonPressed & IN_ATTACK) { - g_InputInternal->SetMouseCodeState( MOUSE_LEFT, vgui::BUTTON_PRESSED ); + g_InputInternal->SetMouseCodeState(MOUSE_LEFT, vgui::BUTTON_PRESSED); g_InputInternal->InternalMousePressed(MOUSE_LEFT); } if (m_nButtonPressed & IN_ATTACK2) { - g_InputInternal->SetMouseCodeState( MOUSE_RIGHT, vgui::BUTTON_PRESSED ); - g_InputInternal->InternalMousePressed( MOUSE_RIGHT ); + g_InputInternal->SetMouseCodeState(MOUSE_RIGHT, vgui::BUTTON_PRESSED); + g_InputInternal->InternalMousePressed(MOUSE_RIGHT); } - if ( (m_nButtonReleased & IN_ATTACK) || m_bLoseThinkNextFrame) // for a button release on loosing focus + if ((m_nButtonReleased & IN_ATTACK) || m_bLoseThinkNextFrame) // for a button release on loosing focus { - g_InputInternal->SetMouseCodeState( MOUSE_LEFT, vgui::BUTTON_RELEASED ); - g_InputInternal->InternalMouseReleased( MOUSE_LEFT ); + g_InputInternal->SetMouseCodeState(MOUSE_LEFT, vgui::BUTTON_RELEASED); + g_InputInternal->InternalMouseReleased(MOUSE_LEFT); } if (m_nButtonReleased & IN_ATTACK2) { - g_InputInternal->SetMouseCodeState( MOUSE_RIGHT, vgui::BUTTON_RELEASED ); - g_InputInternal->InternalMouseReleased( MOUSE_RIGHT ); + g_InputInternal->SetMouseCodeState(MOUSE_RIGHT, vgui::BUTTON_RELEASED); + g_InputInternal->InternalMouseReleased(MOUSE_RIGHT); + } +#else + vgui::VPANEL focus = g_InputInternal->GetMouseOver(); + // Generate mouse input commands + if ((px != m_nOldPx) || (py != m_nOldPy)) + { + g_InputInternal->UpdateCursorPosInternal(px, py); + + m_nOldPx = px; + m_nOldPy = py; + + focus = pPanel->IsWithinTraverse(px, py, true); + g_InputInternal->SetMouseFocus(focus); + vgui::ivgui()->PostMessage(focus, new KeyValues("CursorMoved", "xpos", px, "ypos", py), NULL); } + for (int i = 0; i < 2; i++) + { + const int nBit = i ? IN_ATTACK2 : (IN_ATTACK | IN_USE); + const vgui::MouseCode nButton = i ? MOUSE_RIGHT : MOUSE_LEFT; + + if ((m_nButtonReleased & nBit) || ((m_nButtonState & nBit) && m_bLoseThinkNextFrame)) // for a button release on loosing focus + { + g_InputInternal->SetMouseCodeState(nButton, vgui::BUTTON_RELEASED); + vgui::ivgui()->PostMessage(focus, new KeyValues("MouseReleased", "code", nButton), NULL); + } + else if (m_nButtonPressed & nBit) + { + g_InputInternal->SetMouseCodeState(nButton, vgui::BUTTON_PRESSED); + vgui::ivgui()->PostMessage(focus, new KeyValues("MousePressed", "code", nButton), NULL); + } + } +#endif // !MAPBASE + + if ( m_bLoseThinkNextFrame == true ) { m_bLoseThinkNextFrame = false; @@ -627,6 +674,7 @@ bool C_VGuiScreen::IsInputOnlyToOwner( void ) return (m_fScreenFlags & VGUI_SCREEN_ONLY_USABLE_BY_OWNER) != 0; } +#ifndef MAPBASE //----------------------------------------------------------------------------- // // Enumator class for finding vgui screens close to the local player @@ -634,29 +682,29 @@ bool C_VGuiScreen::IsInputOnlyToOwner( void ) //----------------------------------------------------------------------------- class CVGuiScreenEnumerator : public IPartitionEnumerator { - DECLARE_CLASS_GAMEROOT( CVGuiScreenEnumerator, IPartitionEnumerator ); + DECLARE_CLASS_GAMEROOT(CVGuiScreenEnumerator, IPartitionEnumerator); public: - virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ); + virtual IterationRetval_t EnumElement(IHandleEntity* pHandleEntity); int GetScreenCount(); - C_VGuiScreen *GetVGuiScreen( int index ); + C_VGuiScreen* GetVGuiScreen(int index); private: CUtlVector< CHandle< C_VGuiScreen > > m_VguiScreens; }; -IterationRetval_t CVGuiScreenEnumerator::EnumElement( IHandleEntity *pHandleEntity ) +IterationRetval_t CVGuiScreenEnumerator::EnumElement(IHandleEntity* pHandleEntity) { - C_BaseEntity *pEnt = ClientEntityList().GetBaseEntityFromHandle( pHandleEntity->GetRefEHandle() ); - if ( pEnt == NULL ) + C_BaseEntity* pEnt = ClientEntityList().GetBaseEntityFromHandle(pHandleEntity->GetRefEHandle()); + if (pEnt == NULL) return ITERATION_CONTINUE; // FIXME.. pretty expensive... - C_VGuiScreen *pScreen = dynamic_cast(pEnt); - if ( pScreen ) + C_VGuiScreen* pScreen = dynamic_cast(pEnt); + if (pScreen) { - int i = m_VguiScreens.AddToTail( ); - m_VguiScreens[i].Set( pScreen ); + int i = m_VguiScreens.AddToTail(); + m_VguiScreens[i].Set(pScreen); } return ITERATION_CONTINUE; @@ -667,10 +715,12 @@ int CVGuiScreenEnumerator::GetScreenCount() return m_VguiScreens.Count(); } -C_VGuiScreen *CVGuiScreenEnumerator::GetVGuiScreen( int index ) +C_VGuiScreen* CVGuiScreenEnumerator::GetVGuiScreen(int index) { return m_VguiScreens[index].Get(); -} +} +#endif // !MAPBASE + //----------------------------------------------------------------------------- @@ -704,18 +754,29 @@ C_BaseEntity *FindNearbyVguiScreen( const Vector &viewPosition, const QAngle &vi Ray_t lookRay; lookRay.Init( viewPosition, lookEnd ); +#ifndef MAPBASE // Look for vgui screens that are close to the player CVGuiScreenEnumerator localScreens; - partition->EnumerateElementsInSphere( PARTITION_CLIENT_NON_STATIC_EDICTS, viewPosition, VGUI_SCREEN_MODE_RADIUS, false, &localScreens ); + partition->EnumerateElementsInSphere(PARTITION_CLIENT_NON_STATIC_EDICTS, viewPosition, VGUI_SCREEN_MODE_RADIUS, false, &localScreens); +#endif // !MAPBASE Vector vecOut, vecViewDelta; float flBestDist = 2.0f; C_VGuiScreen *pBestScreen = NULL; +#ifndef MAPBASE for (int i = localScreens.GetScreenCount(); --i >= 0; ) +#else + for (C_VGuiScreen* pScreen = g_VGUIScreenList.m_pClassList; pScreen != NULL; pScreen = pScreen->m_pNext) +#endif // !MAPBASE { - C_VGuiScreen *pScreen = localScreens.GetVGuiScreen(i); - +#ifndef MAPBASE + C_VGuiScreen* pScreen = localScreens.GetVGuiScreen(i); +#else + // Skip if out of PVS + if (pScreen->IsDormant()) + continue; +#endif if ( pScreen->IsAttachedToViewModel() ) continue; @@ -865,11 +926,21 @@ vgui::Panel *CVGuiScreenPanel::CreateControlByName(const char *controlName) //----------------------------------------------------------------------------- // Purpose: Called when the user presses a button //----------------------------------------------------------------------------- -void CVGuiScreenPanel::OnCommand( const char *command) +void CVGuiScreenPanel::OnCommand(const char* command) { - if ( Q_stricmp( command, "vguicancel" ) ) + if (Q_stricmp(command, "vguicancel")) { - engine->ClientCmd( const_cast( command ) ); +#ifdef MAPBASE + if (m_hEntity && m_hEntity->IsServerEntity()) + { + KeyValues* pCommand = new KeyValues("EntityCommand"); + pCommand->SetInt("entindex", m_hEntity->index); + pCommand->SetString("command_data", command); + engine->ServerCmdKeyValues(pCommand); + } + else +#endif + engine->ClientCmd(const_cast(command)); } BaseClass::OnCommand(command); diff --git a/sp/src/game/client/c_vguiscreen.h b/sp/src/game/client/c_vguiscreen.h index 71780093c5b..331fbc78627 100644 --- a/sp/src/game/client/c_vguiscreen.h +++ b/sp/src/game/client/c_vguiscreen.h @@ -66,6 +66,10 @@ class C_VGuiScreen : public C_BaseEntity public: DECLARE_CLIENTCLASS(); +#ifdef MAPBASE + C_VGuiScreen* m_pNext; +#endif // MAPBASE + C_VGuiScreen(); ~C_VGuiScreen(); diff --git a/sp/src/game/client/client_ez2.vpc b/sp/src/game/client/client_ez2.vpc index b3be97355ba..96137862542 100644 --- a/sp/src/game/client/client_ez2.vpc +++ b/sp/src/game/client/client_ez2.vpc @@ -43,12 +43,14 @@ $Project "Client (Entropy Zero 2)" $File "ez2\c_npc_crabsynth.h" $File "ez2\c_npc_wilson.cpp" $File "ez2\c_npc_wilson.h" + $File "ez2\c_weapon_oicw.cpp" $File "ez2\c_weapon_pulse_pistol.cpp" $File "ez2\zombiegooproxy.cpp" $File "ez2\hud_slamstatus.cpp" $File "ezu\hud_healthvialstatus.cpp" $File "workshop_publish.cpp" [!$NO_STEAM] + $File "$SRCDIR\game\shared\workshop_mount.cpp" [!$NO_STEAM] } $Folder "HL2 DLL" { diff --git a/sp/src/game/client/episodic/episodic_screenspaceeffects.cpp b/sp/src/game/client/episodic/episodic_screenspaceeffects.cpp index a2b59bbcfb8..fd8cafb2827 100644 --- a/sp/src/game/client/episodic/episodic_screenspaceeffects.cpp +++ b/sp/src/game/client/episodic/episodic_screenspaceeffects.cpp @@ -462,3 +462,225 @@ void CEP2StunEffect::Render( int x, int y, int w, int h ) pRenderContext->MatrixMode( MATERIAL_PROJECTION ); pRenderContext->PopMatrix(); } + +// ================================================================================================================ +// +// Chromatic Aberration +// +// ================================================================================================================ + +#ifdef MAPBASE +ConVar r_chromatic_aberration_offset( "r_chromatic_aberration_offset", "8.0" ); +ConVar r_chromatic_aberration_intensity( "r_chromatic_aberration_intensity", "0.2" ); +ConVar r_chromatic_aberration_noise( "r_chromatic_aberration_noise", "4.0" ); + +ConVar r_chromatic_aberration_frame1_clr( "r_chromatic_aberration_frame1_clr", "1.0 0.0 0.0 1.0" ); +ConVar r_chromatic_aberration_frame1_offset_x( "r_chromatic_aberration_frame1_offset_x", "1.0" ); +ConVar r_chromatic_aberration_frame1_offset_y( "r_chromatic_aberration_frame1_offset_y", "4.0" ); + +ConVar r_chromatic_aberration_frame2_clr( "r_chromatic_aberration_frame2_clr", "0.0 1.0 0.0 1.0" ); +ConVar r_chromatic_aberration_frame2_offset_x( "r_chromatic_aberration_frame2_offset_x", "-5.0" ); +ConVar r_chromatic_aberration_frame2_offset_y( "r_chromatic_aberration_frame2_offset_y", "-1.0" ); + +ConVar r_chromatic_aberration_frame3_clr( "r_chromatic_aberration_frame3_clr", "0.0 0.0 1.0 1.0" ); +ConVar r_chromatic_aberration_frame3_offset_x( "r_chromatic_aberration_frame3_offset_x", "3.0" ); +ConVar r_chromatic_aberration_frame3_offset_y( "r_chromatic_aberration_frame3_offset_y", "-3.0" ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChromaticAberrationEffect::Init( void ) +{ + m_flDuration = 0.0f; + m_flFinishTime = 0.0f; + m_bUpdateView = true; + + KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" ); + pVMTKeyValues->SetString( "$basetexture", STUN_TEXTURE ); + m_EffectMaterial.Init( "__stuneffect", TEXTURE_GROUP_CLIENT_EFFECTS, pVMTKeyValues ); + m_StunTexture.Init( STUN_TEXTURE, TEXTURE_GROUP_CLIENT_EFFECTS ); +} + +void CChromaticAberrationEffect::Shutdown( void ) +{ + m_EffectMaterial.Shutdown(); + m_StunTexture.Shutdown(); +} + +//------------------------------------------------------------------------------ +// Purpose: Pick up changes in our parameters +//------------------------------------------------------------------------------ +void CChromaticAberrationEffect::SetParameters( KeyValues *params ) +{ + if( params->FindKey( "duration" ) ) + { + m_flDuration = params->GetFloat( "duration" ); + m_flFinishTime = gpGlobals->curtime + m_flDuration; + m_bUpdateView = true; + } + + if( params->FindKey( "fadeout" ) ) + { + m_bFadeOut = ( params->GetInt( "fadeout" ) == 1 ); + } + + if( params->FindKey( "stretch" ) ) + { + m_bStretch = ( params->GetInt( "stretch" ) == 1 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Render the effect +//----------------------------------------------------------------------------- +void CChromaticAberrationEffect::RenderColorFrame( CMatRenderContextPtr &pRenderContext, float flEffectPerc, int nColorMode, int x, int y, int w, int h ) +{ + // Change color + float flColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + + float viewOffsX = flEffectPerc; + if (m_bStretch) + viewOffsX *= r_chromatic_aberration_offset.GetFloat() * 2; + else + viewOffsX *= r_chromatic_aberration_offset.GetFloat(); + + float viewOffsY = viewOffsX; + + { + char szColor[16] = { 0 }; + float flNoise = sin( gpGlobals->curtime * r_chromatic_aberration_noise.GetFloat() ) * flEffectPerc; + + switch (nColorMode) + { + // Red + case 0: + Q_strncpy( szColor, r_chromatic_aberration_frame1_clr.GetString(), sizeof( szColor ) ); + + viewOffsX *= r_chromatic_aberration_frame1_offset_x.GetFloat(); + viewOffsY *= r_chromatic_aberration_frame1_offset_y.GetFloat(); + + viewOffsX += flNoise; + viewOffsY += flNoise; + break; + + // Green + case 1: + Q_strncpy( szColor, r_chromatic_aberration_frame2_clr.GetString(), sizeof( szColor ) ); + + viewOffsX *= r_chromatic_aberration_frame2_offset_x.GetFloat(); + viewOffsY *= r_chromatic_aberration_frame2_offset_y.GetFloat(); + + viewOffsX += flNoise; + viewOffsY += flNoise; + break; + + // Blue + case 2: + Q_strncpy( szColor, r_chromatic_aberration_frame3_clr.GetString(), sizeof( szColor ) ); + + viewOffsX *= r_chromatic_aberration_frame3_offset_x.GetFloat(); + viewOffsY *= r_chromatic_aberration_frame3_offset_y.GetFloat(); + + viewOffsX += flNoise; + viewOffsY += flNoise; + break; + } + + char *c = strtok( szColor, " " ); + for (int i = 0; i < 4 && c != NULL; i++, c = strtok( NULL, " " )) + { + flColor[i] = atof( c ); + } + } + + if (flColor[3] == 0.0f || g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80) + return; + + m_EffectMaterial->ColorModulate( flColor[0], flColor[1], flColor[2] ); + + // Set alpha blend value + float flOverlayAlpha = clamp( r_chromatic_aberration_intensity.GetFloat() * flEffectPerc * flColor[3], 0.0f, 1.0f); + m_EffectMaterial->AlphaModulate( flOverlayAlpha ); + + // Draw full screen alpha-blended quad + if (m_bStretch) + { + float vX = x - (viewOffsX * 0.5f); + float vY = y - (viewOffsY * 0.5f); + + pRenderContext->DrawScreenSpaceRectangle( m_EffectMaterial, vX, vY, w + viewOffsX, h + viewOffsY, + 0, 0, (m_StunTexture->GetActualWidth()-1), (m_StunTexture->GetActualHeight()-1), + m_StunTexture->GetActualWidth(), m_StunTexture->GetActualHeight() ); + } + else + { + float vX = x + viewOffsX; + float vY = y + viewOffsY; + + pRenderContext->DrawScreenSpaceRectangle( m_EffectMaterial, 0, 0, w, h, + vX, vY, (m_StunTexture->GetActualWidth()-1)+vX, (m_StunTexture->GetActualHeight()-1)+vY, + m_StunTexture->GetActualWidth(), m_StunTexture->GetActualHeight() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Render the effect +//----------------------------------------------------------------------------- +void CChromaticAberrationEffect::Render( int x, int y, int w, int h ) +{ + // Make sure we're ready to play this effect + if ( !IsEnabled() ) + return; + + if ( m_bFadeOut && m_flFinishTime < gpGlobals->curtime ) + { + g_pScreenSpaceEffects->DisableScreenSpaceEffect( "mapbase_chromatic_aberration" ); + return; + } + + CMatRenderContextPtr pRenderContext( materials ); + + // Set ourselves to the proper rendermode + pRenderContext->MatrixMode( MATERIAL_VIEW ); + pRenderContext->PushMatrix(); + pRenderContext->LoadIdentity(); + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->PushMatrix(); + pRenderContext->LoadIdentity(); + + // Draw the texture if we're using it + if ( m_bUpdateView ) + { + // Save off this pass + Rect_t srcRect; + srcRect.x = x; + srcRect.y = y; + srcRect.width = w; + srcRect.height = h; + pRenderContext->CopyRenderTargetToTextureEx( m_StunTexture, 0, &srcRect, NULL ); + m_bUpdateView = false; + } + + float flEffectPerc = SmoothCurve( clamp( ( m_flFinishTime - gpGlobals->curtime ) / m_flDuration, 0.0f, 1.0f ) ); + if (!m_bFadeOut) + flEffectPerc = 1.0f - flEffectPerc; + + RenderColorFrame( pRenderContext, flEffectPerc, 0, x, y, w, h ); + RenderColorFrame( pRenderContext, flEffectPerc, 1, x, y, w, h ); + RenderColorFrame( pRenderContext, flEffectPerc, 2, x, y, w, h ); + + // Save off this pass + Rect_t srcRect; + srcRect.x = x; + srcRect.y = y; + srcRect.width = w; + srcRect.height = h; + pRenderContext->CopyRenderTargetToTextureEx( m_StunTexture, 0, &srcRect, NULL ); + + // Restore our state + pRenderContext->MatrixMode( MATERIAL_VIEW ); + pRenderContext->PopMatrix(); + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->PopMatrix(); +} +#endif diff --git a/sp/src/game/client/episodic/episodic_screenspaceeffects.h b/sp/src/game/client/episodic/episodic_screenspaceeffects.h index ff5bc71610e..661aa403d99 100644 --- a/sp/src/game/client/episodic/episodic_screenspaceeffects.h +++ b/sp/src/game/client/episodic/episodic_screenspaceeffects.h @@ -116,4 +116,38 @@ class CEP2StunEffect : public IScreenSpaceEffect ADD_SCREENSPACE_EFFECT( CEP2StunEffect, ep2_groggy ); +#ifdef MAPBASE +class CChromaticAberrationEffect : public IScreenSpaceEffect +{ +public: + CChromaticAberrationEffect( void ) : + m_flDuration( 0.0f ), + m_flFinishTime( 0.0f ), + m_bUpdateView( true ), + m_bEnabled( false ), + m_bFadeOut( false ) {} + + virtual void Init( void ); + virtual void Shutdown( void ); + virtual void SetParameters( KeyValues *params ); + virtual void Enable( bool bEnable ) { m_bEnabled = bEnable; }; + virtual bool IsEnabled( ) { return m_bEnabled; } + + virtual void RenderColorFrame( CMatRenderContextPtr &pRenderContext, float flEffectPerc, int nColorMode, int x, int y, int w, int h ); + virtual void Render( int x, int y, int w, int h ); + +private: + CTextureReference m_StunTexture; + CMaterialReference m_EffectMaterial; + float m_flDuration; + float m_flFinishTime; + bool m_bUpdateView; + bool m_bStretch; + bool m_bFadeOut; + bool m_bEnabled; +}; + +ADD_SCREENSPACE_EFFECT( CChromaticAberrationEffect, mapbase_chromatic_aberration ); +#endif + #endif // EPISODIC_SCREENSPACEEFFECTS_H diff --git a/sp/src/game/client/ez2/c_weapon_oicw.cpp b/sp/src/game/client/ez2/c_weapon_oicw.cpp new file mode 100644 index 00000000000..439dfc1fe05 --- /dev/null +++ b/sp/src/game/client/ez2/c_weapon_oicw.cpp @@ -0,0 +1,255 @@ +//=============================================================================// +// +// Purpose: OICW scope effects. +// +// Author: Blixibon +// +//=============================================================================// + +#include "cbase.h" +#include "c_basehlcombatweapon.h" +#include "tier0/icommandline.h" +#include "proxyentity.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/imaterialvar.h" +#include "baseviewmodel_shared.h" +#include "hud_crosshair.h" + +class C_WeaponOICW : public C_BaseHLCombatWeapon +{ +public: + DECLARE_CLASS( C_WeaponOICW, C_BaseHLCombatWeapon ); + DECLARE_CLIENTCLASS(); + DECLARE_PREDICTABLE(); + + C_WeaponOICW(); + ~C_WeaponOICW(); + + bool IsDynamicScopeZoomed( void ) const { return m_bZoomed; } + + virtual void AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ); + virtual float CalcViewmodelBob( void ); + void DrawCrosshair( void ); + + bool m_bZoomed; + bool m_bZoomTransition; +}; + +LINK_ENTITY_TO_CLASS( weapon_oicw, C_WeaponOICW ); + +IMPLEMENT_CLIENTCLASS_DT( C_WeaponOICW, DT_WeaponOICW, CWeaponOICW ) + RecvPropBool( RECVINFO( m_bZoomed ) ), + RecvPropBool( RECVINFO( m_bZoomTransition ) ), +END_RECV_TABLE() + +BEGIN_PREDICTION_DATA( C_WeaponOICW ) +END_PREDICTION_DATA() + +C_WeaponOICW::C_WeaponOICW() +{ +} + +C_WeaponOICW::~C_WeaponOICW() +{ +} + +void C_WeaponOICW::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) +{ + //if (m_bZoomed) + // return; + + BaseClass::AddViewmodelBob( viewmodel, origin, angles ); +} + +extern float g_lateralBob; +extern float g_verticalBob; + +float C_WeaponOICW::CalcViewmodelBob( void ) +{ + BaseClass::CalcViewmodelBob(); + + // Reduce bob while zoomed + if (m_bZoomed) + { + g_lateralBob *= 0.05f; + g_verticalBob *= 0.1f; + } + + // Return value not used (see base class) + return 0.0f; +} + +void C_WeaponOICW::DrawCrosshair( void ) +{ + // Don't draw crosshair when zoomed + if (m_bZoomed) + { + CHudCrosshair *crosshair = GET_HUDELEMENT( CHudCrosshair ); + if ( !crosshair ) + return; + + crosshair->ResetCrosshair(); + return; + } + + BaseClass::DrawCrosshair(); +} + +//----------------------------------------------------------------------------- +// Apply effects when the pulse pistol is charging +//----------------------------------------------------------------------------- +class COICWScopeMaterialProxy : public CEntityMaterialProxy +{ +public: + COICWScopeMaterialProxy() { m_ZoomVar = NULL; } + virtual ~COICWScopeMaterialProxy() {} + virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); + virtual void OnBind( C_BaseEntity *pEntity ); + virtual IMaterial *GetMaterial(); + +private: + IMaterialVar *m_ZoomVar; +}; + +bool COICWScopeMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) +{ + bool foundAnyVar = false; + bool foundVar; + + const char *pszChargeVar = pKeyValues->GetString( "outputZoom" ); + m_ZoomVar = pMaterial->FindVar( pszChargeVar, &foundVar, true ); + if (foundVar) + foundAnyVar = true; + + return foundAnyVar; +} + +void COICWScopeMaterialProxy::OnBind( C_BaseEntity *pEnt ) +{ + if (!m_ZoomVar) + return; + + // Need both the weapon and the viewmodel + C_BaseViewModel *pVM = NULL; + C_WeaponOICW *pOICW = NULL; + if ( pEnt->IsBaseCombatWeapon() ) + { + pOICW = assert_cast( pEnt ); + if (C_BasePlayer *pPlayer = ToBasePlayer( pOICW->GetOwner() )) + pVM = pPlayer->GetViewModel( pOICW->m_nViewModelIndex ); + } + else + { + pVM = dynamic_cast( pEnt ); + if (pVM) + pOICW = assert_cast( pVM->GetOwningWeapon() ); + } + + if (!pOICW || !pVM) + return; + + if ( pOICW ) + { + if (pOICW->IsDynamicScopeZoomed()) + { + // Zoomed + if (pOICW->m_bZoomTransition) + { + // Transition to zoom using viewmodel cycle + m_ZoomVar->SetFloatValue( pVM->GetCycle() ); + } + else + m_ZoomVar->SetFloatValue( 1.0f ); + } + else + { + // Not zoomed + if (pOICW->m_bZoomTransition) + { + // Transition from zoom using viewmodel cycle + m_ZoomVar->SetFloatValue( 1.0f - pVM->GetCycle() ); + } + else + m_ZoomVar->SetFloatValue( 0.0f ); + } + } + else + { + m_ZoomVar->SetFloatValue( 0.0f ); + } +} + +IMaterial *COICWScopeMaterialProxy::GetMaterial() +{ + if ( !m_ZoomVar ) + return NULL; + + return m_ZoomVar->GetOwningMaterial(); +} + +EXPOSE_INTERFACE( COICWScopeMaterialProxy, IMaterialProxy, "OICWScope" IMATERIAL_PROXY_INTERFACE_VERSION ); + +//----------------------------------------------------------------------------- +// Purpose: System for managing the OICW scope render target +// +// TODO: More formal system for managing weapon scope RTs? I don't know how this +// is "supposed" to work relative to games which actually use these +// Maybe something like what's described in baseclientrendertargets.cpp's header? +//----------------------------------------------------------------------------- +class CWeaponOICWScopeSystem : public CAutoGameSystem +{ +public: + CWeaponOICWScopeSystem() : CAutoGameSystem( "CWeaponOICWScopeSystem" ) + { + } + + virtual bool Init() + { + InitializeRTs(); + return true; + } + + //----------------------------------------------------------------------------- + // Initialize custom RT textures if necessary + //----------------------------------------------------------------------------- + void InitializeRTs() + { + if (!m_bInitializedRTs) + { + // Cancel if we shouldn't run with the scope RT + int nNoScopeRT = CommandLine()->ParmValue( "-no_scope_rt", 0 ); + if (nNoScopeRT == 1) + return; + + materials->BeginRenderTargetAllocation(); + + m_ScopeTexture.Init( g_pMaterialSystem->CreateNamedRenderTargetTextureEx2( + "_rt_Scope", + 512, 512, RT_SIZE_OFFSCREEN, + g_pMaterialSystem->GetBackBufferFormat(), + MATERIAL_RT_DEPTH_SHARED, + TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT, + CREATERENDERTARGETFLAGS_HDR ) ); + + materials->EndRenderTargetAllocation(); + + m_bInitializedRTs = true; + } + } + + void Shutdown() + { + if (m_bInitializedRTs) + { + m_ScopeTexture.Shutdown(); + m_bInitializedRTs = false; + } + } + +private: + + CTextureReference m_ScopeTexture; + bool m_bInitializedRTs = false; +}; + +CWeaponOICWScopeSystem m_OICWScopeSystem; diff --git a/sp/src/game/client/hl2/hud_credits.cpp b/sp/src/game/client/hl2/hud_credits.cpp index fb4354f5495..bf0e58fd6ba 100644 --- a/sp/src/game/client/hl2/hud_credits.cpp +++ b/sp/src/game/client/hl2/hud_credits.cpp @@ -35,8 +35,7 @@ struct creditname_t #ifdef MAPBASE // New credits stuff - - Color cColorOverride; + CCopyableUtlVector cColorOverride; // Images int iImageID = -1; @@ -408,8 +407,8 @@ void CHudCredits::DrawOutroCreditsName( void ) Color cColor = m_TextColor; #ifdef MAPBASE - if (pCredit->cColorOverride.a() > 0) - cColor = pCredit->cColorOverride; + if (pCredit->cColorOverride.Count() > 0) + cColor.SetRawColor( pCredit->cColorOverride[0] ); // Some lines should stick around and fade out if ( i >= m_CreditsList.Count()-m_iEndLines ) @@ -473,6 +472,12 @@ void CHudCredits::DrawOutroCreditsName( void ) { FOR_EACH_VEC( outStrings, i ) { + if (i < pCredit->cColorOverride.Count()) + { + // Change color to this particular column's color + cColor.SetRawColor( pCredit->cColorOverride[i] ); + } + int iImageID = GetOrAllocateImageID( outStrings[i] ); // Center the image if needed @@ -492,6 +497,12 @@ void CHudCredits::DrawOutroCreditsName( void ) { FOR_EACH_VEC( outStrings, i ) { + if (i < pCredit->cColorOverride.Count()) + { + // Change color to this particular column's color + cColor.SetRawColor( pCredit->cColorOverride[i] ); + } + DrawOutroCreditFont( outStrings[i], pCredit->flYPos, m_hTFont, cColor, iWidth*(i + 1), iDivisor ); } } @@ -768,8 +779,8 @@ void CHudCredits::DrawIntroCreditsName( void ) surface()->DrawSetTextFont( m_hTFont ); #ifdef MAPBASE Color cColor = m_cColor; - if (pCredit->cColorOverride.a() > 0) - cColor = pCredit->cColorOverride; + if (pCredit->cColorOverride.Count() > 0) + cColor.SetRawColor( pCredit->cColorOverride[0] ); surface()->DrawSetTextColor( cColor[0], cColor[1], cColor[2], FadeBlend( m_flFadeInTime, m_flFadeOutTime, m_flFadeHoldTime + pCredit->flTimeAdd, localTime ) * cColor[3] ); #else @@ -941,9 +952,25 @@ void CHudCredits::PrepareOutroCredits( void ) // Get color case 2: - int tmp[4]; - UTIL_StringToIntArray( tmp, 4, outStrings[i] ); - pCredit->cColorOverride = Color( tmp[0], tmp[1], tmp[2], tmp[3] ); + char *pToken = strtok( outStrings[i], "," ); + if (pToken) + { + // Multiple colors for multiple columns + while (pToken != NULL) + { + int tmp[4]; + UTIL_StringToIntArray( tmp, 4, pToken ); + pCredit->cColorOverride.AddToTail( Color( tmp[0], tmp[1], tmp[2], tmp[3] ).GetRawColor() ); + + pToken = strtok( NULL, "," ); + } + } + else + { + int tmp[4]; + UTIL_StringToIntArray( tmp, 4, outStrings[i] ); + pCredit->cColorOverride.AddToTail( Color( tmp[0], tmp[1], tmp[2], tmp[3] ).GetRawColor() ); + } break; } } @@ -999,9 +1026,25 @@ void CHudCredits::PrepareOutroCredits( void ) { // Get color case 1: - int tmp[4]; - UTIL_StringToIntArray( tmp, 4, outStrings[i] ); - pCredit->cColorOverride = Color( tmp[0], tmp[1], tmp[2], tmp[3] ); + char *pToken = strtok( outStrings[i], "," ); + if (pToken) + { + // Multiple colors for multiple columns + while (pToken != NULL) + { + int tmp[4]; + UTIL_StringToIntArray( tmp, 4, pToken ); + pCredit->cColorOverride.AddToTail( Color( tmp[0], tmp[1], tmp[2], tmp[3] ).GetRawColor() ); + + pToken = strtok( NULL, "," ); + } + } + else + { + int tmp[4]; + UTIL_StringToIntArray( tmp, 4, outStrings[i] ); + pCredit->cColorOverride.AddToTail( Color( tmp[0], tmp[1], tmp[2], tmp[3] ).GetRawColor() ); + } break; } } @@ -1024,8 +1067,12 @@ void CHudCredits::PrepareOutroCredits( void ) #ifdef MAPBASE // Check if the last line has a color override. If it does, use that as the alpha for the fadeout - if (m_CreditsList.Tail().cColorOverride.a() != 0) - m_Alpha = m_CreditsList.Tail().cColorOverride.a(); + if (m_CreditsList.Tail().cColorOverride.Count() > 0) + { + Color clr; + clr.SetRawColor( m_CreditsList.Tail().cColorOverride[0] ); + m_Alpha = clr.a(); + } #endif SetActive( true ); @@ -1057,9 +1104,10 @@ void CHudCredits::PrepareIntroCredits( void ) { // Get color case 1: + // TODO: Columns? int tmp[4]; UTIL_StringToIntArray( tmp, 4, outStrings[i] ); - pCredit->cColorOverride = Color( tmp[0], tmp[1], tmp[2], tmp[3] ); + pCredit->cColorOverride.AddToTail( Color( tmp[0], tmp[1], tmp[2], tmp[3] ).GetRawColor() ); break; } } diff --git a/sp/src/game/client/mapbase/mapbase_autocubemap.cpp b/sp/src/game/client/mapbase/mapbase_autocubemap.cpp index 03602ef2b4c..79f2497d96a 100644 --- a/sp/src/game/client/mapbase/mapbase_autocubemap.cpp +++ b/sp/src/game/client/mapbase/mapbase_autocubemap.cpp @@ -125,6 +125,12 @@ class CAutoCubemapSystem : public CAutoGameSystem //Msg("No maps to cubemap with!\n"); //return; + if (C_BasePlayer::GetLocalPlayer() == NULL) + { + Msg( "Must be in a level (or have a loaded map list) to begin autocubemap\n" ); + return; + } + // Just do this map m_AutoCubemapMaps.AddToTail( strdup( g_MapName ) ); } diff --git a/sp/src/game/client/particlemgr.cpp b/sp/src/game/client/particlemgr.cpp index d62c6e73874..b37a2000389 100644 --- a/sp/src/game/client/particlemgr.cpp +++ b/sp/src/game/client/particlemgr.cpp @@ -146,6 +146,8 @@ CParticleEffectBinding::CParticleEffectBinding() m_LastMin = m_Min; m_LastMax = m_Max; + m_flParticleCullRadius = 0.0f; + SetParticleCullRadius( 0.0f ); m_nActiveParticles = 0; diff --git a/sp/src/game/client/rendertexture.cpp b/sp/src/game/client/rendertexture.cpp index 78780e93022..758f505f5c5 100644 --- a/sp/src/game/client/rendertexture.cpp +++ b/sp/src/game/client/rendertexture.cpp @@ -80,6 +80,23 @@ ITexture *GetCameraTexture( void ) return s_pCameraTexture; } +#ifdef EZ2 +//============================================================================= +// Dynamic Scope Texture +//============================================================================= +static CTextureReference s_pScopeTexture; +ITexture *GetScopeTexture( void ) +{ + if ( !s_pScopeTexture ) + { + s_pScopeTexture.Init( materials->FindTexture( "_rt_Scope", TEXTURE_GROUP_RENDER_TARGET ) ); + Assert( !IsErrorTexture( s_pScopeTexture ) ); + AddReleaseFunc(); + } + return s_pScopeTexture; +} +#endif + //============================================================================= // Full Frame Depth Texture //============================================================================= @@ -247,6 +264,10 @@ void ReleaseRenderTargets( void ) { s_pPowerOfTwoFrameBufferTexture.Shutdown(); s_pCameraTexture.Shutdown(); +#ifdef EZ2 + //Release the scope render target too + s_pScopeTexture.Shutdown(); +#endif s_pWaterReflectionTexture.Shutdown(); s_pWaterRefractionTexture.Shutdown(); s_pQuarterSizedFB0.Shutdown(); diff --git a/sp/src/game/client/rendertexture.h b/sp/src/game/client/rendertexture.h index 2afdc6aeb86..39f57a175d9 100644 --- a/sp/src/game/client/rendertexture.h +++ b/sp/src/game/client/rendertexture.h @@ -15,6 +15,10 @@ ITexture *GetWaterReflectionTexture( void ); ITexture *GetWaterRefractionTexture( void ); ITexture *GetFullscreenTexture( void ); ITexture *GetCameraTexture( void ); +#ifdef EZ2 +//Dynamic Scope Texture +ITexture *GetScopeTexture(); +#endif ITexture *GetFullFrameDepthTexture( void ); // SmallBufferHDRx=r16g16b16a16 quarter-sized texture diff --git a/sp/src/game/client/viewrender.cpp b/sp/src/game/client/viewrender.cpp index 6bcff8da90e..8c26c53b794 100644 --- a/sp/src/game/client/viewrender.cpp +++ b/sp/src/game/client/viewrender.cpp @@ -2291,6 +2291,13 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT PerformScreenSpaceEffects( 0, 0, view.width, view.height ); +#ifdef EZ2 + if( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 70 ) + { + DrawScope( view ); + } +#endif + if ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER ) { pRenderContext.GetFrom( materials ); @@ -3751,6 +3758,72 @@ void CViewRender::DrawMonitors( const CViewSetup &cameraView ) #endif // USE_MONITORS } +#ifdef EZ2 +void CViewRender::DrawScope( const CViewSetup &viewSet ) +{ + C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + + if(!localPlayer) + return; + + if( !localPlayer->GetActiveWeapon() ) + return; + + if( !localPlayer->GetActiveWeapon()->GetViewModel() ) + return; + + if ( localPlayer->GetActiveWeapon()->GetDynamicScopeFOV() == 0.0f ) + return; + + //Copy our current View. + CViewSetup scopeView = viewSet; + + //Get our camera render target. + ITexture *pRenderTarget = GetScopeTexture(); + + if( pRenderTarget == NULL ) + return; + + //Our view information, Origin, View Direction, window size + // location on material, and visual ratios. + scopeView.width = pRenderTarget->GetActualWidth(); + scopeView.height = pRenderTarget->GetActualHeight(); + scopeView.x = 0; + scopeView.y = 0; + scopeView.fov = localPlayer->GetActiveWeapon()->GetDynamicScopeFOV(); + scopeView.m_bOrtho = false; + + scopeView.m_flAspectRatio = 1.0f; + + if (/*!localPlayer->GetActiveWeapon()->IsDynamicScopeZoomed() &&*/ localPlayer->GetViewModel()) + { + int nAttach = localPlayer->GetViewModel()->LookupAttachment( "scope_cam" ); + if (nAttach != -1) + { + // Set the view to the attachment + localPlayer->GetViewModel()->GetAttachment( nAttach, scopeView.origin, scopeView.angles ); + } + } + + //Set the view up and output the scene to our RenderTarget (Scope Material). + Frustum frustum; + render->Push3DView( scopeView, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, pRenderTarget, (VPlane *)frustum ); + + SkyboxVisibility_t nSkyboxVisible = SKYBOX_NOT_VISIBLE; + int ClearFlags = 0; + CSkyboxView *pSkyView = new CSkyboxView( this ); + if( pSkyView->Setup( scopeView, &ClearFlags, &nSkyboxVisible ) != false ) + AddViewToScene( pSkyView ); + SafeRelease( pSkyView ); + + ViewDrawScene( false, SKYBOX_3DSKYBOX_VISIBLE, scopeView, VIEW_CLEAR_DEPTH, VIEW_INTRO_CAMERA ); // Using VIEW_INTRO_CAMERA to indicate this is a perspective view + + //GetClientModeNormal()->DoPostScreenSpaceEffects( &scopeView ); + + render->PopView( m_Frustum ); +} +#endif + //----------------------------------------------------------------------------- // diff --git a/sp/src/game/client/viewrender.h b/sp/src/game/client/viewrender.h index 3ba6cd9fc43..eb2e63a752f 100644 --- a/sp/src/game/client/viewrender.h +++ b/sp/src/game/client/viewrender.h @@ -447,6 +447,9 @@ class CViewRender : public IViewRender, void ViewDrawScene( bool bDrew3dSkybox, SkyboxVisibility_t nSkyboxVisible, const CViewSetup &view, int nClearFlags, view_id_t viewID, bool bDrawViewModel = false, int baseDrawFlags = 0, ViewCustomVisibility_t *pCustomVisibility = NULL ); void DrawMonitors( const CViewSetup &cameraView ); +#ifdef EZ2 + void DrawScope( const CViewSetup &viewSet ); +#endif bool DrawOneMonitor( ITexture *pRenderTarget, int cameraNum, C_PointCamera *pCameraEnt, const CViewSetup &cameraView, C_BasePlayer *localPlayer, int x, int y, int width, int height ); diff --git a/sp/src/game/client/vscript_client.cpp b/sp/src/game/client/vscript_client.cpp index 7bece925a43..2312d6ca9a8 100644 --- a/sp/src/game/client/vscript_client.cpp +++ b/sp/src/game/client/vscript_client.cpp @@ -272,6 +272,8 @@ void CScriptMaterialProxy::Release( void ) m_hScriptInstance = NULL; } + g_ScriptPersistableList.FindAndRemove( this ); + delete this; } diff --git a/sp/src/game/client/weapons_resource.cpp b/sp/src/game/client/weapons_resource.cpp index 8f8d58e86de..7bba99670d3 100644 --- a/sp/src/game/client/weapons_resource.cpp +++ b/sp/src/game/client/weapons_resource.cpp @@ -176,6 +176,28 @@ void WeaponsResource::LoadWeaponSprites( WEAPON_FILE_INFO_HANDLE hWeaponFileInfo } } +#ifdef EZ2 + p = FindHudTextureInDict( tempList, "weapon_dual" ); + if ( p ) + { + pWeaponInfo->iconInactiveDual = gHUD.AddUnsearchableHudIconToList( *p ); + if ( pWeaponInfo->iconInactiveDual) + { + pWeaponInfo->iconInactiveDual->Precache(); + } + } + + p = FindHudTextureInDict( tempList, "weapon_dual_s" ); + if ( p ) + { + pWeaponInfo->iconActiveDual = gHUD.AddUnsearchableHudIconToList( *p ); + if ( pWeaponInfo->iconActiveDual) + { + pWeaponInfo->iconActiveDual->Precache(); + } + } +#endif + p = FindHudTextureInDict( tempList, "weapon_small" ); if ( p ) { diff --git a/sp/src/game/server/BasePropDoor.h b/sp/src/game/server/BasePropDoor.h index b8d3b90aa52..8310a7ec21e 100644 --- a/sp/src/game/server/BasePropDoor.h +++ b/sp/src/game/server/BasePropDoor.h @@ -38,6 +38,9 @@ abstract_class CBasePropDoor : public CDynamicProp DECLARE_CLASS( CBasePropDoor, CDynamicProp ); DECLARE_SERVERCLASS(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif CBasePropDoor( void ); @@ -79,6 +82,28 @@ abstract_class CBasePropDoor : public CDynamicProp virtual bool PassesDoorFilter(CBaseEntity *pEntity) { return true; } virtual bool KeyValue( const char *szKeyName, const char *szValue ); + + float GetSpeed() const { return m_flSpeed; } +#endif + +#ifdef MAPBASE_VSCRIPT + bool ScriptIsDoorOpen() { return IsDoorOpen(); } + bool ScriptIsDoorAjar() { return IsDoorAjar(); } + bool ScriptIsDoorOpening() { return IsDoorOpening(); } + bool ScriptIsDoorClosed() { return IsDoorClosed(); } + bool ScriptIsDoorClosing() { return IsDoorClosing(); } + bool ScriptIsDoorLocked() { return IsDoorLocked(); } + bool ScriptIsDoorBlocked() const { return IsDoorBlocked(); } + HSCRIPT ScriptGetActivator() { return ToHScript( m_hActivator.Get() ); } + + HSCRIPT ScriptGetDoorList( int i ) { return m_hDoorList.IsValidIndex(i) ? ToHScript( m_hDoorList[i] ) : NULL; } + int GetDoorListCount() { return m_hDoorList.Count(); } + + const char *ScriptGetFullyOpenSound() { return STRING( m_SoundOpen ); } + const char *ScriptGetFullyClosedSound() { return STRING( m_SoundClose ); } + const char *ScriptGetMovingSound() { return STRING( m_SoundMoving ); } + const char *ScriptGetLockedSound() { return STRING( m_ls.sLockedSound ); } + const char *ScriptGetUnlockedSound() { return STRING( m_ls.sUnlockedSound ); } #endif #ifdef EZ2 @@ -193,6 +218,12 @@ abstract_class CBasePropDoor : public CDynamicProp #ifdef MAPBASE void InputAllowPlayerUse(inputdata_t &inputdata); void InputDisallowPlayerUse(inputdata_t &inputdata); + + void InputSetFullyOpenSound(inputdata_t &inputdata); + void InputSetFullyClosedSound(inputdata_t &inputdata); + void InputSetMovingSound(inputdata_t &inputdata); + void InputSetLockedSound(inputdata_t &inputdata); + void InputSetUnlockedSound(inputdata_t &inputdata); #endif void SetDoorBlocker( CBaseEntity *pBlocker ); diff --git a/sp/src/game/server/Human_Error/vehicle_drivable_apc.cpp b/sp/src/game/server/Human_Error/vehicle_drivable_apc.cpp index fba67d42db3..68d248bc252 100644 --- a/sp/src/game/server/Human_Error/vehicle_drivable_apc.cpp +++ b/sp/src/game/server/Human_Error/vehicle_drivable_apc.cpp @@ -842,11 +842,15 @@ void CPropDrivableAPC::AimGunAt( Vector *endPos, float flInterval ) //DevMsg("newTargetPitch: %f\n", newTargetPitch); + // EZ2: Invalid check, this always resolves to true + // m_bUnableToFire is unused, ignore block +#if 0 // If the angles have been clamped, we're looking outside of our valid range if ( fabs(newTargetYaw-targetYaw) > 1e-4 || fabs(newTargetPitch-targetPitch) > 1e-4 ) { m_bUnableToFire = true; } +#endif targetYaw = newTargetYaw; targetPitch = newTargetPitch; diff --git a/sp/src/game/server/SkyCamera.cpp b/sp/src/game/server/SkyCamera.cpp index be9716d4781..f5bb282522e 100644 --- a/sp/src/game/server/SkyCamera.cpp +++ b/sp/src/game/server/SkyCamera.cpp @@ -211,6 +211,11 @@ void CSkyCamera::Activate( ) } } #endif + +#ifdef MAPBASE + if (HasSpawnFlags( SF_SKY_MASTER )) + g_hActiveSkybox = this; +#endif } #ifdef MAPBASE @@ -368,9 +373,11 @@ void CSkyCamera::InputActivateSkybox( inputdata_t &inputdata ) // Deactivate that skybox pActiveSky->SetThink( NULL ); pActiveSky->SetNextThink( TICK_NEVER_THINK ); + pActiveSky->RemoveSpawnFlags( SF_SKY_MASTER ); } g_hActiveSkybox = this; + AddSpawnFlags( SF_SKY_MASTER ); if (HasSpawnFlags( SF_SKY_START_UPDATING )) InputStartUpdating( inputdata ); @@ -384,6 +391,7 @@ void CSkyCamera::InputDeactivateSkybox( inputdata_t &inputdata ) if (GetCurrentSkyCamera() == this) { g_hActiveSkybox = NULL; + RemoveSpawnFlags( SF_SKY_MASTER ); // ClientData doesn't catch this immediately CBasePlayer *pPlayer = NULL; diff --git a/sp/src/game/server/ai_baseactor.cpp b/sp/src/game/server/ai_baseactor.cpp index 0238df79ee5..fcfd236853d 100644 --- a/sp/src/game/server/ai_baseactor.cpp +++ b/sp/src/game/server/ai_baseactor.cpp @@ -1145,6 +1145,10 @@ void CAI_BaseActor::UpdateHeadControl( const Vector &vHeadTarget, float flHeadIn ConcatTransforms( worldToForward, targetXform, headXform ); MatrixAngles( headXform, vTargetAngles ); +#ifdef EZ2 + HeadAngleOverride( vTargetAngles ); +#endif + #ifdef MAPBASE // This is here to cover an edge case where pose parameters set to NaN invalidate the model. if (!vTargetAngles.IsValid()) @@ -1273,6 +1277,16 @@ bool CAI_BaseActor::HasActiveLookTargets( void ) return m_lookQueue.Count() != 0; } +#ifdef EZ2 +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CAI_BaseActor::HasActiveRandomLookTargets( void ) +{ + return m_randomLookQueue.Count() != 0; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Clear any active look targets for the specified entity //----------------------------------------------------------------------------- @@ -1755,6 +1769,12 @@ void CAI_BaseActor::MaintainLookTargets( float flInterval ) dir = HeadDirection3D(); } } +#ifdef EZ2 + else if ( HeadTargetPosOverride( active[i]->GetPosition(), dir, flDist ) ) + { + flInterest = flInterest * HeadTargetValidity( active[i]->GetPosition() ); + } +#endif else { dir = active[i]->GetPosition() - vEyePosition; diff --git a/sp/src/game/server/ai_baseactor.h b/sp/src/game/server/ai_baseactor.h index e293cc8b20b..cf76c78c49a 100644 --- a/sp/src/game/server/ai_baseactor.h +++ b/sp/src/game/server/ai_baseactor.h @@ -129,6 +129,9 @@ class CAI_BaseActor : public CAI_ExpresserHost virtual bool PickRandomLookTarget( AILookTargetArgs_t *pArgs ); virtual void MakeRandomLookTarget( AILookTargetArgs_t *pArgs, float minTime, float maxTime ); virtual bool HasActiveLookTargets( void ); +#ifdef EZ2 + virtual bool HasActiveRandomLookTargets( void ); +#endif virtual void OnSelectedLookTarget( AILookTargetArgs_t *pArgs ) { return; } virtual void ClearLookTarget( CBaseEntity *pTarget ); virtual void ExpireCurrentRandomLookTarget() { m_flNextRandomLookTime = gpGlobals->curtime - 0.1f; } @@ -153,6 +156,11 @@ class CAI_BaseActor : public CAI_ExpresserHost virtual bool ValidEyeTarget(const Vector &lookTargetPos); virtual bool ValidHeadTarget(const Vector &lookTargetPos); virtual float HeadTargetValidity(const Vector &lookTargetPos); +#ifdef EZ2 + // Used by Wilson for looking from cameras + virtual bool HeadTargetPosOverride( const Vector &vecTargetPos, Vector &vecDir, float &flDist ) { return false; } + virtual bool HeadAngleOverride( QAngle &vTargetAngles ) { return false; } +#endif virtual bool ShouldBruteForceFailedNav() { return true; } diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index 0ef28cbc355..cfcd983ad6d 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -6340,7 +6340,11 @@ bool CAI_BaseNPC::UpdateEnemyMemory( CBaseEntity *pEnemy, const Vector &position // If the was eluding me and allow the NPC to play a sound if (GetEnemies()->HasEludedMe(pEnemy)) { +#ifdef MAPBASE + FoundEnemySound( pEnemy ); +#else FoundEnemySound(); +#endif } float reactionDelay = ( !pInformer || pInformer == this ) ? GetReactionDelay( pEnemy ) : 0.0; bool result = GetEnemies()->UpdateMemory(GetNavigator()->GetNetwork(), pEnemy, position, reactionDelay, firstHand); @@ -8275,10 +8279,13 @@ int CAI_BaseNPC::UnholsterWeapon( void ) if (i == -1) { // Set i to the first weapon you can find - for (i = 0; i < WeaponCount(); i++) + for (i = 0;;) { if (GetWeapon(i)) break; + + if (++i >= WeaponCount()) + return -1; } } #else @@ -11976,7 +11983,11 @@ bool CAI_BaseNPC::ChooseEnemy( void ) if ( fEnemyEluded ) { SetCondition( COND_LOST_ENEMY ); +#ifdef MAPBASE + LostEnemySound( pInitialEnemy ); +#else LostEnemySound(); +#endif } if ( fEnemyWasPlayer ) @@ -15260,22 +15271,39 @@ void CAI_BaseNPC::ParseScriptedNPCInteractions(void) else if (!Q_strncmp(szName, "their_", 6)) { const char *szTheirName = szName + 6; - sInteraction.bHasSeparateSequenceNames = true; if (!Q_strncmp(szTheirName, "entry_sequence", 14)) + { + sInteraction.bHasSeparateSequenceNames = true; sInteraction.sTheirPhases[SNPCINT_ENTRY].iszSequence = AllocPooledString(szValue); + } else if (!Q_strncmp(szTheirName, "entry_activity", 14)) + { + sInteraction.bHasSeparateSequenceNames = true; sInteraction.sTheirPhases[SNPCINT_ENTRY].iActivity = GetOrRegisterActivity(szValue); + } else if (!Q_strncmp(szTheirName, "sequence", 8)) + { + sInteraction.bHasSeparateSequenceNames = true; sInteraction.sTheirPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString(szValue); + } else if (!Q_strncmp(szTheirName, "activity", 8)) + { + sInteraction.bHasSeparateSequenceNames = true; sInteraction.sTheirPhases[SNPCINT_SEQUENCE].iActivity = GetOrRegisterActivity(szValue); + } else if (!Q_strncmp(szTheirName, "exit_sequence", 13)) + { + sInteraction.bHasSeparateSequenceNames = true; sInteraction.sTheirPhases[SNPCINT_EXIT].iszSequence = AllocPooledString(szValue); + } else if (!Q_strncmp(szTheirName, "exit_activity", 13)) + { + sInteraction.bHasSeparateSequenceNames = true; sInteraction.sTheirPhases[SNPCINT_EXIT].iActivity = GetOrRegisterActivity(szValue); + } #ifdef EZ else if (!Q_strncmp(szTheirName, "healthfrac_commandable", 22)) { @@ -16266,6 +16294,9 @@ bool CAI_BaseNPC::InteractionIsAllowed( CAI_BaseNPC *pOtherNPC, ScriptedNPCInter if (pOtherNPC->Classify() == CLASS_PLAYER_ALLY_VITAL) return false; + if (m_iDynamicInteractionsAllowed == TRS_FALSE) + return false; + #ifdef EZ // 1upD / Blixibon - Check commandable if (pInteraction->iFlags & SCNPC_FLAG_TEST_SQUADMATE_HEALTH && pOtherNPC->IsCommandable()) @@ -16277,17 +16308,9 @@ bool CAI_BaseNPC::InteractionIsAllowed( CAI_BaseNPC *pOtherNPC, ScriptedNPCInter } #endif -#ifdef EZ - // Entropy : Zero always allows Mapbase interactions - if (m_iDynamicInteractionsAllowed == TRS_FALSE) - return false; -#else - // This convar allows all NPCs to perform Mapbase interactions for both testing and player fun. - if (ai_dynint_always_enabled.GetBool() && m_iDynamicInteractionsAllowed != TRS_FALSE) - return true; - - // m_iDynamicInteractionsAllowed == TRS_FALSE case is already handled in CanRunAScriptedNPCInteraction(). - if (pInteraction->iFlags & SCNPC_FLAG_MAPBASE_ADDITION && m_iDynamicInteractionsAllowed == TRS_NONE) +#ifndef EZ // Entropy : Zero always allows Mapbase interactions + // To maintain existing behavior, Mapbase additions require either explicit TRS_YES or ai_dynint_always_enabled. + if (pInteraction->iFlags & SCNPC_FLAG_MAPBASE_ADDITION && m_iDynamicInteractionsAllowed == TRS_NONE && !ai_dynint_always_enabled.GetBool()) return false; #endif diff --git a/sp/src/game/server/ai_basenpc.h b/sp/src/game/server/ai_basenpc.h index a45a6a66221..f92a00d1e5e 100644 --- a/sp/src/game/server/ai_basenpc.h +++ b/sp/src/game/server/ai_basenpc.h @@ -1457,6 +1457,11 @@ class CAI_BaseNPC : public CBaseCombatCharacter, virtual void FearSound( void ) { return; }; virtual void LostEnemySound( void ) { return; }; virtual void FoundEnemySound( void ) { return; }; +#ifdef MAPBASE + // New versions of the above functions which pass the enemy in question as a parameter. Chains to the original by default + virtual void LostEnemySound( CBaseEntity *pEnemy ) { LostEnemySound(); }; + virtual void FoundEnemySound( CBaseEntity *pEnemy ) { FoundEnemySound(); }; +#endif virtual void BarnacleDeathSound( void ) { CTakeDamageInfo info; PainSound( info ); } virtual void SpeakSentence( int sentenceType ) { return; }; diff --git a/sp/src/game/server/ai_basenpc_schedule.cpp b/sp/src/game/server/ai_basenpc_schedule.cpp index b8be6cef510..a12bb902d03 100644 --- a/sp/src/game/server/ai_basenpc_schedule.cpp +++ b/sp/src/game/server/ai_basenpc_schedule.cpp @@ -3540,6 +3540,12 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) // as this should only run with the NPC "receiving" the interaction ScriptedNPCInteraction_t *pInteraction = m_hForcedInteractionPartner->GetRunningDynamicInteraction(); + if ( !(pInteraction->iFlags & SCNPC_FLAG_TEST_OTHER_ANGLES) ) + { + TaskComplete(); + return; + } + // Get our target's origin Vector vecTarget = m_hForcedInteractionPartner->GetAbsOrigin(); diff --git a/sp/src/game/server/ai_behavior_standoff.h b/sp/src/game/server/ai_behavior_standoff.h index a1a0ece77c5..271a905aeb6 100644 --- a/sp/src/game/server/ai_behavior_standoff.h +++ b/sp/src/game/server/ai_behavior_standoff.h @@ -163,6 +163,10 @@ class CAI_StandoffBehavior : public CAI_MappedActivityBehavior_Temporary // Don't do death poses while crouching bool ShouldPickADeathPose( void ) { return (GetPosture() != AIP_CROUCHING && GetPosture() != AIP_PEEKING) && BaseClass::ShouldPickADeathPose(); } #endif + +#ifdef EZ2 + const AI_StandoffParams_t &GetParams() { return m_params; } +#endif private: diff --git a/sp/src/game/server/ai_playerally.cpp b/sp/src/game/server/ai_playerally.cpp index 317a5ac8393..85c55fde163 100644 --- a/sp/src/game/server/ai_playerally.cpp +++ b/sp/src/game/server/ai_playerally.cpp @@ -14,10 +14,12 @@ #include "gameinterface.h" #ifdef MAPBASE #include "mapbase/matchers.h" +#include "ai_memory.h" #endif #ifdef EZ2 #include "ez2/ai_concept_response.h" #include "ez2/ez2_player.h" +#include "ai_interactions.h" #endif // memdbgon must be the last include file in a .cpp file!!! @@ -137,6 +139,8 @@ ConceptInfo_t g_ConceptInfos[] = { TLK_TAKING_FIRE, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, }, { TLK_NEW_ENEMY, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, }, { TLK_COMBAT_IDLE, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, }, + { TLK_LOSTENEMY, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, }, + { TLK_REFINDENEMY, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, }, #endif #ifdef EZ @@ -477,9 +481,17 @@ void CAI_PlayerAlly::GatherConditions( void ) { bool bPlayerIsLooking = false; +#ifdef EZ2 + if ( ( pLocalPlayer->GetAbsOrigin() - GetAbsOriginForSpeech( pLocalPlayer ) ).Length2DSqr() < Square(TALKER_STARE_DIST) ) +#else if ( ( pLocalPlayer->GetAbsOrigin() - GetAbsOrigin() ).Length2DSqr() < Square(TALKER_STARE_DIST) ) +#endif { +#ifdef EZ2 + if ( pLocalPlayer->FInViewCone( GetEyePositionForSpeech( pLocalPlayer ) ) ) +#else if ( pLocalPlayer->FInViewCone( EyePosition() ) ) +#endif { if ( pLocalPlayer->GetSmoothedVelocity().LengthSqr() < Square( 100 ) ) bPlayerIsLooking = true; @@ -780,7 +792,7 @@ bool CAI_PlayerAlly::SelectAlertSpeech( AISpeechSelection_t *pSelection ) // NPCs can comment on smells in EZ2 if ( pSmellTarget && HasCondition( COND_SMELL ) && GetBestScent() && GetExpresser() && GetExpresser()->CanSpeakConcept( TLK_SMELL ) ) { - if( SelectSpeechResponse( TLK_SMELL, UTIL_VarArgs("distancetosmell:%f", GetAbsOrigin().DistTo( GetBestScent()->GetSoundReactOrigin() ) ), pSmellTarget, pSelection ) ) + if( SelectSpeechResponse( TLK_SMELL, UTIL_VarArgs("distancetosmell:%f", GetAbsOriginForSpeech( pSmellTarget ).DistTo( GetBestScent()->GetSoundReactOrigin() ) ), pSmellTarget, pSelection ) ) return true; } #endif @@ -1342,6 +1354,37 @@ bool CAI_PlayerAlly::CanFlinch( void ) } #endif +#ifdef EZ2 +extern int g_interactionStasisGrenadeFreeze; +extern int g_interactionStasisGrenadeUnfreeze; + +//----------------------------------------------------------------------------- +// Purpose: This is a generic function (to be implemented by sub-classes) to +// handle specific interactions between different types of characters +// (For example the barnacle grabbing an NPC) +// Input : Constant for the type of interaction +// Output : true - if sub-class has a response for the interaction +// false - if sub-class has no response +//----------------------------------------------------------------------------- +bool CAI_PlayerAlly::HandleInteraction(int interactionType, void* data, CBaseCombatCharacter* sourceEnt) +{ + if (interactionType == g_interactionStasisGrenadeFreeze) + { + CapabilitiesRemove(bits_CAP_TURN_HEAD); + // Handle unfreeze normally + return false; + } + else if (interactionType == g_interactionStasisGrenadeUnfreeze) + { + CapabilitiesAdd(bits_CAP_TURN_HEAD); + // Handle unfreeze normally + return false; + } + + return BaseClass::HandleInteraction(interactionType, data, sourceEnt); +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CAI_PlayerAlly::OnKilledNPC( CBaseCombatCharacter *pKilled ) @@ -1373,7 +1416,11 @@ void CAI_PlayerAlly::OnEnemyRangeAttackedMe( CBaseEntity *pEnemy, const Vector & AI_CriteriaSet modifiers; ModifyOrAppendEnemyCriteria( modifiers, pEnemy ); +#ifdef EZ2 + Vector vecEntDir = (pEnemy->EyePosition() - GetEyePositionForSpeech(pEnemy)); +#else Vector vecEntDir = (pEnemy->EyePosition() - EyePosition()); +#endif float flDot = DotProduct( vecEntDir.Normalized(), vecDir ); modifiers.AppendCriteria( "shot_dot", CNumStr( flDot ) ); @@ -1522,6 +1569,28 @@ void CAI_PlayerAlly::PainSound( const CTakeDamageInfo &info ) SpeakIfAllowed( TLK_WOUND ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +void CAI_PlayerAlly::LostEnemySound( CBaseEntity *pEnemy ) +{ + AI_CriteriaSet modifiers; + ModifyOrAppendEnemyCriteria( modifiers, pEnemy ); + + modifiers.AppendCriteria( "lastseenenemy", gpGlobals->curtime - GetEnemies()->LastTimeSeen( pEnemy ) ); + + SpeakIfAllowed( TLK_LOSTENEMY, modifiers ); +} + +//----------------------------------------------------------------------------- +void CAI_PlayerAlly::FoundEnemySound( CBaseEntity *pEnemy ) +{ + AI_CriteriaSet modifiers; + ModifyOrAppendEnemyCriteria( modifiers, pEnemy ); + + SpeakIfAllowed( TLK_REFINDENEMY, modifiers ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Implemented to look at talk target //----------------------------------------------------------------------------- @@ -1619,7 +1688,11 @@ bool CAI_PlayerAlly::IsValidSpeechTarget( int flags, CBaseEntity *pEntity ) //----------------------------------------------------------------------------- CBaseEntity *CAI_PlayerAlly::FindSpeechTarget( int flags ) { +#ifdef EZ2 + const Vector & vAbsOrigin = GetSpeechTargetSearchOrigin(); +#else const Vector & vAbsOrigin = GetAbsOrigin(); +#endif float closestDistSq = FLT_MAX; CBaseEntity * pNearest = NULL; float distSq; diff --git a/sp/src/game/server/ai_playerally.h b/sp/src/game/server/ai_playerally.h index d1c4bd896d7..c3338f574c8 100644 --- a/sp/src/game/server/ai_playerally.h +++ b/sp/src/game/server/ai_playerally.h @@ -142,6 +142,8 @@ #define TLK_TAKING_FIRE "TLK_TAKING_FIRE" // Someone fired at me (regardless of whether I was hit) #define TLK_NEW_ENEMY "TLK_NEW_ENEMY" // A new enemy appeared while combat was already in progress #define TLK_COMBAT_IDLE "TLK_COMBAT_IDLE" // Similar to TLK_ATTACKING, but specifically for when *not* currently attacking (e.g. when in cover or reloading) +#define TLK_LOSTENEMY "TLK_LOSTENEMY" // Current enemy has eluded squad +#define TLK_REFINDENEMY "TLK_REFINDENEMY" // Found a previously eluded enemy #endif #ifdef EZ @@ -349,6 +351,10 @@ class CAI_PlayerAlly : public CAI_BaseActor virtual bool CanFlinch( void ); #endif +#ifdef EZ2 + bool HandleInteraction(int interactionType, void* data, CBaseCombatCharacter* sourceEnt); +#endif + //--------------------------------- // Combat //--------------------------------- @@ -371,6 +377,11 @@ class CAI_PlayerAlly : public CAI_BaseActor virtual void PainSound( const CTakeDamageInfo &info ); +#ifdef MAPBASE + virtual void LostEnemySound( CBaseEntity *pEnemy ); + virtual void FoundEnemySound( CBaseEntity *pEnemy ); +#endif + //--------------------------------- // Speech & Acting //--------------------------------- @@ -379,6 +390,14 @@ class CAI_PlayerAlly : public CAI_BaseActor CBaseEntity *FindSpeechTarget( int flags ); virtual bool IsValidSpeechTarget( int flags, CBaseEntity *pEntity ); + +#ifdef EZ2 + // Used by Wilson camera targets + virtual const Vector &GetSpeechTargetSearchOrigin() { return GetAbsOrigin(); } + virtual const Vector GetEyePositionForSpeech( CBaseEntity *pSpeechTarget ) { return EyePosition(); } + virtual const Vector &GetWorldSpaceCenterForSpeech( CBaseEntity *pSpeechTarget ) { return WorldSpaceCenter(); } + virtual const Vector &GetAbsOriginForSpeech( CBaseEntity *pSpeechTarget ) { return GetAbsOrigin(); } +#endif CBaseEntity *GetSpeechTarget() { return m_hTalkTarget.Get(); } void SetSpeechTarget( CBaseEntity *pSpeechTarget ) { m_hTalkTarget = pSpeechTarget; } @@ -404,6 +423,8 @@ class CAI_PlayerAlly : public CAI_BaseActor // So Will-E can override idle speech stuff virtual void HandlePrescheduleIdleSpeech(); inline void SetNextIdleSpeechTime( float flTime ) { m_flNextIdleSpeechTime = flTime; } + + bool Remark( AI_CriteriaSet &modifiers, CBaseEntity *pRemarkable ) { return SpeakIfAllowed( TLK_REMARK, modifiers ); } #endif //--------------------------------- diff --git a/sp/src/game/server/basecombatcharacter.cpp b/sp/src/game/server/basecombatcharacter.cpp index b9211c507ec..d73aa59334d 100644 --- a/sp/src/game/server/basecombatcharacter.cpp +++ b/sp/src/game/server/basecombatcharacter.cpp @@ -209,6 +209,15 @@ BEGIN_ENT_SCRIPTDESC( CBaseCombatCharacter, CBaseFlex, "The base class shared by DEFINE_SCRIPTFUNC( EyeDirection2D, "Get the eyes' 2D direction." ) DEFINE_SCRIPTFUNC( EyeDirection3D, "Get the eyes' 3D direction." ) + DEFINE_SCRIPTFUNC( LastHitGroup, "Get the last hitgroup." ) + +#ifdef GLOWS_ENABLE + DEFINE_SCRIPTFUNC( AddGlowEffect, "" ) + DEFINE_SCRIPTFUNC( RemoveGlowEffect, "" ) + DEFINE_SCRIPTFUNC( IsGlowEffectActive, "" ) + DEFINE_SCRIPTFUNC( SetGlowColor, "" ) +#endif + // // Hooks // @@ -1646,6 +1655,15 @@ void CBaseCombatCharacter::FixupBurningServerRagdoll( CBaseEntity *pRagdoll ) } } +inline bool CBaseCombatCharacter::ShouldFadeServerRagdolls() const +{ +#ifdef MAPBASE + return IsNPC() ? HasSpawnFlags( SF_NPC_FADE_CORPSE ) : true; +#else + return true; +#endif +} + bool CBaseCombatCharacter::BecomeRagdollBoogie( CBaseEntity *pKiller, const Vector &forceVector, float duration, int flags ) { Assert( CanBecomeRagdoll() ); @@ -1654,7 +1672,7 @@ bool CBaseCombatCharacter::BecomeRagdollBoogie( CBaseEntity *pKiller, const Vect info.SetDamageForce( forceVector ); - CBaseEntity *pRagdoll = CreateServerRagdoll( this, 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); + CBaseEntity *pRagdoll = CreateServerRagdoll( this, 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, ShouldFadeServerRagdolls() ); pRagdoll->SetCollisionBounds( CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs() ); @@ -1677,7 +1695,7 @@ CBaseEntity *CBaseCombatCharacter::BecomeRagdollBoogie( CBaseEntity *pKiller, co info.SetDamageForce( forceVector ); - CBaseEntity *pRagdoll = CreateServerRagdoll( this, 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); + CBaseEntity *pRagdoll = CreateServerRagdoll( this, 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, ShouldFadeServerRagdolls() ); pRagdoll->SetCollisionBounds( CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs() ); @@ -1762,7 +1780,7 @@ bool CBaseCombatCharacter::BecomeRagdoll( const CTakeDamageInfo &info, const Vec #endif // in single player create ragdolls on the server when the player hits someone // with their vehicle - for more dramatic death/collisions - CBaseEntity *pRagdoll = CreateServerRagdoll( this, m_nForceBone, info2, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); + CBaseEntity *pRagdoll = CreateServerRagdoll( this, m_nForceBone, info2, COLLISION_GROUP_INTERACTIVE_DEBRIS, ShouldFadeServerRagdolls() ); FixupBurningServerRagdoll( pRagdoll ); #ifdef EZ m_hDeathRagdoll = pRagdoll; @@ -1779,7 +1797,7 @@ bool CBaseCombatCharacter::BecomeRagdoll( const CTakeDamageInfo &info, const Vec // Burning corpses are server-side in episodic, if we're in darkness mode if ( IsOnFire() && HL2GameRules()->IsAlyxInDarknessMode() ) { - CBaseEntity *pRagdoll = CreateServerRagdoll( this, m_nForceBone, newinfo, COLLISION_GROUP_DEBRIS ); + CBaseEntity *pRagdoll = CreateServerRagdoll( this, m_nForceBone, newinfo, COLLISION_GROUP_DEBRIS, ShouldFadeServerRagdolls() ); FixupBurningServerRagdoll( pRagdoll ); #ifdef EZ m_hDeathRagdoll = pRagdoll; @@ -1803,7 +1821,7 @@ bool CBaseCombatCharacter::BecomeRagdoll( const CTakeDamageInfo &info, const Vec return false; //FIXME: This is fairly leafy to be here, but time is short! - CBaseEntity *pRagdoll = CreateServerRagdoll( this, m_nForceBone, newinfo, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); + CBaseEntity *pRagdoll = CreateServerRagdoll( this, m_nForceBone, newinfo, COLLISION_GROUP_INTERACTIVE_DEBRIS, ShouldFadeServerRagdolls() ); FixupBurningServerRagdoll( pRagdoll ); #ifdef EZ m_hDeathRagdoll = pRagdoll; @@ -1817,7 +1835,7 @@ bool CBaseCombatCharacter::BecomeRagdoll( const CTakeDamageInfo &info, const Vec if( hl2_episodic.GetBool() && Classify() == CLASS_PLAYER_ALLY_VITAL ) { - CreateServerRagdoll( this, m_nForceBone, newinfo, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); + CreateServerRagdoll( this, m_nForceBone, newinfo, COLLISION_GROUP_INTERACTIVE_DEBRIS, ShouldFadeServerRagdolls() ); RemoveDeferred(); return true; } @@ -2458,6 +2476,14 @@ void CBaseCombatCharacter::Weapon_HandleEquip( CBaseCombatWeapon *pWeapon ) if (pWeapon->GetSlot() == m_hMyWeapons[i]->GetSlot() && pWeapon->GetPosition() == m_hMyWeapons[i]->GetPosition()) { +#ifdef EZ2 + if (Weapon_EquipDual( pWeapon, m_hMyWeapons[i] )) + { + // Weapon has been merged with the existing weapon + return; + } +#endif + // Replace our existing weapon in this slot Weapon_Drop(m_hMyWeapons[i]); { diff --git a/sp/src/game/server/basecombatcharacter.h b/sp/src/game/server/basecombatcharacter.h index 7b1690fa983..da0d37d9ae9 100644 --- a/sp/src/game/server/basecombatcharacter.h +++ b/sp/src/game/server/basecombatcharacter.h @@ -239,6 +239,10 @@ class CBaseCombatCharacter : public CBaseFlex virtual void Weapon_Equip( CBaseCombatWeapon *pWeapon ); // Adds weapon to player virtual void Weapon_EquipHolstered( CBaseCombatWeapon *pWeapon ); // Pretty much only useful for NPCs virtual void Weapon_HandleEquip( CBaseCombatWeapon *pWeapon ); +#ifdef EZ2 + // Used by CEZ2_Player and CNPC_Assassin + virtual bool Weapon_EquipDual( CBaseCombatWeapon *pWeapon, CBaseCombatWeapon *pExistingWeapon ) { return false; } +#endif #else virtual void Weapon_Equip( CBaseCombatWeapon *pWeapon ); // Adds weapon to player #endif @@ -350,6 +354,8 @@ class CBaseCombatCharacter : public CBaseFlex // A version of BecomeRagdollBoogie() that allows the color to change and returns the entity itself instead. // In order to avoid breaking anything, it doesn't change the original function. virtual CBaseEntity *BecomeRagdollBoogie( CBaseEntity *pKiller, const Vector &forceVector, float duration, int flags, const Vector *vecColor ); + + bool ShouldFadeServerRagdolls() const; #endif CBaseEntity *FindHealthItem( const Vector &vecPosition, const Vector &range ); diff --git a/sp/src/game/server/baseentity.cpp b/sp/src/game/server/baseentity.cpp index 9df5cc09df1..d62e95364b5 100644 --- a/sp/src/game/server/baseentity.cpp +++ b/sp/src/game/server/baseentity.cpp @@ -2312,6 +2312,8 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC_NAMED( ScriptEntityToWorldTransform, "EntityToWorldTransform", "Get the entity's transform" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetPhysicsObject, "GetPhysicsObject", "Get the entity's physics object if it has one" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptPhysicsInitNormal, "PhysicsInitNormal", "Initializes the entity's physics object with the specified solid type, solid flags, and whether to start asleep" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptPhysicsDestroyObject, "PhysicsDestroyObject", "Destroys the entity's physics object" ) DEFINE_SCRIPTFUNC( ApplyAbsVelocityImpulse, "" ) DEFINE_SCRIPTFUNC( ApplyLocalAngularVelocityImpulse, "" ) diff --git a/sp/src/game/server/baseentity.h b/sp/src/game/server/baseentity.h index 3c2b7636198..8b6653d5084 100644 --- a/sp/src/game/server/baseentity.h +++ b/sp/src/game/server/baseentity.h @@ -633,6 +633,9 @@ class CBaseEntity : public IServerEntity void ValidateEntityConnections(); void FireNamedOutput( const char *pszOutput, variant_t variant, CBaseEntity *pActivator, CBaseEntity *pCaller, float flDelay = 0.0f ); +#ifdef MAPBASE + virtual +#endif CBaseEntityOutput *FindNamedOutput( const char *pszOutput ); #ifdef MAPBASE_VSCRIPT void ScriptFireOutput( const char *pszOutput, HSCRIPT hActivator, HSCRIPT hCaller, const char *szValue, float flDelay ); @@ -891,6 +894,13 @@ class CBaseEntity : public IServerEntity void SetAIWalkable( bool bBlocksLOS ); bool IsAIWalkable( void ); + +#ifdef MAPBASE + // Handle a potentially complex command from a client. + // Returns true if the command was handled successfully. + virtual bool HandleEntityCommand(CBasePlayer* pClient, KeyValues* pKeyValues) { return false; } +#endif // MAPBASE + private: int SaveDataDescBlock( ISave &save, datamap_t *dmap ); int RestoreDataDescBlock( IRestore &restore, datamap_t *dmap ); @@ -2135,6 +2145,8 @@ class CBaseEntity : public IServerEntity HSCRIPT ScriptEntityToWorldTransform( void ); HSCRIPT ScriptGetPhysicsObject( void ); + void ScriptPhysicsInitNormal( int nSolidType, int nSolidFlags, bool createAsleep ); + void ScriptPhysicsDestroyObject() { VPhysicsDestroyObject(); } void ScriptSetParent(HSCRIPT hParent, const char *szAttachment); #endif diff --git a/sp/src/game/server/episodic/grenade_hopwire.cpp b/sp/src/game/server/episodic/grenade_hopwire.cpp index 2f8bd27c2d7..8e54b55c2db 100644 --- a/sp/src/game/server/episodic/grenade_hopwire.cpp +++ b/sp/src/game/server/episodic/grenade_hopwire.cpp @@ -106,6 +106,7 @@ int g_interactionXenGrenadeHop = 0; int g_interactionXenGrenadeRagdoll = 0; int g_interactionStasisGrenadeFreeze = 0; +int g_interactionStasisGrenadeUnfreeze = 0; #endif #ifdef EZ2 @@ -2366,6 +2367,7 @@ void CGrenadeHopwire::Precache( void ) g_interactionXenGrenadeRagdoll = CBaseCombatCharacter::GetInteractionID(); g_interactionStasisGrenadeFreeze = CBaseCombatCharacter::GetInteractionID(); + g_interactionStasisGrenadeUnfreeze = CBaseCombatCharacter::GetInteractionID(); } if (GetHopwireStyle() == HOPWIRE_XEN) @@ -3010,42 +3012,36 @@ void CStasisVortexController::PullThink( void ) // Reset ragdoll pRagdoll = NULL; - // Freeze NPCs by stopping their think function - if (pEnts[i]->MyCombatCharacterPointer()) + // Freeze NPCs + if (pEnts[i]->MyNPCPointer()) { - // If this NPC has already been frozen, don't refreeze - if (pEnts[i]->GetNextThink() == TICK_NEVER_THINK) + // If this NPC is in the freeze schedule, set it's think to the end of the vortex + if (pEnts[i]->MyNPCPointer()->IsCurSchedule(SCHED_NPC_FREEZE, false)) { + pEnts[i]->SetNextThink(MAX(pEnts[i]->GetNextThink(), m_flEndTime - TICK_INTERVAL)); + pEnts[i]->SetAbsVelocity(vec3_origin); continue; } - if (pEnts[i]->MyNPCPointer()) - { - pEnts[i]->MyNPCPointer()->SetCondition( COND_NPC_FREEZE ); - pEnts[i]->MyNPCPointer()->TaskInterrupt(); - } - - pEnts[i]->SetContextThink( &CStasisVortexController::UnfreezeNPCThink, m_flEndTime, "StasisGrenadeUnfreeze" ); + pEnts[i]->MyNPCPointer()->SetCondition(COND_NPC_FREEZE); + pEnts[i]->MyNPCPointer()->TaskInterrupt(); - // Don't cancel the think function until the NPC picks up SCHED_NPC_FREEZE - if (pEnts[i]->MyNPCPointer() && !(pEnts[i]->MyNPCPointer()->IsCurSchedule(SCHED_NPC_FREEZE, false))) - continue; - - pEnts[i]->SetNextThink( TICK_NEVER_THINK ); - DevMsg( "NPC '%s' caught in stasis field! Thinking stopped\n", pEnts[i]->GetDebugName() ); + // Add tick interval to the unfreeze time to make sure that the unfreeze is after the end time + pEnts[i]->SetContextThink(&CStasisVortexController::UnfreezeNPCThink, m_flEndTime + TICK_INTERVAL, "StasisGrenadeUnfreeze"); continue; } if (FClassnameIs( pEnts[i], "prop_ragdoll" )) { - pRagdoll = dynamic_cast< CRagdollProp* >(this); + pRagdoll = dynamic_cast< CRagdollProp* >(pEnts[i]); } - if (pRagdoll) + if (pRagdoll && pRagdoll->IsMotionEnabled()) { pRagdoll->DisableMotion(); - pRagdoll->SetContextThink( &CStasisVortexController::UnfreezePhysicsObjectThink, m_flEndTime, "StasisGrenadeUnfreeze" ); + // Add tick interval to the unfreeze time to make sure that the unfreeze is after the end time + pEnts[i]->SetContextThink( &CStasisVortexController::UnfreezePhysicsObjectThink, m_flEndTime + TICK_INTERVAL, "StasisGrenadeUnfreeze" ); continue; } @@ -3065,7 +3061,8 @@ void CStasisVortexController::PullThink( void ) pPhysObject->EnableMotion(false); pPhysObject->EnableGravity( false ); - pEnts[i]->SetContextThink( &CStasisVortexController::UnfreezePhysicsObjectThink, m_flEndTime, "StasisGrenadeUnfreeze" ); + // Add tick interval to the unfreeze time to make sure that the unfreeze is after the end time + pEnts[i]->SetContextThink( &CStasisVortexController::UnfreezePhysicsObjectThink, m_flEndTime + TICK_INTERVAL, "StasisGrenadeUnfreeze" ); } // Keep going if need-be @@ -3109,11 +3106,14 @@ void CStasisVortexController::UnfreezeNPCThink( void ) if (MyNPCPointer() == NULL) return; - MyNPCPointer()->TaskInterrupt(); + // Dispatch interaction. Skip unfreezing if it returns true + if (MyNPCPointer()->DispatchInteraction(g_interactionStasisGrenadeUnfreeze, NULL, GetThrower())) + { + return; + } + MyNPCPointer()->ClearCondition( COND_NPC_FREEZE ); MyNPCPointer()->SetCondition( COND_NPC_UNFREEZE ); - MyNPCPointer()->Think(); - SetNextThink( gpGlobals->curtime ); } //----------------------------------------------------------------------------- @@ -3125,15 +3125,17 @@ void CStasisVortexController::UnfreezePhysicsObjectThink( void ) SetContextThink( NULL, TICK_NEVER_THINK, "StasisGrenadeUnfreeze" ); - if (FClassnameIs( this, "prop_ragdoll" )) + if (FClassnameIs( GetBaseEntity(), "prop_ragdoll")) { - pRagdoll = dynamic_cast< CRagdollProp* >(this); + pRagdoll = dynamic_cast< CRagdollProp* >(GetBaseEntity()); } if (pRagdoll) { - inputdata_t dummy; - pRagdoll->InputEnableMotion( dummy ); + inputdata_t ragdollData; + ragdollData.value.SetFloat(0.1f); + pRagdoll->InputEnableMotion(ragdollData); + pRagdoll->InputStartRadgollBoogie(ragdollData); return; } @@ -3159,7 +3161,7 @@ void CStasisVortexController::StartPull( const Vector &origin, float radius, flo m_flStrength= strength; // Play a danger sound throughout the duration of the vortex so that NPCs run away - CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), radius, duration, this ); + CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), radius * 1.25f, duration * 1.25f, this ); SetDefLessFunc( m_SpawnList ); m_SpawnList.EnsureCapacity( 16 ); diff --git a/sp/src/game/server/episodic/npc_advisor.cpp b/sp/src/game/server/episodic/npc_advisor.cpp index 93bf5065b16..711d3ca74ef 100644 --- a/sp/src/game/server/episodic/npc_advisor.cpp +++ b/sp/src/game/server/episodic/npc_advisor.cpp @@ -616,6 +616,8 @@ class CNPC_Advisor : public CAI_BaseActor #ifdef EZ2 virtual void SetModel( const char *szModelName ); + + void GetDefaultSpriteColor(int &r, int &g, int &b); virtual CSprite *GetGlowSpritePtr( int i ); virtual void SetGlowSpritePtr( int i, CSprite *sprite ); @@ -1242,6 +1244,28 @@ void CNPC_Advisor::SetModel( const char *szModelName ) gm_nRRearTopJetAttachment = LookupAttachment( "attach_r_rear_top_jet" ); } +//----------------------------------------------------------------------------- +// Purpose: The advisor's default sprite color +//----------------------------------------------------------------------------- +void CNPC_Advisor::GetDefaultSpriteColor( int &r, int &g, int &b ) +{ + EZ_VARIANT EZvar = GetEZVariant(); + switch (EZvar) + { + case EZ_VARIANT_ATHENAEUM: + r = 160; + g = 48; + b = 255; + break; + + default: + r = 0; + g = 255; + b = 255; + break; + } +} + //----------------------------------------------------------------------------- // Purpose: Return the pointer for a given sprite given an index //----------------------------------------------------------------------------- @@ -1291,9 +1315,7 @@ EyeGlow_t *CNPC_Advisor::GetEyeGlowData( int index ) eyeGlow->alpha = 255; eyeGlow->brightness = 224; - eyeGlow->red = 0; - eyeGlow->green = 255; - eyeGlow->blue = 255; + GetDefaultSpriteColor( eyeGlow->red, eyeGlow->green, eyeGlow->blue ); eyeGlow->renderMode = kRenderWorldGlow; eyeGlow->scale = 0.1f; @@ -1307,9 +1329,7 @@ EyeGlow_t *CNPC_Advisor::GetEyeGlowData( int index ) eyeGlow->alpha = 255; eyeGlow->brightness = 224; - eyeGlow->red = 0; - eyeGlow->green = 255; - eyeGlow->blue = 255; + GetDefaultSpriteColor( eyeGlow->red, eyeGlow->green, eyeGlow->blue ); eyeGlow->renderMode = kRenderWorldGlow; eyeGlow->scale = 0.1f; @@ -1343,8 +1363,11 @@ void CNPC_Advisor::SetEyeState( AdvisorEyeState_t state ) } else { - m_pEyeGlow->SetRenderColor( 0, 255, 255 ); - m_pCameraGlow2->SetRenderColor( 0, 255, 255 ); + int r, g, b; + GetDefaultSpriteColor( r, g, b ); + + m_pEyeGlow->SetRenderColor( r, g, b ); + m_pCameraGlow2->SetRenderColor( r, g, b ); m_pEyeGlow->SetScale( 0.1 ); m_pCameraGlow2->SetScale( 0.1 ); diff --git a/sp/src/game/server/ez2/ez2_player.cpp b/sp/src/game/server/ez2/ez2_player.cpp index 3f96e47471f..faaaf617d69 100644 --- a/sp/src/game/server/ez2/ez2_player.cpp +++ b/sp/src/game/server/ez2/ez2_player.cpp @@ -69,6 +69,8 @@ BEGIN_DATADESC(CEZ2_Player) DEFINE_FIELD(m_hSpeechTarget, FIELD_EHANDLE), + DEFINE_KEYFIELD( m_bCanDualWield, FIELD_BOOLEAN, "CanDualWield" ), + // These don't need to be saved //DEFINE_FIELD(m_iVisibleEnemies, FIELD_INTEGER), //DEFINE_FIELD(m_iCloseEnemies, FIELD_INTEGER), @@ -80,9 +82,12 @@ BEGIN_DATADESC(CEZ2_Player) DEFINE_INPUTFUNC(FIELD_VOID, "StopScripting", InputStopScripting), DEFINE_INPUTFUNC(FIELD_VOID, "__FinishBonusChallenge", InputFinishBonusChallenge), + + DEFINE_INPUTFUNC( FIELD_VOID, "EnableDualWield", InputEnableDualWield ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableDualWield", InputDisableDualWield ), END_DATADESC() -BEGIN_ENT_SCRIPTDESC( CEZ2_Player, CBasePlayer, "E:Z2's player entity." ) +BEGIN_ENT_SCRIPTDESC( CEZ2_Player, CHL2_Player, "E:Z2's player entity." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetNPCComponent, "GetNPCComponent", "Gets the player's NPC component." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetStaringEntity, "GetStaringEntity", "Gets the player's staring entity." ) @@ -509,8 +514,6 @@ void CEZ2_Player::Spawn( void ) BaseClass::Spawn(); - SetModel( "models/bad_cop.mdl" ); - Activate(); if (GetBonusChallenge() != EZ_CHALLENGE_NONE) @@ -1601,7 +1604,7 @@ bool CEZ2_Player::GetGameTextSpeechParams( hudtextparms_t ¶ms ) //----------------------------------------------------------------------------- CAI_Expresser *CEZ2_Player::CreateExpresser(void) { - m_pExpresser = new CAI_Expresser(this); + m_pExpresser = new CAI_ExpresserWithFollowup(this); if (!m_pExpresser) return NULL; @@ -1993,6 +1996,48 @@ void CEZ2_Player::Weapon_HandleEquip( CBaseCombatWeapon *pWeapon ) } } +//----------------------------------------------------------------------------- +// Purpose: Equips a dual weapon +//----------------------------------------------------------------------------- +bool CEZ2_Player::Weapon_EquipDual( CBaseCombatWeapon *pWeapon, CBaseCombatWeapon *pExistingWeapon ) +{ + if (!m_bCanDualWield) + return false; + + // For now, only identical weapons can be dual wielded + if (pWeapon->GetClassname() != pExistingWeapon->GetClassname()) + return false; + + CBaseHLCombatWeapon *pHLWeapon = dynamic_cast(pWeapon); + if (pHLWeapon && pHLWeapon->CanDualWield() && pHLWeapon->GetWpnData().szViewModelDual[0]) + { + CBaseHLCombatWeapon *pHLExistingWeapon = static_cast(pExistingWeapon); + Assert( pHLExistingWeapon ); + + if (pHLExistingWeapon && !pHLExistingWeapon->GetLeftHandGun()) + { + pHLExistingWeapon->CreateLeftHandGun(); + + // Combine the ammo + pHLExistingWeapon->m_iClip1 += pWeapon->m_iClip1; + pHLExistingWeapon->m_iClip2 += pWeapon->m_iClip2; + + // Switch to the new dual weapon + Weapon_Switch( pExistingWeapon ); + + // Remove the weapon on the ground + UTIL_Remove( pWeapon ); + + // Emit a pickup sound + EmitSound( "BaseCombatCharacter.AmmoPickup" ); + + return true; + } + } + + return false; +} + //----------------------------------------------------------------------------- // Purpose: Event fired upon picking up a new weapon //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/ez2/ez2_player.h b/sp/src/game/server/ez2/ez2_player.h index 6374663b16c..ef22414ea45 100644 --- a/sp/src/game/server/ez2/ez2_player.h +++ b/sp/src/game/server/ez2/ez2_player.h @@ -126,6 +126,8 @@ class CEZ2_Player : public CAI_ExpresserHost, public CGameEventList virtual CAI_Expresser * CreateExpresser(void); virtual CAI_Expresser * GetExpresser() { return m_pExpresser; } + bool IsAllowedToSpeakFollowup( AIConcept_t concept ) { return IsAllowedToSpeak( concept ); } + bool GetGameTextSpeechParams( hudtextparms_t ¶ms ); void ModifyOrAppendDamageCriteria(AI_CriteriaSet & set, const CTakeDamageInfo & info, bool bPlayer = true); @@ -184,6 +186,7 @@ class CEZ2_Player : public CAI_ExpresserHost, public CGameEventList bool HandleRemoveFromPlayerSquad( CAI_BaseNPC *pNPC ); void Weapon_HandleEquip( CBaseCombatWeapon *pWeapon ); + bool Weapon_EquipDual( CBaseCombatWeapon *pWeapon, CBaseCombatWeapon *pExistingWeapon ); void Event_FirstDrawWeapon( CBaseCombatWeapon *pWeapon ); void Event_ThrewGrenade( CBaseCombatWeapon *pWeapon ); @@ -210,6 +213,9 @@ class CEZ2_Player : public CAI_ExpresserHost, public CGameEventList void InputStartScripting( inputdata_t &inputdata ); void InputStopScripting( inputdata_t &inputdata ); + void InputEnableDualWield( inputdata_t &inputdata ) { m_bCanDualWield = true; } + void InputDisableDualWield( inputdata_t &inputdata ) { m_bCanDualWield = false; } + // Blixibon - Speech thinking implementation void DoSpeechAI(); bool DoIdleSpeech(); @@ -302,6 +308,8 @@ class CEZ2_Player : public CAI_ExpresserHost, public CGameEventList EHANDLE m_hSpeechTarget; + bool m_bCanDualWield; + int m_iVisibleEnemies; int m_iCloseEnemies; diff --git a/sp/src/game/server/ez2/ez2_save_metadata.cpp b/sp/src/game/server/ez2/ez2_save_metadata.cpp index 4516aa44758..6ed3ed95b1f 100644 --- a/sp/src/game/server/ez2/ez2_save_metadata.cpp +++ b/sp/src/game/server/ez2/ez2_save_metadata.cpp @@ -7,6 +7,7 @@ #include "cbase.h" #include "tier3/tier3.h" #include "vgui/ILocalize.h" +#include "achievementmgr.h" #include "npc_wilson.h" //============================================================================= @@ -85,6 +86,13 @@ class CCustomSaveMetadata : public CAutoGameSystem pCustomSaveMetadata->SetBool("wilson", true ); } + // Cheated + CAchievementMgr *pAchievementMgr = dynamic_cast(engine->GetAchievementMgr()); + if (pAchievementMgr) + { + pCustomSaveMetadata->SetBool( "cheated", pAchievementMgr->WereCheatsEverOn() ); + } + Msg( "Saving custom metadata to %s\n", name ); pCustomSaveMetadata->SaveToFile( filesystem, name, "MOD" ); diff --git a/sp/src/game/server/ez2/npc_assassin.cpp b/sp/src/game/server/ez2/npc_assassin.cpp index 161bd19247e..0561d9b6194 100644 --- a/sp/src/game/server/ez2/npc_assassin.cpp +++ b/sp/src/game/server/ez2/npc_assassin.cpp @@ -1880,26 +1880,7 @@ void CNPC_Assassin::AddLeftHandGun( CBaseCombatWeapon *pWeapon ) return; } - // Create a fake second pistol - CBaseEntity *pEnt = CBaseEntity::CreateNoSpawn( "prop_dynamic_override", this->GetLocalOrigin(), this->GetLocalAngles(), this ); - if (pEnt) - { - // HACKHACK: Just add "_left" to the end of the model name - char szLeftModel[MAX_PATH]; - V_StripExtension( pWeapon->GetWorldModel(), szLeftModel, sizeof( szLeftModel ) ); - V_strncat( szLeftModel, "_left.mdl", sizeof( szLeftModel ) ); - - pEnt->SetModelName( MAKE_STRING( szLeftModel ) ); - pEnt->SetRenderMode( kRenderTransColor ); - DispatchSpawn( pEnt ); - pEnt->FollowEntity( this, true ); - pEnt->SetOwnerEntity( pWeapon ); - - m_hLeftHandGun = static_cast(pEnt); - - // Make it dual-wielded - assert_cast(pWeapon)->SetLeftHandGun( m_hLeftHandGun ); - } + m_hLeftHandGun = assert_cast(pWeapon)->CreateLeftHandGun(); } //----------------------------------------------------------------------------- @@ -2018,24 +1999,28 @@ void CNPC_Assassin::Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecT } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: Equips a dual weapon //----------------------------------------------------------------------------- -void CNPC_Assassin::Weapon_Equip( CBaseCombatWeapon *pWeapon ) +bool CNPC_Assassin::Weapon_EquipDual( CBaseCombatWeapon *pWeapon, CBaseCombatWeapon *pExistingWeapon ) { - if ( GetActiveWeapon() && GetActiveWeapon()->GetClassname() == pWeapon->GetClassname() ) + if (GetActiveWeapon() != pExistingWeapon) + return false; + + // For now, only identical weapons can be dual wielded + if (pWeapon->GetClassname() != pExistingWeapon->GetClassname()) + return false; + + CBaseHLCombatWeapon *pHLWeapon = dynamic_cast(pWeapon); + if (pHLWeapon && pHLWeapon->CanDualWield() && !m_bDualWeapons) { - CBaseHLCombatWeapon *pHLWeapon = dynamic_cast(pWeapon); - if ( pHLWeapon && pHLWeapon->CanDualWield() && !m_bDualWeapons ) - { - // Add left hand gun for weapon that already exists - AddLeftHandGun( GetActiveWeapon() ); - m_bDualWeapons = true; - UTIL_Remove( pWeapon ); - return; - } + // Add left hand gun for weapon that already exists + AddLeftHandGun( GetActiveWeapon() ); + m_bDualWeapons = true; + UTIL_Remove( pWeapon ); + return true; } - BaseClass::Weapon_Equip( pWeapon ); + return false; } //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/ez2/npc_assassin.h b/sp/src/game/server/ez2/npc_assassin.h index 42bf0413175..c0ba340437e 100644 --- a/sp/src/game/server/ez2/npc_assassin.h +++ b/sp/src/game/server/ez2/npc_assassin.h @@ -76,7 +76,7 @@ class CNPC_Assassin : public CNPC_Combine void Weapon_HandleEquip( CBaseCombatWeapon *pWeapon ); void Weapon_EquipHolstered( CBaseCombatWeapon *pWeapon ); void Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarget = NULL, const Vector *pVelocity = NULL ); - void Weapon_Equip( CBaseCombatWeapon *pWeapon ); + bool Weapon_EquipDual( CBaseCombatWeapon *pWeapon, CBaseCombatWeapon *pExistingWeapon ); Vector GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget = NULL ); float GetSpreadBias( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget ); diff --git a/sp/src/game/server/ez2/npc_basepredator.cpp b/sp/src/game/server/ez2/npc_basepredator.cpp index 46e850fa9c3..3e2474e3b00 100644 --- a/sp/src/game/server/ez2/npc_basepredator.cpp +++ b/sp/src/game/server/ez2/npc_basepredator.cpp @@ -485,8 +485,8 @@ bool CNPC_BasePredator::ShouldAttackObstruction( CBaseEntity *pEntity ) if ( !pEntity->MyCombatCharacterPointer() && pEntity->GetMoveType() != MOVETYPE_VPHYSICS ) return false; - // Don't attack props which don't have motion enabled - if ( pEntity->VPhysicsGetObject() && !pEntity->VPhysicsGetObject()->IsMotionEnabled() ) + // Don't attack props which don't have motion enabled or have more than 2x mass than us + if ( pEntity->VPhysicsGetObject() && (!pEntity->VPhysicsGetObject()->IsMotionEnabled() || pEntity->VPhysicsGetObject()->GetMass()*2 > GetMass()) ) return false; // Don't attack NPCs in the same squad diff --git a/sp/src/game/server/ez2/npc_bullsquid.cpp b/sp/src/game/server/ez2/npc_bullsquid.cpp index d9abc186a9c..fa52f27a02d 100644 --- a/sp/src/game/server/ez2/npc_bullsquid.cpp +++ b/sp/src/game/server/ez2/npc_bullsquid.cpp @@ -157,6 +157,9 @@ void CNPC_Bullsquid::Precache() { m_AdultModelName = GetModelName(); } + else + PrecacheModel( STRING( m_AdultModelName ) ); + // If there is no baby model name, use the same model as regular if ( m_BabyModelName == NULL_STRING ) { @@ -192,6 +195,8 @@ void CNPC_Bullsquid::Precache() } PrecacheModel( STRING( GetModelName() ) ); + PrecacheModel( STRING( m_BabyModelName ) ); + PrecacheModel( STRING( m_EggModelName ) ); m_nSquidSpitSprite = PrecacheModel("sprites/greenspit1.vmt");// client side spittle. diff --git a/sp/src/game/server/ez2/npc_flyingpredator.cpp b/sp/src/game/server/ez2/npc_flyingpredator.cpp index e7d4fd96dcc..54573b1d229 100644 --- a/sp/src/game/server/ez2/npc_flyingpredator.cpp +++ b/sp/src/game/server/ez2/npc_flyingpredator.cpp @@ -176,6 +176,9 @@ BEGIN_DATADESC( CNPC_FlyingPredator ) DEFINE_FIELD( m_vDesiredTarget, FIELD_VECTOR ), DEFINE_FIELD( m_vCurrentTarget, FIELD_VECTOR ), + DEFINE_KEYFIELD( m_AdultModelName, FIELD_MODELNAME, "adultmodel" ), + DEFINE_KEYFIELD( m_BabyModelName, FIELD_MODELNAME, "babymodel" ), + DEFINE_INPUTFUNC( FIELD_STRING, "ForceFlying", InputForceFlying ), DEFINE_INPUTFUNC( FIELD_STRING, "Fly", InputFly ), @@ -188,14 +191,14 @@ void CNPC_FlyingPredator::Spawn() { Precache( ); - SetModel( STRING( GetModelName() ) ); - if (m_bIsBaby) { + SetModel( STRING( m_BabyModelName ) ); SetHullType( HULL_TINY ); } else { + SetModel( STRING( m_AdultModelName ) ); SetHullType( HULL_WIDE_SHORT ); } @@ -259,10 +262,23 @@ void CNPC_FlyingPredator::Precache() if ( GetModelName() == NULL_STRING ) { - SetModelName( AllocPooledString( m_bIsBaby ? "models/stukapup.mdl" : "models/stukabat.mdl" ) ); + SetModelName( AllocPooledString( "models/stukabat.mdl" ) ); + } + + if ( m_AdultModelName == NULL_STRING ) + { + m_AdultModelName = GetModelName(); + } + else + PrecacheModel( STRING( m_AdultModelName ) ); + + if ( m_BabyModelName == NULL_STRING ) + { + m_BabyModelName = AllocPooledString( "models/stukapup.mdl" ); } PrecacheModel( STRING( GetModelName() ) ); + PrecacheModel( STRING( m_BabyModelName ) ); if (m_tEzVariant == EZ_VARIANT_RAD) { @@ -304,9 +320,25 @@ int CNPC_FlyingPredator::RangeAttack1Conditions( float flDot, float flDist ) if (flDist <= InnateRange1MinRange()) return COND_TOO_CLOSE_TO_ATTACK; - // Trace hull to enemy if (GetEnemy()) { + // If the enemy isn't facing me, delegate attacks to squadmates which the enemy does see + // If the enemy isn't facing any squadmate, this doesn't matter + if (GetSquad() && !HasCondition( COND_ENEMY_FACING_ME )) + { + AISquadIter_t iter; + for ( CAI_BaseNPC *pNPC = m_pSquad->GetFirstMember(&iter); pNPC; pNPC = m_pSquad->GetNextMember(&iter) ) + { + // UNDONE: Classname check + //if (GetClassname() == pNPC->GetClassname()) + { + if (pNPC->GetEnemy() == GetEnemy() && pNPC->HasCondition( COND_ENEMY_FACING_ME )) + return COND_NONE; + } + } + } + + // Trace hull to enemy trace_t tr; AI_TraceHull( GetAbsOrigin(), GetEnemyLKP(), GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr); @@ -965,7 +997,8 @@ bool CNPC_FlyingPredator::SpawnNPC( const Vector position, bool isBaby ) pChild->m_tWanderState = this->m_tWanderState; pChild->m_bSpawningEnabled = this->m_bSpawningEnabled; pChild->m_bCanUseFlyNav = this->m_bCanUseFlyNav; - pChild->SetModelName( this->GetModelName() ); + pChild->m_BabyModelName = this->m_BabyModelName; + pChild->m_AdultModelName = this->m_AdultModelName; pChild->m_nSkin = this->m_nSkin; pChild->Precache(); @@ -1141,6 +1174,10 @@ void CNPC_FlyingPredator::DiveBombAttack() // Fly towards my enemy Vector vEnemyPos = GetEnemyLKP(); m_vecDiveBombDirection = vEnemyPos - GetLocalOrigin(); + + // Roughly guess how long the dive will be + float flSoundDuration = m_vecDiveBombDirection.Length() / 300.0f; + CSoundEnt::InsertSound( SOUND_DANGER, vEnemyPos, 64.0f, flSoundDuration, this, 0, GetEnemy() ); } else { @@ -1612,6 +1649,19 @@ void CNPC_FlyingPredator::StartTouch( CBaseEntity *pOther ) if ( IRelationType( pOther ) != D_LI ) { CTakeDamageInfo info( this, this, m_bIsBaby ? sk_flyingpredator_dmg_dive.GetFloat() / 3.0f : sk_flyingpredator_dmg_dive.GetFloat(), DMG_CLUB | DMG_ALWAYSGIB ); + + Vector vecForce = GetAbsVelocity(); + + // Push based on how much mass the object has. Don't boost if it's lower than ours + if (pOther->VPhysicsGetObject() && VPhysicsGetObject()) + vecForce *= MIN( (VPhysicsGetObject()->GetMass() / pOther->VPhysicsGetObject()->GetMass()), 1.0f ); + + info.SetDamageForce( vecForce ); + if (ShouldApplyHitVelocityToTarget( pOther )) + { + pOther->ApplyAbsVelocityImpulse( vecForce ); + } + pOther->TakeDamage( info ); if ( m_tFlyState == FlyState_Falling ) @@ -1622,6 +1672,7 @@ void CNPC_FlyingPredator::StartTouch( CBaseEntity *pOther ) else { EmitSound( "NPC_FlyingPredator.Bite" ); + VacateStrategySlot(); } } diff --git a/sp/src/game/server/ez2/npc_flyingpredator.h b/sp/src/game/server/ez2/npc_flyingpredator.h index 749a6fed789..e5ae16cf2cc 100644 --- a/sp/src/game/server/ez2/npc_flyingpredator.h +++ b/sp/src/game/server/ez2/npc_flyingpredator.h @@ -126,6 +126,9 @@ class CNPC_FlyingPredator : public CNPC_BasePredator Vector m_vDesiredTarget; Vector m_vCurrentTarget; + string_t m_BabyModelName; + string_t m_AdultModelName; + DEFINE_CUSTOM_AI; }; #endif // NPC_FLYINGPREDATOR_H \ No newline at end of file diff --git a/sp/src/game/server/ez2/npc_husk_soldier.cpp b/sp/src/game/server/ez2/npc_husk_soldier.cpp index 9ff63f03fa5..391f880b6a7 100644 --- a/sp/src/game/server/ez2/npc_husk_soldier.cpp +++ b/sp/src/game/server/ez2/npc_husk_soldier.cpp @@ -555,6 +555,16 @@ void CNPC_HuskSoldier::StartPlayerGive( CBasePlayer *pPlayer ) // We like this person now AddPassiveTarget( pPlayer ); + + m_OnGiftAccept.FireOutput( GetTarget(), this ); + + AI_CriteriaSet modifiers; + modifiers.AppendCriteria( "gift", GetTarget()->GetClassname() ); + modifiers.AppendCriteria( "gift_name", STRING( GetTarget()->GetEntityName() ) ); + GetTarget()->AppendContextToCriteria( modifiers, "gift_" ); + + // TODO: Add to CAI_PlayerAlly concept manager? + SpeakIfAllowed( "TLK_GIFT_ACCEPT", modifiers, SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL ); } //----------------------------------------------------------------------------- @@ -568,6 +578,15 @@ void CNPC_HuskSoldier::OnCantBeGivenObject( CBaseEntity *pItem ) SetSchedule( SCHED_HUSK_SOLDIER_REJECT_GIFT ); m_OnGiftReject.FireOutput( pItem, this ); + + AI_CriteriaSet modifiers; + + modifiers.AppendCriteria( "gift", pItem->GetClassname() ); + modifiers.AppendCriteria( "gift_name", STRING( pItem->GetEntityName() ) ); + pItem->AppendContextToCriteria( modifiers, "gift_" ); + + // TODO: Add to CAI_PlayerAlly concept manager? + SpeakIfAllowed( "TLK_GIFT_REJECT", modifiers, SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL ); } } diff --git a/sp/src/game/server/ez2/npc_wilson.cpp b/sp/src/game/server/ez2/npc_wilson.cpp index 91b0809c012..6f4d7471ade 100644 --- a/sp/src/game/server/ez2/npc_wilson.cpp +++ b/sp/src/game/server/ez2/npc_wilson.cpp @@ -82,6 +82,10 @@ ConVar npc_wilson_depressing_death("npc_wilson_depressing_death", "0", FCVAR_NON ConVar npc_wilson_clearance_speed_threshold( "npc_wilson_clearance_speed_threshold", "250.0", FCVAR_NONE, "The speed at which Will-E starts to think he's gonna get knocked off if approaching a surface." ); ConVar npc_wilson_clearance_debug( "npc_wilson_clearance_debug", "0", FCVAR_NONE, "Debugs Will-E's low clearance detection." ); +ConVar npc_wilson_camera_look_scale_pitch( "npc_wilson_camera_look_scale_pitch", "0.025", FCVAR_NONE, "How much to scale Wilson's eye movements when looking at a target through a camera." ); +ConVar npc_wilson_camera_look_scale_yaw( "npc_wilson_camera_look_scale_yaw", "0.05", FCVAR_NONE, "How much to scale Wilson's eye movements when looking at a target through a camera." ); +ConVar npc_wilson_camera_look_scale_roll( "npc_wilson_camera_look_scale_roll", "0.1", FCVAR_NONE, "How much to scale Wilson's eye movements when looking at a target through a camera." ); + static const char *g_DamageZapContext = "DamageZapEffect"; static const char *g_AutoSetLocatorContext = "AutoSetLocator"; @@ -128,7 +132,10 @@ BEGIN_DATADESC(CNPC_Wilson) DEFINE_KEYFIELD( m_bDead, FIELD_BOOLEAN, "dead" ), - DEFINE_INPUT( m_bOmniscient, FIELD_BOOLEAN, "SetOmniscient" ), + DEFINE_INPUT( m_bOmniscient, FIELD_BOOLEAN, "SetOmniscient" ), // TODO: Replace with "Omnipresent" when saves can be changed + DEFINE_KEYFIELD( m_iszCameraTargets, FIELD_STRING, "CameraTargets" ), + + DEFINE_KEYFIELD( m_bSeeThroughPlayer, FIELD_BOOLEAN, "SeeThroughPlayer" ), DEFINE_INPUT( m_bCanBeEnemy, FIELD_BOOLEAN, "SetCanBeEnemy" ), @@ -153,6 +160,15 @@ BEGIN_DATADESC(CNPC_Wilson) DEFINE_INPUTFUNC( FIELD_VOID, "TurnOnDeadMode", InputTurnOnDeadMode ), DEFINE_INPUTFUNC( FIELD_VOID, "TurnOffDeadMode", InputTurnOffDeadMode ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetCameraTargets", InputSetCameraTargets ), + DEFINE_INPUTFUNC( FIELD_VOID, "ClearCameraTargets", InputClearCameraTargets ), + + DEFINE_INPUTFUNC( FIELD_VOID, "EnableSeeThroughPlayer", InputEnableSeeThroughPlayer ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableSeeThroughPlayer", InputDisableSeeThroughPlayer ), + + DEFINE_INPUTFUNC( FIELD_VOID, "EnableOmnipresence", InputEnableOmnipresence ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableOmnipresence", InputDisableOmnipresence ), + DEFINE_THINKFUNC( TeslaThink ), DEFINE_OUTPUT( m_OnTipped, "OnTipped" ), @@ -278,6 +294,12 @@ void CNPC_Wilson::Spawn() CapabilitiesAdd( bits_CAP_SQUAD ); + if ( LookupAttachment( "forward" ) > 0 ) + { + // If we have the "forward" attachment, we can turn our head + CapabilitiesAdd( bits_CAP_TURN_HEAD ); + } + // Add to the player's squad if we have no squad name if (!GetSquad()) AddToSquad( GetPlayerSquadName() ); @@ -368,6 +390,42 @@ void CNPC_Wilson::Activate( void ) m_pMotionController->Enable(); } } + + RefreshCameraTargets(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CNPC_Wilson::KeyValue( const char *szKeyName, const char *szValue ) +{ + bool bResult = BaseClass::KeyValue( szKeyName, szValue ); + + if( !bResult ) + { + // Temporary until we can change saves and rename m_bOmniscient + if (FStrEq( szKeyName, "Omnipresent" )) + { + m_bOmniscient = atoi(szValue) != 0 ? true : false; + return true; + } + } + + return bResult; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Wilson::NPCInit( void ) +{ + BaseClass::NPCInit(); + + if (m_bOmniscient) + { + // Infinite look distance needed to see through distant cameras + SetDistLook( 9999999.0f ); + m_flDistTooFar = 9999999.0f; + } } //----------------------------------------------------------------------------- @@ -718,7 +776,7 @@ void CNPC_Wilson::NPCThink() { BaseClass::NPCThink(); - if (HasCondition(COND_IN_PVS)) + if (HasCondition(COND_IN_PVS) && (!m_bOmniscient || HasCondition(COND_SEE_PLAYER))) { // Needed for choreography reasons AimGun(); @@ -903,7 +961,7 @@ void CNPC_Wilson::GatherEnemyConditions( CBaseEntity *pEnemy ) if ( GetLastEnemyTime() == 0 || gpGlobals->curtime - GetLastEnemyTime() > 30 ) { - if ( HasCondition( COND_SEE_ENEMY ) && (WorldSpaceCenter() - pEnemy->WorldSpaceCenter()).LengthSqr() <= Square(384.0f) && pEnemy->Classify() != CLASS_BULLSEYE ) + if ( HasCondition( COND_SEE_ENEMY ) && (GetWorldSpaceCenterForSpeech( pEnemy ) - pEnemy->WorldSpaceCenter()).LengthSqr() <= Square(384.0f) && pEnemy->Classify() != CLASS_BULLSEYE ) { AI_CriteriaSet modifiers; @@ -1002,7 +1060,7 @@ void CNPC_Wilson::OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity if ( pAttacker ) { CBasePlayer *pPlayer = AI_GetSinglePlayer(); - if ( pPlayer && ( pPlayer->GetAbsOrigin().AsVector2D() - GetAbsOrigin().AsVector2D() ).LengthSqr() < Square( 25*12 ) && IsAllowedToSpeak( TLK_WATCHOUT ) ) + if ( pPlayer && ( pPlayer->GetAbsOrigin().AsVector2D() - GetAbsOriginForSpeech( pPlayer ).AsVector2D() ).LengthSqr() < Square( 25*12 ) && IsAllowedToSpeak( TLK_WATCHOUT ) ) { if ( !pPlayer->FInViewCone( pAttacker ) ) { @@ -1176,6 +1234,156 @@ void CNPC_Wilson::AimGun() } } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CNPC_Wilson::FInViewCone( const Vector &vecSpot ) +{ + if ( m_bSeeThroughPlayer ) + { + // Bad Cop has special eye + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if ( pPlayer && pPlayer->FInViewCone( vecSpot ) ) + return true; + } + + if ( m_hCameraTargets.Count() > 0 ) + { + FOR_EACH_VEC( m_hCameraTargets, i ) + { + if ( !m_hCameraTargets[i]->IsEnabled() ) + continue; + + if ( m_hCameraTargets[i]->FInViewCone( vecSpot ) ) + return true; + } + + // None of the cameras saw it + // If we're omniscient, then our actual POV is irrelevant + if (m_bOmniscient) + return false; + } + + return BaseClass::FInViewCone( vecSpot ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CNPC_Wilson::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker ) +{ + if ( m_bSeeThroughPlayer ) + { + // Bad Cop has special eye + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if ( pEntity == pPlayer || (pPlayer && pPlayer->FVisible( pEntity )) ) + return true; + } + + if ( m_hCameraTargets.Count() > 0 ) + { + FOR_EACH_VEC( m_hCameraTargets, i ) + { + if ( !m_hCameraTargets[i]->IsEnabled() ) + continue; + + // Note that this will not use the visibility cache normally used by CBaseCombatCharacters (which is acceptable) + if ( m_hCameraTargets[i]->FVisible( pEntity, traceMask, ppBlocker ) ) + return true; + } + + // None of the cameras saw it + // If we're omniscient, then our actual POV is irrelevant + if (m_bOmniscient) + return false; + } + + return BaseClass::FVisible( pEntity, traceMask, ppBlocker ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CNPC_Wilson::FVisible( const Vector &vecTarget, int traceMask, CBaseEntity **ppBlocker ) +{ + if ( m_bSeeThroughPlayer ) + { + // Bad Cop has special eye + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if ( pPlayer && pPlayer->FVisible( vecTarget ) ) + return true; + } + + if ( m_hCameraTargets.Count() > 0 ) + { + FOR_EACH_VEC( m_hCameraTargets, i ) + { + if ( !m_hCameraTargets[i]->IsEnabled() ) + continue; + + if ( m_hCameraTargets[i]->FVisible( vecTarget, traceMask, ppBlocker ) ) + return true; + } + + // None of the cameras saw it + // If we're omniscient, then our actual POV is irrelevant + if (m_bOmniscient) + return false; + } + + return BaseClass::FVisible( vecTarget, traceMask, ppBlocker ); +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the first camera which can see the target +//----------------------------------------------------------------------------- +CWilsonCamera *CNPC_Wilson::GetCameraForTarget( CBaseEntity *pTarget ) +{ + FOR_EACH_VEC( m_hCameraTargets, i ) + { + if ( !m_hCameraTargets[i]->IsEnabled() ) + continue; + + if ( m_hCameraTargets[i]->FInViewCone( pTarget->WorldSpaceCenter() ) && m_hCameraTargets[i]->FVisible( pTarget ) ) + return m_hCameraTargets[i]; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the first camera which can see the target +//----------------------------------------------------------------------------- +CWilsonCamera *CNPC_Wilson::GetCameraForTarget( const Vector &vecTarget ) +{ + FOR_EACH_VEC( m_hCameraTargets, i ) + { + if ( !m_hCameraTargets[i]->IsEnabled() ) + continue; + + if ( m_hCameraTargets[i]->FInViewCone( vecTarget ) && m_hCameraTargets[i]->FVisible( vecTarget ) ) + return m_hCameraTargets[i]; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Position of ears +//----------------------------------------------------------------------------- +Vector CNPC_Wilson::EarPosition( void ) +{ + if ( m_bSeeThroughPlayer ) + { + // Bad Cop has special ears + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if (pPlayer) + return pPlayer->EarPosition(); + } + + return BaseClass::EarPosition(); +} + //----------------------------------------------------------------------------- // Purpose: Return true if this NPC can hear the specified sound //----------------------------------------------------------------------------- @@ -1280,7 +1488,7 @@ bool CNPC_Wilson::DoCustomSpeechAI() } // If we're in a vehicle, use the vehicle's origin instead - Vector vecSearchOrigin = m_hAttachedVehicle ? m_hAttachedVehicle->GetAbsOrigin() : GetAbsOrigin(); + Vector vecSearchOrigin = m_hAttachedVehicle ? m_hAttachedVehicle->GetAbsOrigin() : GetAbsOriginForSpeech( pPlayer ); float flDistSqr = (vecSearchOrigin - pPlayer->GetAbsOrigin()).LengthSqr(); if (flDistSqr >= Square( 300 )) { @@ -1352,6 +1560,14 @@ bool CNPC_Wilson::DoIdleSpeechAI( AISpeechSelection_t *pSelection, int iState ) return true; } + // From SelectIdleSpeech() + // This was implemented for Axon Pariah, although it can be used with turret Wilson too. + if ( GetTimePlayerStaring() > 6 && GetSmoothedVelocity().IsZero() ) + { + if ( SelectSpeechResponse( TLK_STARE, NULL, pTarget, pSelection ) ) + return true; + } + // From SelectIdleSpeech() // Player allies normally reduce how much this is spoken while moving, // but Will-E is supposed to be moved, so that doesn't apply here. @@ -1713,7 +1929,7 @@ void CNPC_Wilson::ModifyOrAppendCriteria(AI_CriteriaSet& set) CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); if (pPlayer) { - set.AppendCriteria( "wilson_distance", CFmtStrN<32>( "%f.3", (GetAbsOrigin() - pPlayer->GetAbsOrigin()).Length() ) ); + set.AppendCriteria( "wilson_distance", CFmtStrN<32>( "%f.3", (GetAbsOriginForSpeech( pPlayer ) - pPlayer->GetAbsOrigin()).Length() ) ); if (pPlayer->IsInAVehicle()) { @@ -1747,6 +1963,19 @@ void CNPC_Wilson::ModifyOrAppendCriteria(AI_CriteriaSet& set) set.AppendCriteria( "wilson_distance", "99999999999" ); set.AppendCriteria( "player_in_vehicle", "0" ); } + + if (m_hCameraTargets.Count() > 0 && GetSpeechTarget()) + { + // Check which camera we're looking at our speech target through + CWilsonCamera *pCamera = GetCameraForTarget( GetSpeechTarget() ); + if (pCamera) + { + set.AppendCriteria( "camera", STRING(pCamera->GetEntityName()) ); + pCamera->AppendContextToCriteria( set, "camera_" ); + } + else + set.AppendCriteria( "camera", "0" ); + } } //----------------------------------------------------------------------------- @@ -1834,8 +2063,8 @@ bool CNPC_Wilson::IsOkToSpeak( ConceptCategory_t category, bool fRespondingToPla } else { - // If we're not responding to the player, don't talk if running a logic_choreo - if ( IsRunningScriptedSceneAndNotPaused( this, false ) ) + // If we're not responding to the player, don't talk if running a non-instanced logic_choreo OR an instanced logic_choreo with speech in it + if ( IsRunningScriptedSceneAndNotPaused( this ) || IsRunningScriptedSceneWithSpeechAndNotPaused( this, false ) ) { return false; } @@ -1890,6 +2119,258 @@ bool CNPC_Wilson::GetGameTextSpeechParams( hudtextparms_t ¶ms ) return true; } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Wilson::RefreshCameraTargets() +{ + m_hCameraTargets.RemoveAll(); + + if (m_iszCameraTargets == NULL_STRING) + return; + + CBaseEntity *pCamera = gEntList.FindEntityGeneric( NULL, STRING( m_iszCameraTargets ), this, this, this ); + while (pCamera) + { + if (!FClassnameIs( pCamera, "point_wilson_camera" )) + { + DevWarning( "%s: \"%s\" (%s) is not a point_wilson_camera\n", GetDebugName(), pCamera->GetDebugName(), pCamera->GetClassname() ); + continue; + } + + m_hCameraTargets.AddToTail( static_cast(pCamera) ); + + pCamera = gEntList.FindEntityGeneric( pCamera, STRING( m_iszCameraTargets ), this, this, this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const Vector &CNPC_Wilson::GetSpeechTargetSearchOrigin() +{ + if ( m_bOmniscient ) + { + // It doesn't matter where we are, so search from the player's origin + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if (pPlayer) + return pPlayer->GetAbsOrigin(); + } + + return BaseClass::GetSpeechTargetSearchOrigin(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const Vector &CNPC_Wilson::GetAbsOriginForSpeech( CBaseEntity *pSpeechTarget ) +{ + CWilsonCamera *pNearestCamera = GetCameraForTarget( pSpeechTarget ); + if (!pNearestCamera) + return BaseClass::GetAbsOriginForSpeech( pSpeechTarget ); + + return pNearestCamera->GetAbsOrigin(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const Vector &CNPC_Wilson::GetWorldSpaceCenterForSpeech( CBaseEntity *pSpeechTarget ) +{ + CWilsonCamera *pNearestCamera = GetCameraForTarget( pSpeechTarget ); + if (!pNearestCamera) + return BaseClass::GetWorldSpaceCenterForSpeech( pSpeechTarget ); + + return pNearestCamera->WorldSpaceCenter(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const Vector CNPC_Wilson::GetEyePositionForSpeech( CBaseEntity *pSpeechTarget ) +{ + CWilsonCamera *pNearestCamera = GetCameraForTarget( pSpeechTarget ); + if (!pNearestCamera) + return BaseClass::GetEyePositionForSpeech( pSpeechTarget ); + + return pNearestCamera->EyePosition(); +} + +#define MIN_LOOK_TARGET_DIST 1.0f +#define MAX_FULL_LOOK_TARGET_DIST 10.0f + +//----------------------------------------------------------------------------- +// Purpose: Returns true if target is in legal range of eye movement for the current head position +//----------------------------------------------------------------------------- +bool CNPC_Wilson::ValidEyeTarget(const Vector &lookTargetPos) +{ + if (!m_bOmniscient) + return BaseClass::ValidEyeTarget( lookTargetPos ); + + CWilsonCamera *pNearestCamera = GetCameraForTarget( lookTargetPos ); + if (!pNearestCamera) + return BaseClass::ValidEyeTarget( lookTargetPos ); + + // Just use head target code for now + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if target is in legal range of possible head movements +//----------------------------------------------------------------------------- +bool CNPC_Wilson::ValidHeadTarget(const Vector &lookTargetPos) +{ + if (!m_bOmniscient) + return BaseClass::ValidEyeTarget( lookTargetPos ); + + CWilsonCamera *pNearestCamera = GetCameraForTarget( lookTargetPos ); + if (!pNearestCamera) + return BaseClass::ValidEyeTarget( lookTargetPos ); + + Vector vFacing; + pNearestCamera->GetVectors( &vFacing, NULL, NULL ); + + Vector lookTargetDir = lookTargetPos - pNearestCamera->EyePosition(); + float flDist = VectorNormalize(lookTargetDir); + + if (flDist < MIN_LOOK_TARGET_DIST) + { + return false; + } + + // Only look if it doesn't crank my head too far + float dotPr = DotProduct(lookTargetDir, vFacing); + if (dotPr > 0 && fabs( lookTargetDir.z ) < 0.7) // +- 90 degrees side to side, +- 45 up/down + { + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns how much to try to look at the target +//----------------------------------------------------------------------------- +float CNPC_Wilson::HeadTargetValidity(const Vector &lookTargetPos) +{ + if (!m_bOmniscient) + return BaseClass::ValidEyeTarget( lookTargetPos ); + + CWilsonCamera *pNearestCamera = GetCameraForTarget( lookTargetPos ); + if (!pNearestCamera) + return BaseClass::ValidEyeTarget( lookTargetPos ); + + Vector vFacing; + pNearestCamera->GetVectors( &vFacing, NULL, NULL ); + + Vector lookTargetDir = lookTargetPos - pNearestCamera->EyePosition(); + float flDist = lookTargetDir.Length2D(); + VectorNormalize(lookTargetDir); + + if (flDist <= MIN_LOOK_TARGET_DIST) + { + return 0; + } + + // Only look if it doesn't crank my head too far + float dotPr = DotProduct(lookTargetDir, vFacing); + // only look if target is within +-135 degrees + // scale 1..-0.707 == 1..1, -.707..-1 == 1..0 + // X * b + b = 1 == 1 / (X + 1) = b, 3.4142 + float flInterest = clamp( 3.4142f + 3.4142f * dotPr, 0.f, 1.f ); + + // stop looking when point too close + if (flDist < MAX_FULL_LOOK_TARGET_DIST) + { + flInterest = flInterest * (flDist - MIN_LOOK_TARGET_DIST ) / (MAX_FULL_LOOK_TARGET_DIST - MIN_LOOK_TARGET_DIST); + } + + return flInterest; +} + +//----------------------------------------------------------------------------- +// Purpose: Overrides head target parameters +//----------------------------------------------------------------------------- +bool CNPC_Wilson::HeadTargetPosOverride( const Vector &vecTargetPos, Vector &vecDir, float &flDist ) +{ + if (!m_bOmniscient) + return BaseClass::HeadTargetPosOverride( vecTargetPos, vecDir, flDist ); + + CWilsonCamera *pNearestCamera = GetCameraForTarget( vecTargetPos ); + if (!pNearestCamera) + return BaseClass::HeadTargetPosOverride( vecTargetPos, vecDir, flDist ); + + // Translate this target position from the camera to Wilson's actual entity + matrix3x4_t matCameraToTarget, matTargetToCamera; + matrix3x4_t matWorldToTarget( + 1.0f, 0.0f, 0.0f, -vecTargetPos.x, + 0.0f, 1.0f, 0.0f, -vecTargetPos.y, + 0.0f, 0.0f, 1.0f, -vecTargetPos.z ); + ConcatTransforms( matWorldToTarget, pNearestCamera->EntityToWorldTransform(), matCameraToTarget ); + MatrixInvert( matCameraToTarget, matTargetToCamera ); + + matrix3x4_t matWilson; + GetAttachment( LookupAttachment( "forward" ), matWilson ); + + matrix3x4_t matTargetToWilson; + ConcatTransforms( matWilson, matTargetToCamera, matTargetToWilson ); + + Vector vecOrigin; + MatrixGetColumn( matTargetToWilson, 3, vecOrigin ); + + vecDir = vecOrigin - EyePosition(); + flDist = VectorNormalize( vecDir ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Overrides head target parameters +//----------------------------------------------------------------------------- +bool CNPC_Wilson::HeadAngleOverride( QAngle &vTargetAngles ) +{ + if (!m_bOmniscient) + return BaseClass::HeadAngleOverride( vTargetAngles ); + + vTargetAngles -= EyeAngles(); + + vTargetAngles.x *= npc_wilson_camera_look_scale_pitch.GetFloat(); + vTargetAngles.y *= npc_wilson_camera_look_scale_yaw.GetFloat(); + vTargetAngles.z *= npc_wilson_camera_look_scale_roll.GetFloat(); + + vTargetAngles += EyeAngles(); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CNPC_Wilson::PickTacticalLookTarget( AILookTargetArgs_t *pArgs ) +{ + if (BaseClass::PickTacticalLookTarget( pArgs )) + { + return true; + } + + if ( HasCondition( COND_SEE_PLAYER ) ) + { + // 1/3rd chance to look at player + if (random->RandomInt( 0, 2 ) == 0) + { + pArgs->flDuration = RandomFloat( 3.0f, 5.0f ); + if (GetState() == NPC_STATE_ALERT) + pArgs->flDuration *= 0.5f; + + pArgs->flInfluence = 0.5f; + pArgs->hTarget = AI_GetSinglePlayer(); + + return true; + } + } + + return false; +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -2484,3 +2965,78 @@ void CArbeitScanner::CleanupScan(bool dispatchInteraction) m_pSprite = NULL; } } + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +BEGIN_DATADESC( CWilsonCamera ) + + DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + DEFINE_KEYFIELD( m_flFieldOfView, FIELD_FLOAT, "FieldOfView" ), + DEFINE_KEYFIELD( m_flLookDistSqr, FIELD_FLOAT, "LookDist" ), + DEFINE_KEYFIELD( m_b3DViewCone, FIELD_BOOLEAN, "Use3DViewCone" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFOV", InputSetFOV ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetLookDist", InputSetLookDist ), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( point_wilson_camera, CWilsonCamera ); + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CWilsonCamera::CWilsonCamera( void ) +{ + m_bDisabled = false; + m_flFieldOfView = 90.0f; + m_flLookDistSqr = 1024.0f; + m_b3DViewCone = false; +} + +//------------------------------------------------------------------------------ +// Purpose : +// Input : +// Output : +//------------------------------------------------------------------------------ +void CWilsonCamera::Spawn( void ) +{ + BaseClass::Spawn(); + + // Make FOV and look dist ready for visibility calculations + m_flFieldOfView = cos( DEG2RAD(m_flFieldOfView/2) ); + m_flLookDistSqr *= m_flLookDistSqr; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWilsonCamera::FInViewCone( const Vector &vecSpot ) +{ + Vector los = ( vecSpot - EyePosition() ); + + if (los.LengthSqr() > m_flLookDistSqr) + return false; + + Vector facingDir; + GetVectors( &facingDir, NULL, NULL ); + + if (m_b3DViewCone) + { + // do this in 2D + los.z = 0; + VectorNormalize( los ); + + facingDir.z = 0; + facingDir.AsVector2D().NormalizeInPlace(); + } + + float flDot = DotProduct( los, facingDir ); + + if ( flDot > m_flFieldOfView ) + return true; + + return false; +} diff --git a/sp/src/game/server/ez2/npc_wilson.h b/sp/src/game/server/ez2/npc_wilson.h index cd876f33f07..52c4af183a5 100644 --- a/sp/src/game/server/ez2/npc_wilson.h +++ b/sp/src/game/server/ez2/npc_wilson.h @@ -28,6 +28,8 @@ // So now it's CAI_SensingDummy. typedef CAI_SensingDummy CAI_WilsonBase; +class CWilsonCamera; + //----------------------------------------------------------------------------- // Purpose: Wilson, Willie, Will-E // @@ -53,6 +55,8 @@ class CNPC_Wilson : public CAI_WilsonBase, public CDefaultPlayerPickupVPhysics, void Precache(); void Spawn(); void Activate( void ); + bool KeyValue( const char *szKeyName, const char *szValue ); + void NPCInit( void ); bool CreateVPhysics( void ); void UpdateOnRemove( void ); float MaxYawSpeed( void ){ return 0; } @@ -97,6 +101,14 @@ class CNPC_Wilson : public CAI_WilsonBase, public CDefaultPlayerPickupVPhysics, void AimGun(); + bool FInViewCone( CBaseEntity *pEntity ) { return BaseClass::FInViewCone( pEntity ); } + bool FInViewCone( const Vector &vecSpot ); + bool FVisible( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL ); + bool FVisible( const Vector &vecTarget, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL ); + CWilsonCamera* GetCameraForTarget( CBaseEntity *pTarget ); + CWilsonCamera* GetCameraForTarget( const Vector &vecTarget ); + + virtual Vector EarPosition( void ); // position of ears bool QueryHearSound( CSound *pSound ); // Wilson hardly cares about his NPC state because he's just a vessel for choreography and player attachment, not a useful combat ally. @@ -151,6 +163,21 @@ class CNPC_Wilson : public CAI_WilsonBase, public CDefaultPlayerPickupVPhysics, bool IsOmniscient() { return m_bOmniscient; } + void RefreshCameraTargets(); + const Vector& GetSpeechTargetSearchOrigin(); + const Vector& GetAbsOriginForSpeech( CBaseEntity *pSpeechTarget ); + const Vector& GetWorldSpaceCenterForSpeech( CBaseEntity *pSpeechTarget ); + const Vector GetEyePositionForSpeech( CBaseEntity *pSpeechTarget ); + + bool ValidEyeTarget( const Vector &lookTargetPos ); + bool ValidHeadTarget( const Vector &lookTargetPos ); + float HeadTargetValidity( const Vector &lookTargetPos ); + bool HeadTargetPosOverride( const Vector &vecTargetPos, Vector &vecDir, float &flDist ); + bool HeadAngleOverride( QAngle &vTargetAngles ); + float GetHeadDebounce( void ) { return 0.5f; } // how much of previous head turn to use + + bool PickTacticalLookTarget( AILookTargetArgs_t *pArgs ); + void InputEnableMotion( inputdata_t &inputdata ); void InputDisableMotion( inputdata_t &inputdata ); @@ -179,6 +206,15 @@ class CNPC_Wilson : public CAI_WilsonBase, public CDefaultPlayerPickupVPhysics, void InputTurnOnDeadMode( inputdata_t &inputdata ) { SetPlayingDead( true ); } void InputTurnOffDeadMode( inputdata_t &inputdata ) { SetPlayingDead( false ); } + void InputSetCameraTargets( inputdata_t &inputdata ) { m_iszCameraTargets = inputdata.value.StringID(); RefreshCameraTargets(); } + void InputClearCameraTargets( inputdata_t &inputdata ) { m_iszCameraTargets = NULL_STRING; RefreshCameraTargets(); } + + void InputEnableSeeThroughPlayer( inputdata_t &inputdata ) { m_bSeeThroughPlayer = true; } + void InputDisableSeeThroughPlayer( inputdata_t &inputdata ) { m_bSeeThroughPlayer = false; } + + void InputEnableOmnipresence( inputdata_t &inputdata ) { m_bOmniscient = true; } + void InputDisableOmnipresence( inputdata_t &inputdata ) { m_bOmniscient = false; } + protected: //----------------------------------------------------- @@ -244,8 +280,15 @@ class CNPC_Wilson : public CAI_WilsonBase, public CDefaultPlayerPickupVPhysics, // Makes Will-E always available as a speech target, even when out of regular range. // (e.g. Will-E on monitor in ez2_c3_3) + // + // MISNOMER: Actually means "omnipresent". This should be corrected next time we can break saves. bool m_bOmniscient; + string_t m_iszCameraTargets; + CUtlVector> m_hCameraTargets; // Refreshed every restore + + bool m_bSeeThroughPlayer; + // See CNPC_Wilson::CanBeAnEnemyOf(). bool m_bCanBeEnemy; @@ -356,3 +399,35 @@ class CArbeitScanner : public CBaseAnimating int m_iScanAttachment; }; + +//----------------------------------------------------------------------------- +// Purpose: Camera entity that Wilson can use to see outside of his body. +//----------------------------------------------------------------------------- +class CWilsonCamera : public CBaseEntity +{ + DECLARE_CLASS( CWilsonCamera, CBaseEntity ); + DECLARE_DATADESC(); +public: + CWilsonCamera(); + + void Spawn(); + bool FInViewCone( const Vector &vecSpot ); + + inline bool IsEnabled() const { return !m_bDisabled; } + inline float GetFOV() const { return m_flFieldOfView; } + inline float GetLookDistSqr() const { return m_flLookDistSqr; } + inline bool Using3DViewCone() const { return m_b3DViewCone; } + + void InputEnable( inputdata_t &inputdata ) { m_bDisabled = false; } + void InputDisable( inputdata_t &inputdata ) { m_bDisabled = true; } + + void InputSetFOV( inputdata_t &inputdata ) { m_flFieldOfView = cos( DEG2RAD( inputdata.value.Float() / 2 ) ); } + void InputSetLookDist( inputdata_t &inputdata ) { m_flLookDistSqr = Square( inputdata.value.Float() ); } + +private: + + bool m_bDisabled; + float m_flFieldOfView; + float m_flLookDistSqr; + bool m_b3DViewCone; +}; diff --git a/sp/src/game/server/ez2/weapon_oicw.cpp b/sp/src/game/server/ez2/weapon_oicw.cpp new file mode 100644 index 00000000000..d55f3355160 --- /dev/null +++ b/sp/src/game/server/ez2/weapon_oicw.cpp @@ -0,0 +1,690 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "basehlcombatweapon.h" +#include "npcevent.h" +#include "basecombatcharacter.h" +#include "ai_basenpc.h" +#include "player.h" +#include "game.h" +#include "in_buttons.h" +#include "grenade_ar2.h" +#include "ai_memory.h" +#include "soundent.h" +#include "rumble_shared.h" +#include "gamestats.h" +#include "hl2_player.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern ConVar sk_plr_dmg_smg1_grenade; +#ifdef MAPBASE +extern ConVar sk_npc_dmg_smg1_grenade; +#endif + +class CWeaponOICW : public CHLSelectFireMachineGun +{ + DECLARE_DATADESC(); +public: + DECLARE_CLASS( CWeaponOICW, CHLSelectFireMachineGun ); + + CWeaponOICW(); + + DECLARE_SERVERCLASS(); + + void Precache( void ); + void AddViewKick( void ); + void SecondaryAttack( void ); + int GetMinBurst() { return 2; } + int GetMaxBurst() { return 5; } + + bool Reload( void ); + void ItemPostFrame( void ); + + float GetFireRate( void ) { return m_bZoomed ? 0.15 : 0.1f; } + int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } + int WeaponRangeAttack2Condition( float flDot, float flDist ); + Activity GetPrimaryAttackActivity( void ); + bool SendWeaponAnim( int iActivity ); + void WeaponIdle( void ); + + virtual const Vector& GetBulletSpread( void ) + { + static const Vector cone = VECTOR_CONE_2DEGREES; + static const Vector zoomedCone = VECTOR_CONE_1DEGREES * 0.25f; + + if (GetOwner() && GetOwner()->IsNPC()) + { + static const Vector npcCone = VECTOR_CONE_3DEGREES; + return npcCone; + } + + return m_bZoomed ? zoomedCone : cone; + } + + bool IsDynamicScopeZoomed( void ) const { return m_bZoomed; } + + const WeaponProficiencyInfo_t *GetProficiencyValues(); + + void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ); + void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ); + void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); + + DECLARE_ACTTABLE(); + +protected: + + Vector m_vecTossVelocity; + float m_flNextGrenadeCheck; + CNetworkVar( bool, m_bZoomed ); + CNetworkVar( bool, m_bZoomTransition ); +}; + +IMPLEMENT_SERVERCLASS_ST(CWeaponOICW, DT_WeaponOICW) + SendPropBool( SENDINFO( m_bZoomed ) ), + SendPropBool( SENDINFO( m_bZoomTransition ) ), +END_SEND_TABLE() + +//LINK_ENTITY_TO_CLASS( weapon_ar1, CWeaponOICW ); +LINK_ENTITY_TO_CLASS( weapon_oicw, CWeaponOICW ); + +// Don't need to precache this in every game +//PRECACHE_WEAPON_REGISTER( weapon_oicw ); + +BEGIN_DATADESC( CWeaponOICW ) + + DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ), + DEFINE_FIELD( m_flNextGrenadeCheck, FIELD_TIME ), + DEFINE_FIELD( m_bZoomed, FIELD_BOOLEAN ), + +END_DATADESC() + +acttable_t CWeaponOICW::m_acttable[] = +{ + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_AR1, true }, + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + // Optional new NPC activities + // (these should fall back to AR2 animations when they don't exist on an NPC) + { ACT_RELOAD, ACT_RELOAD_AR1, true }, + { ACT_IDLE, ACT_IDLE_AR1, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_AR1, true }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_AR1_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_AR1_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_AR1, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_AR1_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_AR1_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_AR1, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_AR1_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_AR1_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_AR1, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_AR1_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_AR1_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_AR1, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_AR1_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_AR1_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_AR1, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_AR1_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_AR1_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_AR1, false },//always aims +//End readiness activities + + { ACT_WALK, ACT_WALK_AR1, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_AR1, true }, + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, + { ACT_RUN, ACT_RUN_AR1, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_AR1, true }, + { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true }, + { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_AR1, true }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_AR1_LOW, true }, + { ACT_COVER_LOW, ACT_COVER_AR1_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_AR1_LOW, false }, + { ACT_RELOAD_LOW, ACT_RELOAD_AR1_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_AR1, true }, + + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, + +#if EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_AR1_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_AR1_MED, false }, +#endif + +#if EXPANDED_HL2DM_ACTIVITIES + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_AR1, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_AR1, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_AR1, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_AR1, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_AR1, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_AR1, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_AR1, false }, + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_AR1, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR1, false }, +#endif +#endif +}; + +IMPLEMENT_ACTTABLE(CWeaponOICW); + +//========================================================= +CWeaponOICW::CWeaponOICW( ) +{ + m_fMinRange1 = 65; + m_fMaxRange1 = 2048; + + m_bAltFiresUnderwater = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponOICW::Precache( void ) +{ + UTIL_PrecacheOther("grenade_ar2"); + + BaseClass::Precache(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponOICW::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ) +{ + WeaponSoundRealtime( SINGLE_NPC ); + + CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_MACHINEGUN, 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() ); + + pOperator->FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, + MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2, entindex(), 0 ); + + pOperator->DoMuzzleFlash(); + m_iClip1 = m_iClip1 - 1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponOICW::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ) +{ + // Ensure we have enough rounds in the clip + m_iClip1++; + + Vector vecShootOrigin, vecShootDir; + QAngle angShootDir; + GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir ); + AngleVectors( angShootDir, &vecShootDir ); + FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir ); +} + +#ifdef MAPBASE +float GetCurrentGravity( void ); +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponOICW::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + switch( pEvent->event ) + { + case EVENT_WEAPON_SMG1: + case EVENT_WEAPON_AR1: + case EVENT_WEAPON_AR2: + { + Vector vecShootOrigin, vecShootDir; + QAngle angDiscard; + + // Support old style attachment point firing + if ((pEvent->options == NULL) || (pEvent->options[0] == '\0') || (!pOperator->GetAttachment(pEvent->options, vecShootOrigin, angDiscard))) + { + vecShootOrigin = pOperator->Weapon_ShootPosition(); + } + + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + ASSERT( npc != NULL ); + vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin ); + + FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir ); + } + break; + +#ifdef MAPBASE + case EVENT_WEAPON_AR2_ALTFIRE: + { + WeaponSound( WPN_DOUBLE ); + + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + if (!npc) + return; + + Vector vecShootOrigin, vecShootDir; + vecShootOrigin = pOperator->Weapon_ShootPosition(); + vecShootDir = npc->GetShootEnemyDir( vecShootOrigin ); + + Vector vecTarget = npc->GetAltFireTarget(); + Vector vecThrow; + if (vecTarget == vec3_origin) + AngleVectors( npc->EyeAngles(), &vecThrow ); // Not much else to do, unfortunately + else + { + // Because this is happening right now, we can't "VecCheckThrow" and can only "VecDoThrow", you know what I mean? + // ...Anyway, this borrows from that so we'll never return vec3_origin. + //vecThrow = VecCheckThrow( this, vecShootOrigin, vecTarget, 600.0, 0.5 ); + + vecThrow = (vecTarget - vecShootOrigin); + + // throw at a constant time + float time = vecThrow.Length() / 600.0; + vecThrow = vecThrow * (1.0 / time); + + // adjust upward toss to compensate for gravity loss + vecThrow.z += (GetCurrentGravity() * 0.5) * time * 0.5; + } + + CGrenadeAR2 *pGrenade = (CGrenadeAR2*)Create( "grenade_ar2", vecShootOrigin, vec3_angle, npc ); + pGrenade->SetAbsVelocity( vecThrow ); + pGrenade->SetLocalAngularVelocity( QAngle( 0, 400, 0 ) ); + pGrenade->SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE ); + + pGrenade->SetThrower( npc ); + + pGrenade->SetGravity(0.5); // lower gravity since grenade is aerodynamic and engine doesn't know it. + + pGrenade->SetDamage(sk_npc_dmg_smg1_grenade.GetFloat()); + + variant_t var; + var.SetEntity(pGrenade); + npc->FireNamedOutput("OnThrowGrenade", var, pGrenade, npc); + } + break; +#else + /*//FIXME: Re-enable + case EVENT_WEAPON_AR2_GRENADE: + { + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + + Vector vecShootOrigin, vecShootDir; + vecShootOrigin = pOperator->Weapon_ShootPosition(); + vecShootDir = npc->GetShootEnemyDir( vecShootOrigin ); + + Vector vecThrow = m_vecTossVelocity; + + CGrenadeAR2 *pGrenade = (CGrenadeAR2*)Create( "grenade_ar2", vecShootOrigin, vec3_angle, npc ); + pGrenade->SetAbsVelocity( vecThrow ); + pGrenade->SetLocalAngularVelocity( QAngle( 0, 400, 0 ) ); + pGrenade->SetMoveType( MOVETYPE_FLYGRAVITY ); + pGrenade->m_hOwner = npc; + pGrenade->m_pMyWeaponAR2 = this; + pGrenade->SetDamage(sk_npc_dmg_ar2_grenade.GetFloat()); + + // FIXME: arrgg ,this is hard coded into the weapon??? + m_flNextGrenadeCheck = gpGlobals->curtime + 6;// wait six seconds before even looking again to see if a grenade can be thrown. + + m_iClip2--; + } + break; + */ +#endif + + default: + BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Activity +//----------------------------------------------------------------------------- +Activity CWeaponOICW::GetPrimaryAttackActivity( void ) +{ + if ( m_bZoomed ) + return ACT_VM_PRIMARYATTACK_SPECIAL; + + if ( m_nShotsFired < 2 ) + return ACT_VM_PRIMARYATTACK; + + if ( m_nShotsFired < 3 ) + return ACT_VM_RECOIL1; + + if ( m_nShotsFired < 4 ) + return ACT_VM_RECOIL2; + + return ACT_VM_RECOIL3; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWeaponOICW::SendWeaponAnim( int iActivity ) +{ + // Finished transitioning to or from zoom + if ( m_bZoomTransition && iActivity == GetIdealActivity() ) + m_bZoomTransition = false; + + return BaseClass::SendWeaponAnim( iActivity ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponOICW::WeaponIdle( void ) +{ + if ( m_bZoomed ) + { + // Override base weapon idle when zoomed + if ( HasWeaponIdleTimeElapsed() ) + SendWeaponAnim( ACT_VM_IDLE_SPECIAL ); + + return; + } + + return BaseClass::WeaponIdle(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponOICW::ItemPostFrame( void ) +{ + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + if ( pOwner ) + { + CHL2_Player *pHL2Player = static_cast(GetOwner()); + if ( pHL2Player->IsZooming() ) + { + if (!m_bZoomed) + { + // Reset to new idle sequence + SetWeaponIdleTime( gpGlobals->curtime ); + + m_bZoomed = true; + m_bZoomTransition = true; + } + } + else + { + if (m_bZoomed) + { + // Reset to new idle sequence + SetWeaponIdleTime( gpGlobals->curtime ); + + m_bZoomed = false; + m_bZoomTransition = true; + } + } + } + + BaseClass::ItemPostFrame(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CWeaponOICW::Reload( void ) +{ + bool fRet; + float fCacheTime = m_flNextSecondaryAttack; + + fRet = DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD ); + if ( fRet ) + { + // Undo whatever the reload process has done to our secondary + // attack timer. We allow you to interrupt reloading to fire + // a grenade. + m_flNextSecondaryAttack = GetOwner()->m_flNextAttack = fCacheTime; + + WeaponSound( RELOAD ); + + if (m_bZoomed) + { + // Make the player stop zooming + CHL2_Player *pHL2Player = static_cast(GetOwner()); + if (pHL2Player->IsZooming()) + pHL2Player->StopZooming(); + } + } + + return fRet; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponOICW::AddViewKick( void ) +{ + #define EASY_DAMPEN 0.5f + #define MAX_VERTICAL_KICK 8.0f //Degrees + #define SLIDE_LIMIT 1.5f //Seconds + + #define ZOOMED_EASY_DAMPEN 0.5f + #define ZOOMED_MAX_VERTICAL_KICK 3.5f //Degrees + #define ZOOMED_SLIDE_LIMIT 8.0f //Seconds + + //Get the view kick + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + + if ( pPlayer == NULL ) + return; + + if ( m_bZoomed ) + { + DoMachineGunKick( pPlayer, ZOOMED_EASY_DAMPEN, ZOOMED_MAX_VERTICAL_KICK, m_fFireDuration, ZOOMED_SLIDE_LIMIT ); + } + else + { + DoMachineGunKick( pPlayer, EASY_DAMPEN, MAX_VERTICAL_KICK, m_fFireDuration, SLIDE_LIMIT ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponOICW::SecondaryAttack( void ) +{ + // Only the player fires this way so we can cast + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + + if ( pPlayer == NULL ) + return; + + //Must have ammo + if ( ( pPlayer->GetAmmoCount( m_iSecondaryAmmoType ) <= 0 ) || ( pPlayer->GetWaterLevel() == 3 ) ) + { + SendWeaponAnim( ACT_VM_DRYFIRE ); + BaseClass::WeaponSound( EMPTY ); + m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f; + return; + } + + if( m_bInReload ) + m_bInReload = false; + + // MUST call sound before removing a round from the clip of a CMachineGun + BaseClass::WeaponSound( WPN_DOUBLE ); + + pPlayer->RumbleEffect( RUMBLE_357, 0, RUMBLE_FLAGS_NONE ); + + Vector vecSrc = pPlayer->Weapon_ShootPosition(); + Vector vecThrow; + // Don't autoaim on grenade tosses + AngleVectors( pPlayer->EyeAngles() + pPlayer->GetPunchAngle(), &vecThrow ); + VectorScale( vecThrow, 1000.0f, vecThrow ); + + //Create the grenade + QAngle angles; + VectorAngles( vecThrow, angles ); + CGrenadeAR2 *pGrenade = (CGrenadeAR2*)Create( "grenade_ar2", vecSrc, angles, pPlayer ); + pGrenade->SetAbsVelocity( vecThrow ); + + pGrenade->SetLocalAngularVelocity( RandomAngle( -400, 400 ) ); + pGrenade->SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE ); + pGrenade->SetThrower( GetOwner() ); + pGrenade->SetDamage( sk_plr_dmg_smg1_grenade.GetFloat() ); + + SendWeaponAnim( ACT_VM_SECONDARYATTACK ); + + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 1000, 0.2, GetOwner(), SOUNDENT_CHANNEL_WEAPON ); + + // player "shoot" animation +#ifdef MAPBASE + pPlayer->SetAnimation( PLAYER_ATTACK2 ); +#else + pPlayer->SetAnimation( PLAYER_ATTACK1 ); +#endif + + // Decrease ammo + pPlayer->RemoveAmmo( 1, m_iSecondaryAmmoType ); + + // Can shoot again immediately + m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f; + + // Can blow up after a short delay (so have time to release mouse button) + m_flNextSecondaryAttack = gpGlobals->curtime + 1.0f; + + // Register a muzzleflash for the AI. + pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 ); + + m_iSecondaryAttacks++; + gamestats->Event_WeaponFired( pPlayer, false, GetClassname() ); +} + +#define COMBINE_MIN_GRENADE_CLEAR_DIST 256 + +//----------------------------------------------------------------------------- +// Purpose: +// Input : flDot - +// flDist - +// Output : int +//----------------------------------------------------------------------------- +int CWeaponOICW::WeaponRangeAttack2Condition( float flDot, float flDist ) +{ + CAI_BaseNPC *npcOwner = GetOwner()->MyNPCPointer(); + + return COND_NONE; + +/* + // -------------------------------------------------------- + // Assume things haven't changed too much since last time + // -------------------------------------------------------- + if (gpGlobals->curtime < m_flNextGrenadeCheck ) + return m_lastGrenadeCondition; +*/ + + // ----------------------- + // If moving, don't check. + // ----------------------- + if ( npcOwner->IsMoving()) + return COND_NONE; + + CBaseEntity *pEnemy = npcOwner->GetEnemy(); + + if (!pEnemy) + return COND_NONE; + + Vector vecEnemyLKP = npcOwner->GetEnemyLKP(); + if ( !( pEnemy->GetFlags() & FL_ONGROUND ) && pEnemy->GetWaterLevel() == 0 && vecEnemyLKP.z > (GetAbsOrigin().z + WorldAlignMaxs().z) ) + { + //!!!BUGBUG - we should make this check movetype and make sure it isn't FLY? Players who jump a lot are unlikely to + // be grenaded. + // don't throw grenades at anything that isn't on the ground! + return COND_NONE; + } + + // -------------------------------------- + // Get target vector + // -------------------------------------- + Vector vecTarget; + if (random->RandomInt(0,1)) + { + // magically know where they are + vecTarget = pEnemy->WorldSpaceCenter(); + } + else + { + // toss it to where you last saw them + vecTarget = vecEnemyLKP; + } + // vecTarget = m_vecEnemyLKP + (pEnemy->BodyTarget( GetLocalOrigin() ) - pEnemy->GetLocalOrigin()); + // estimate position + // vecTarget = vecTarget + pEnemy->m_vecVelocity * 2; + + + if ( ( vecTarget - npcOwner->GetLocalOrigin() ).Length2D() <= COMBINE_MIN_GRENADE_CLEAR_DIST ) + { + // crap, I don't want to blow myself up + m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second. + return (COND_NONE); + } + + // --------------------------------------------------------------------- + // Are any friendlies near the intended grenade impact area? + // --------------------------------------------------------------------- + CBaseEntity *pTarget = NULL; + + while ( ( pTarget = gEntList.FindEntityInSphere( pTarget, vecTarget, COMBINE_MIN_GRENADE_CLEAR_DIST ) ) != NULL ) + { + //Check to see if the default relationship is hatred, and if so intensify that + if ( npcOwner->IRelationType( pTarget ) == D_LI ) + { + // crap, I might blow my own guy up. Don't throw a grenade and don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second. + return (COND_WEAPON_BLOCKED_BY_FRIEND); + } + } + + // --------------------------------------------------------------------- + // Check that throw is legal and clear + // --------------------------------------------------------------------- + // FIXME: speed is based on difficulty... + + Vector vecToss = VecCheckThrow( this, npcOwner->GetLocalOrigin() + Vector(0,0,60), vecTarget, 600.0, 0.5 ); + if ( vecToss != vec3_origin ) + { + m_vecTossVelocity = vecToss; + + // don't check again for a while. + // JAY: HL1 keeps checking - test? + //m_flNextGrenadeCheck = gpGlobals->curtime; + m_flNextGrenadeCheck = gpGlobals->curtime + 0.3; // 1/3 second. + return COND_CAN_RANGE_ATTACK2; + } + else + { + // don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second. + return COND_WEAPON_SIGHT_OCCLUDED; + } +} + +//----------------------------------------------------------------------------- +const WeaponProficiencyInfo_t *CWeaponOICW::GetProficiencyValues() +{ + static WeaponProficiencyInfo_t proficiencyTable[] = + { + { 7.0, 0.75 }, + { 5.00, 0.75 }, + { 10.0/3.0, 0.85 }, + { 5.0/3.0, 0.75 }, + { 1.00, 1.0 }, + }; + + COMPILE_TIME_ASSERT( ARRAYSIZE(proficiencyTable) == WEAPON_PROFICIENCY_PERFECT + 1); + + return proficiencyTable; +} diff --git a/sp/src/game/server/filters.cpp b/sp/src/game/server/filters.cpp index e95ba310a31..ca2f6c7af4a 100644 --- a/sp/src/game/server/filters.cpp +++ b/sp/src/game/server/filters.cpp @@ -996,6 +996,9 @@ class CFilterModel : public CBaseFilter bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) { + if (!pEntity) + return false; + if (FStrEq(STRING(m_strFilterSkin), "-1") /*m_strFilterSkin == NULL_STRING|| FStrEq(STRING(m_strFilterSkin), "")*/) return Matcher_NamesMatch(STRING(m_iFilterModel), STRING(pEntity->GetModelName())); else if (pEntity->GetBaseAnimating()) @@ -1011,6 +1014,17 @@ class CFilterModel : public CBaseFilter inputdata.value.Convert(FIELD_STRING); m_iFilterModel = inputdata.value.StringID(); } + + bool KeyValue( const char *szKeyName, const char *szValue ) + { + if (FStrEq( szKeyName, "filtername" )) + { + m_iFilterModel = AllocPooledString( szValue ); + return true; + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + } }; LINK_ENTITY_TO_CLASS( filter_activator_model, CFilterModel ); @@ -1019,7 +1033,6 @@ BEGIN_DATADESC( CFilterModel ) // Keyfields DEFINE_KEYFIELD( m_iFilterModel, FIELD_STRING, "filtermodel" ), - DEFINE_KEYFIELD( m_iFilterModel, FIELD_STRING, "filtername" ), DEFINE_KEYFIELD( m_strFilterSkin, FIELD_STRING, "skin" ), END_DATADESC() diff --git a/sp/src/game/server/hl2/env_headcrabcanister.cpp b/sp/src/game/server/hl2/env_headcrabcanister.cpp index e94fe3f15f2..4cf7fffbdd3 100644 --- a/sp/src/game/server/hl2/env_headcrabcanister.cpp +++ b/sp/src/game/server/hl2/env_headcrabcanister.cpp @@ -33,6 +33,9 @@ ConVar sk_env_headcrabcanister_shake_radius( "sk_env_headcrabcanister_shake_radi ConVar sk_env_headcrabcanister_shake_radius_vehicle( "sk_env_headcrabcanister_shake_radius_vehicle", "2500" ); #define ENV_HEADCRABCANISTER_TRAIL_TIME 3.0f +#ifdef MAPBASE +#define RANDOM_CRAB_TYPE -1 +#endif //----------------------------------------------------------------------------- // Spawn flags @@ -258,7 +261,22 @@ void CEnvHeadcrabCanister::Precache( void ) PrecacheScriptSound( "HeadcrabCanister.SkyboxExplosion" ); PrecacheScriptSound( "HeadcrabCanister.Open" ); +#ifdef MAPBASE + if ( m_nHeadcrabType != RANDOM_CRAB_TYPE ) + { + UTIL_PrecacheOther( s_pHeadcrabClass[m_nHeadcrabType] ); + } + else + { + // precache all the headcrabs if we're spawning random species + for ( int i = 0; i < ARRAYSIZE( s_pHeadcrabClass ); i++ ) + { + UTIL_PrecacheOther( s_pHeadcrabClass[i] ); + } + } +#else UTIL_PrecacheOther( s_pHeadcrabClass[m_nHeadcrabType] ); +#endif } @@ -733,7 +751,17 @@ void CEnvHeadcrabCanister::HeadcrabCanisterSpawnHeadcrabThink() int nHeadCrabAttachment = LookupAttachment( "headcrab" ); if ( GetAttachment( nHeadCrabAttachment, vecSpawnPosition, vecSpawnAngles ) ) { +#ifdef MAPBASE + int iHeadcrabType = m_nHeadcrabType; + if ( m_nHeadcrabType == RANDOM_CRAB_TYPE ) + { + iHeadcrabType = RandomInt( 0, ARRAYSIZE( s_pHeadcrabClass ) - 1 ); + } + + CBaseEntity *pEnt = CreateEntityByName( s_pHeadcrabClass[iHeadcrabType] ); +#else CBaseEntity *pEnt = CreateEntityByName( s_pHeadcrabClass[m_nHeadcrabType] ); +#endif CBaseHeadcrab *pHeadCrab = assert_cast(pEnt); // Necessary to get it to eject properly (don't allow the NPC diff --git a/sp/src/game/server/hl2/hl2_player.cpp b/sp/src/game/server/hl2/hl2_player.cpp index 8e41f21fab2..f030a54c347 100644 --- a/sp/src/game/server/hl2/hl2_player.cpp +++ b/sp/src/game/server/hl2/hl2_player.cpp @@ -147,7 +147,15 @@ ConVar autoaim_unlock_target( "autoaim_unlock_target", "0.8666" ); ConVar sv_stickysprint("sv_stickysprint", "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX); #ifdef EZ2 +enum +{ + INFINITE_SPRINT_OFF, + INFINITE_SPRINT_DEFAULT, // Aux power itself is infinite + INFINITE_SPRINT_TRUE, // Only sprinting is affected, other suit devices continue to drain power +}; + ConVar sv_infinite_sprint_power( "sv_infinite_sprint_power", "1", FCVAR_CHEAT ); + ConVar sv_infinite_flashlight_power( "sv_infinite_flashlight_power", "0", FCVAR_CHEAT ); ConVar sv_player_death_smell( "sv_player_death_smell", "1", FCVAR_REPLICATED ); ConVar sv_player_kick_attack_enabled( "sv_player_kick_attack_enabled", "1", FCVAR_REPLICATED ); @@ -806,18 +814,18 @@ void CHL2_Player::Precache( void ) // Interactions if ( g_interactionBadCopKick == 0 ) { - g_interactionBadCopKick = CBaseCombatCharacter::GetInteractionID(); + CBaseCombatCharacter::AddInteractionWithString( g_interactionBadCopKick, "g_interactionBadCopKick" ); } if ( g_interactionBadCopKickWarn == 0 ) { - g_interactionBadCopKickWarn = CBaseCombatCharacter::GetInteractionID(); + CBaseCombatCharacter::AddInteractionWithString( g_interactionBadCopKickWarn, "g_interactionBadCopKickWarn" ); } // Interactions if (g_interactionBadCopOrderSurrender == 0) { - g_interactionBadCopOrderSurrender = CBaseCombatCharacter::GetInteractionID(); + CBaseCombatCharacter::AddInteractionWithString( g_interactionBadCopOrderSurrender, "g_interactionBadCopOrderSurrender" ); } #endif @@ -1760,7 +1768,12 @@ void CHL2_Player::StartAutoSprint() //----------------------------------------------------------------------------- void CHL2_Player::StartSprinting( void ) { +#ifdef EZ2 + // Ignore when infinite sprint is active + if( sv_infinite_sprint_power.GetInt() != INFINITE_SPRINT_TRUE && m_HL2Local.m_flSuitPower < 10 ) +#else if( m_HL2Local.m_flSuitPower < 10 ) +#endif { // Don't sprint unless there's a reasonable // amount of suit power. @@ -1775,7 +1788,12 @@ void CHL2_Player::StartSprinting( void ) return; } +#ifdef EZ2 + // "True" infinite sprinting: Aux power continues to drain, but sprinting device is not added + if( sv_infinite_sprint_power.GetInt() != INFINITE_SPRINT_TRUE && !SuitPower_AddDevice( SuitDeviceSprint ) ) +#else if( !SuitPower_AddDevice( SuitDeviceSprint ) ) +#endif return; CPASAttenuationFilter filter( this ); @@ -1881,6 +1899,14 @@ void CHL2_Player::StartZooming( void ) { #ifdef MAPBASE int iFOV = GetPlayerProxy() ? GetPlayerProxy()->m_SuitZoomFOV : 25; + +#ifdef EZ2 + if (GetActiveWeapon() && GetActiveWeapon()->GetDynamicScopeSuitFOV() != 0.0f) + { + // The weapon has its own suit zoom FOV + iFOV = GetActiveWeapon()->GetDynamicScopeSuitFOV(); + } +#endif #else int iFOV = 25; #endif @@ -2659,7 +2685,7 @@ bool CHL2_Player::SuitPower_Drain( float flPower ) #ifdef EZ2 // Sprint cheat on? // Why is this separate from sv_infinite_aux_power? So that we can toggle sprint and flashlight separately. - if (sv_infinite_sprint_power.GetBool()) + if (sv_infinite_sprint_power.GetInt() == INFINITE_SPRINT_DEFAULT) return true; #endif @@ -3920,6 +3946,20 @@ void CHL2_Player::UpdateWeaponPosture( void ) { m_LowerWeaponTimer.Set( .3 ); VPROF( "CHL2_Player::UpdateWeaponPosture-CheckLower" ); + +#ifdef MAPBASE + if (m_nButtons & IN_VGUIMODE) + { + //We're over a friendly, drop our weapon + if (Weapon_Lower() == false) + { + //FIXME: We couldn't lower our weapon! + } + + return; + } +#endif // MAPBASE + Vector vecAim = BaseClass::GetAutoaimVector( AUTOAIM_SCALE_DIRECT_ONLY ); const float CHECK_FRIENDLY_RANGE = 50 * 12; diff --git a/sp/src/game/server/hl2/npc_BaseZombie.cpp b/sp/src/game/server/hl2/npc_BaseZombie.cpp index 40ebfb4ace9..9386867d75f 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.cpp +++ b/sp/src/game/server/hl2/npc_BaseZombie.cpp @@ -216,6 +216,9 @@ BEGIN_DATADESC( CNPC_BaseZombie ) DEFINE_FIELD( m_fIsTorso, FIELD_BOOLEAN ), #ifdef MAPBASE DEFINE_KEYFIELD( m_fIsHeadless, FIELD_BOOLEAN, "Headless" ), + DEFINE_KEYFIELD( m_flMeleeReach, FIELD_FLOAT, "MeleeReach" ), + DEFINE_KEYFIELD( m_flMaxDistToSwat, FIELD_FLOAT, "MaxDistToSwat" ), + DEFINE_KEYFIELD( m_iMaxObjMassToSwat, FIELD_INTEGER, "MaxObjMassToSwat" ), #else DEFINE_FIELD( m_fIsHeadless, FIELD_BOOLEAN ), #endif @@ -265,6 +268,12 @@ CNPC_BaseZombie::CNPC_BaseZombie() // moan loop. m_iMoanSound = g_numZombies; +#ifdef MAPBASE + m_flMeleeReach = ZOMBIE_MELEE_REACH; + m_flMaxDistToSwat = ZOMBIE_PLAYER_MAX_SWAT_DIST; + m_iMaxObjMassToSwat = ZOMBIE_MAX_PHYSOBJ_MASS; +#endif + g_numZombies++; } @@ -304,7 +313,11 @@ bool CNPC_BaseZombie::FindNearestPhysicsObject( int iMaxMass ) float dist = VectorNormalize(vecDirToEnemy); vecDirToEnemy.z = 0; - if( dist > ZOMBIE_PLAYER_MAX_SWAT_DIST ) +#ifndef MAPBASE + if (dist > ZOMBIE_PLAYER_MAX_SWAT_DIST) +#else + if (dist > m_flMaxDistToSwat) +#endif { // Player is too far away. Don't bother // trying to swat anything at them until @@ -915,9 +928,9 @@ HeadcrabRelease_t CNPC_BaseZombie::ShouldReleaseHeadcrab( const CTakeDamageInfo } #endif #ifdef MAPBASE - if ( m_iHealth <= 0 && !m_fIsHeadless ) + if ( m_iHealth <= 0 && !m_fIsHeadless && !HasSpawnFlags(SF_ZOMBIE_NO_HEADCRAB_SPAWN)) #else - if ( m_iHealth <= 0 ) + if ( m_iHealth <= 0 ) #endif { if ( info.GetDamageType() & DMG_REMOVENORAGDOLL ) @@ -2370,7 +2383,11 @@ void CNPC_BaseZombie::GatherConditions( void ) // between him and the object he's heading for already. if( gpGlobals->curtime >= m_flNextSwatScan && (m_hPhysicsEnt == NULL) ) { +#ifdef MAPBASE + FindNearestPhysicsObject(m_iMaxObjMassToSwat); +#else FindNearestPhysicsObject( ZOMBIE_MAX_PHYSOBJ_MASS ); +#endif m_flNextSwatScan = gpGlobals->curtime + 2.0; } } diff --git a/sp/src/game/server/hl2/npc_BaseZombie.h b/sp/src/game/server/hl2/npc_BaseZombie.h index 68bb06363b7..0d1590c1859 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.h +++ b/sp/src/game/server/hl2/npc_BaseZombie.h @@ -44,7 +44,8 @@ extern int AE_ZOMBIE_POUND; #define ZOMBIE_BLOOD_BITE 3 #ifdef MAPBASE -#define SF_ZOMBIE_NO_TORSO ( 1 << 15 ) + #define SF_ZOMBIE_NO_TORSO ( 1 << 15 ) + #define SF_ZOMBIE_NO_HEADCRAB_SPAWN ( 1 << 16 ) #endif @@ -149,7 +150,14 @@ abstract_class CNPC_BaseZombie : public CAI_BaseZombieBase } int MeleeAttack1Conditions ( float flDot, float flDist ); - virtual float GetClawAttackRange() const { return ZOMBIE_MELEE_REACH; } + virtual float GetClawAttackRange() const + { +#ifdef MAPBASE + return m_flMeleeReach; +#else + return ZOMBIE_MELEE_REACH; +#endif + } #ifdef EZ // Zombie scream attack @@ -291,6 +299,12 @@ abstract_class CNPC_BaseZombie : public CAI_BaseZombieBase float m_flNextFlinch; +#ifdef MAPBASE + float m_flMeleeReach; + float m_flMaxDistToSwat; + int m_iMaxObjMassToSwat; +#endif + bool m_bHeadShot; // Used to determine the survival of our crab beyond our death. // diff --git a/sp/src/game/server/hl2/npc_antlion.cpp b/sp/src/game/server/hl2/npc_antlion.cpp index 80b6fff7467..d634e148c43 100644 --- a/sp/src/game/server/hl2/npc_antlion.cpp +++ b/sp/src/game/server/hl2/npc_antlion.cpp @@ -414,15 +414,18 @@ void CNPC_Antlion::Spawn( void ) sInteraction01.vecRelativeOrigin = Vector(224, 0, 0); sInteraction01.angRelativeAngles = QAngle(0, 180, 0); - //sInteraction01.iFlags |= SCNPC_FLAG_TEST_OTHER_ANGLES; + sInteraction01.iFlags |= SCNPC_FLAG_TEST_OTHER_ANGLES; sInteraction01.iFlags |= SCNPC_FLAG_TEST_END_POSITION; sInteraction01.vecRelativeEndPos = Vector(312, -10, 0); sInteraction01.iTriggerMethod = SNPCINT_AUTOMATIC_IN_COMBAT; sInteraction01.flDelay = 15.0f; sInteraction01.iFlags |= SCNPC_FLAG_MAPBASE_ADDITION; sInteraction01.flDistSqr = (8 * 8); + sInteraction01.flMaxAngleDiff = 180.0f; // Initiate from any angle #ifdef EZ sInteraction01.iFlags |= SCNPC_FLAG_TEST_SQUADMATE_HEALTH; + sInteraction01.flHealthRatio = 0.25f; + sInteraction01.MiscCriteria = AllocPooledString( "their_health:<100" ); #endif ScriptedNPCInteraction_t sInteraction02; @@ -431,13 +434,16 @@ void CNPC_Antlion::Spawn( void ) sInteraction02.vecRelativeOrigin = Vector(64, 0, 0); sInteraction02.angRelativeAngles = QAngle(0, 180, 0); - //sInteraction01.iFlags |= SCNPC_FLAG_TEST_OTHER_ANGLES; + sInteraction02.iFlags |= SCNPC_FLAG_TEST_OTHER_ANGLES; sInteraction02.iTriggerMethod = SNPCINT_AUTOMATIC_IN_COMBAT; sInteraction02.flDelay = 7.5f; sInteraction02.iFlags |= SCNPC_FLAG_MAPBASE_ADDITION; sInteraction02.flDistSqr = (8 * 8); + sInteraction02.flMaxAngleDiff = 180.0f; // Initiate from any angle #ifdef EZ sInteraction02.iFlags |= SCNPC_FLAG_TEST_SQUADMATE_HEALTH; + sInteraction02.flHealthRatio = 0.25f; + sInteraction02.MiscCriteria = AllocPooledString( "their_health:<100" ); #endif AddScriptedNPCInteraction(&sInteraction01); AddScriptedNPCInteraction(&sInteraction02); diff --git a/sp/src/game/server/hl2/npc_citizen17.cpp b/sp/src/game/server/hl2/npc_citizen17.cpp index 4c0e0a2af2c..4aa50c9e2cc 100644 --- a/sp/src/game/server/hl2/npc_citizen17.cpp +++ b/sp/src/game/server/hl2/npc_citizen17.cpp @@ -136,6 +136,7 @@ ConVar ai_min_suppression_distance("ai_min_suppression_distance", "256", FCVAR_R ConVar ai_suppression_distance_ratio("ai_suppression_distance_ratio", "0.5", FCVAR_REPLICATED); // 1upD - What percent of distance to suppression target must be covered ConVar ai_willpower_translate_schedules("ai_willpower_translate_schedules", "1", FCVAR_REPLICATED); // 1upD - Should new EZ2 schedules be used? ConVar ai_suppression_shoot_props( "ai_suppression_shoot_props", "1", FCVAR_REPLICATED ); // 1upD - Should rebels with suppressing fire shoot at props? +ConVar ai_suppression_use_enemy_memory( "ai_suppression_use_enemy_memory", "0", FCVAR_REPLICATED ); // Blixibon - Allows suppression AI to respect enemy memory #ifdef EZ1 // Disable this in EZ1 ConVar npc_citizen_gib( "npc_citizen_gib", "0" ); @@ -2566,6 +2567,23 @@ int CNPC_Citizen::TranslateSuppressingFireSchedule(int scheduleType) return scheduleType; } + if (ai_suppression_use_enemy_memory.GetBool() && GetSquad() && GetSquad()->NumMembers() > 1) + { + // If the enemy has eluded any of our squadmates, then someone must've peeked ahead to establish LOF and found no one there + // This means they're not at the position we think they're in + AISquadIter_t iter; + for ( CAI_BaseNPC *pSquadmate = m_pSquad ? m_pSquad->GetFirstMember(&iter) : this; pSquadmate; pSquadmate = m_pSquad ? m_pSquad->GetNextMember(&iter) : NULL ) + { + AI_EnemyInfo_t *pMemory = pSquadmate->GetEnemies()->Find( pEnemy ); + if( pMemory && pMemory->bEludedMe ) + { + if(ai_debug_rebel_suppressing_fire.GetBool()) + DevMsg("NPC_Citizen::TranslateSuppressingFireSchedule: %s not using suppressive fire due to enemy eluded by squad\n", GetDebugName()); + return scheduleType; + } + } + } + #ifdef EZ1 if(m_flLastAttackTime == 0) { @@ -2596,6 +2614,17 @@ int CNPC_Citizen::SelectRangeAttack2Schedule() return BaseClass::SelectRangeAttack2Schedule(); } +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CNPC_Citizen::OnStartSchedule( int schedule ) +{ + // Stop aiming our gun at hints if we're investigating a sound + if ( schedule == SCHED_INVESTIGATE_SOUND ) + StopAiming( "Investigating sound" ); + + BaseClass::OnStartSchedule( schedule ); +} + #define CITIZEN_DECOY_RADIUS 128.0f #define CITIZEN_NUM_DECOYS 10 #define CITIZEN_DECOY_MAX_MASS 200.0f @@ -2613,7 +2642,13 @@ bool CNPC_Citizen::FindDecoyObject(void) CBaseEntity *pCurrent; int count; int i; - Vector vecTarget = GetEnemy()->WorldSpaceCenter(); + + Vector vecTarget; + if (ai_suppression_use_enemy_memory.GetBool()) + vecTarget = GetEnemyLKP() + (GetEnemy()->WorldSpaceCenter() - GetEnemy()->GetAbsOrigin()); // Worldspace center relative to LKP + else + vecTarget = GetEnemy()->WorldSpaceCenter(); + Vector vecDelta; for (i = 0; i < CITIZEN_NUM_DECOYS; i++) @@ -2754,7 +2789,12 @@ bool CNPC_Citizen::FindEnemyCoverTarget(void) trace_t tr; Vector startpos = this->Weapon_ShootPosition(); - Vector targetpos = GetEnemy()->EyePosition(); + + Vector targetpos; + if (ai_suppression_use_enemy_memory.GetBool()) + targetpos = GetEnemyLKP() + GetEnemy()->GetViewOffset(); // Eye position relative to LKP + else + targetpos = GetEnemy()->EyePosition(); float distance = UTIL_DistApprox(startpos, targetpos); if (ai_debug_rebel_suppressing_fire.GetBool() && distance <= ai_min_suppression_distance.GetFloat()) diff --git a/sp/src/game/server/hl2/npc_citizen17.h b/sp/src/game/server/hl2/npc_citizen17.h index 8080e633141..5c511822935 100644 --- a/sp/src/game/server/hl2/npc_citizen17.h +++ b/sp/src/game/server/hl2/npc_citizen17.h @@ -232,6 +232,7 @@ class CNPC_Citizen : public CNPC_PlayerCompanion int TranslateWillpowerSchedule(int scheduleType); int TranslateSuppressingFireSchedule(int scheduleType); int SelectRangeAttack2Schedule(); + void OnStartSchedule( int scheduleType ); bool FindDecoyObject(void); bool FindEnemyCoverTarget(void); void AimGun(); diff --git a/sp/src/game/server/hl2/npc_combine.cpp b/sp/src/game/server/hl2/npc_combine.cpp index c6e9ed96f87..2216ec1fecd 100644 --- a/sp/src/game/server/hl2/npc_combine.cpp +++ b/sp/src/game/server/hl2/npc_combine.cpp @@ -5025,13 +5025,22 @@ void CNPC_Combine::RegenSound() // Input : // Output : //----------------------------------------------------------------------------- +#ifdef MAPBASE +void CNPC_Combine::LostEnemySound( CBaseEntity *pEnemy ) +#else void CNPC_Combine::LostEnemySound( void) +#endif { if ( gpGlobals->curtime <= m_flNextLostSoundTime ) return; #ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM - if (SpeakIfAllowed( TLK_CMB_LOSTENEMY, UTIL_VarArgs("lastseenenemy:%d", GetEnemyLastTimeSeen()) )) + AI_CriteriaSet modifiers; + ModifyOrAppendEnemyCriteria( modifiers, pEnemy ); + + modifiers.AppendCriteria( "lastseenenemy", gpGlobals->curtime - GetEnemies()->LastTimeSeen( pEnemy ) ); + + if (SpeakIfAllowed( TLK_CMB_LOSTENEMY, modifiers )) { m_flNextLostSoundTime = gpGlobals->curtime + random->RandomFloat(5.0,15.0); } @@ -5059,10 +5068,17 @@ void CNPC_Combine::LostEnemySound( void) // Input : // Output : //----------------------------------------------------------------------------- +#ifdef MAPBASE +void CNPC_Combine::FoundEnemySound( CBaseEntity *pEnemy ) +#else void CNPC_Combine::FoundEnemySound( void) +#endif { #ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM - SpeakIfAllowed( TLK_CMB_REFINDENEMY, SENTENCE_PRIORITY_HIGH ); + AI_CriteriaSet modifiers; + ModifyOrAppendEnemyCriteria( modifiers, pEnemy ); + + SpeakIfAllowed( TLK_CMB_REFINDENEMY, modifiers, SENTENCE_PRIORITY_HIGH ); #else m_Sentences.Speak( "COMBINE_REFIND_ENEMY", SENTENCE_PRIORITY_HIGH ); #endif @@ -5875,10 +5891,10 @@ void CNPC_Combine::OnEndMoveAndShoot() //----------------------------------------------------------------------------- bool CNPC_Combine::PickTacticalLookTarget( AILookTargetArgs_t *pArgs ) { - if( GetState() == NPC_STATE_COMBAT ) + if ( HasCondition( COND_SEE_ENEMY ) ) { CBaseEntity *pEnemy = GetEnemy(); - if ( pEnemy && FVisible( pEnemy ) && ValidHeadTarget(pEnemy->EyePosition()) ) + if ( pEnemy && ValidHeadTarget( pEnemy->EyePosition() ) ) { // Look at the enemy if possible. pArgs->hTarget = pEnemy; @@ -5887,8 +5903,12 @@ bool CNPC_Combine::PickTacticalLookTarget( AILookTargetArgs_t *pArgs ) } else { - // Look at yourself instead. We can't be looking in random directions. - pArgs->hTarget = this; + // Look ahead instead. We can't be looking in random directions. + Vector vecForward; + GetVectors( &vecForward, NULL, NULL ); + + pArgs->vTarget = EyePosition() + (vecForward * 16.0f); + pArgs->hTarget = NULL; pArgs->flInfluence = random->RandomFloat( 0.8, 1.0 ); pArgs->flRamp = 0; } diff --git a/sp/src/game/server/hl2/npc_combine.h b/sp/src/game/server/hl2/npc_combine.h index 35fd1bfc9d2..701f5395ea7 100644 --- a/sp/src/game/server/hl2/npc_combine.h +++ b/sp/src/game/server/hl2/npc_combine.h @@ -320,8 +320,13 @@ class CNPC_Combine : public CAI_BaseActor #endif void IdleSound( void ); void AlertSound( void ); +#ifdef MAPBASE + void LostEnemySound( CBaseEntity *pEnemy ); + void FoundEnemySound( CBaseEntity *pEnemy ); +#else void LostEnemySound( void ); void FoundEnemySound( void ); +#endif void AnnounceAssault( void ); void AnnounceEnemyType( CBaseEntity *pEnemy ); void AnnounceEnemyKill( CBaseEntity *pEnemy ); diff --git a/sp/src/game/server/hl2/npc_combinedropship.cpp b/sp/src/game/server/hl2/npc_combinedropship.cpp index 75adafa7193..19f62e2ca8c 100644 --- a/sp/src/game/server/hl2/npc_combinedropship.cpp +++ b/sp/src/game/server/hl2/npc_combinedropship.cpp @@ -180,6 +180,11 @@ class CCombineDropshipContainer : public CPhysicsProp bool AllowsAnyDamage( const CTakeDamageInfo &info ); #endif +#ifdef EZ + // Don't displace dropship containers + virtual bool IsDisplacementImpossible() { return true; } +#endif + private: enum { diff --git a/sp/src/game/server/hl2/npc_headcrab.cpp b/sp/src/game/server/hl2/npc_headcrab.cpp index 3ed08f56332..8e67f97a26f 100644 --- a/sp/src/game/server/hl2/npc_headcrab.cpp +++ b/sp/src/game/server/hl2/npc_headcrab.cpp @@ -1881,9 +1881,11 @@ void CBaseHeadcrab::Event_Killed( const CTakeDamageInfo &info ) #ifdef EZ // Create a little decal underneath the headcrab if ( - sk_headcrab_carcass_smell.GetBool() || + (sk_headcrab_carcass_smell.GetBool() || // This type of damage combination happens from dynamic scripted sequences - info.GetDamageType() & (DMG_GENERIC | DMG_PREVENT_PHYSICS_FORCE) + info.GetDamageType() & (DMG_GENERIC | DMG_PREVENT_PHYSICS_FORCE)) + // Don't do this if there was no carcass + && !(info.GetDamageType() & DMG_REMOVENORAGDOLL) ) { trace_t tr; diff --git a/sp/src/game/server/hl2/npc_metropolice.cpp b/sp/src/game/server/hl2/npc_metropolice.cpp index eb53ddcade0..fecf6587eee 100644 --- a/sp/src/game/server/hl2/npc_metropolice.cpp +++ b/sp/src/game/server/hl2/npc_metropolice.cpp @@ -107,6 +107,10 @@ int g_interactionMetrocopIdleChatter = 0; int g_interactionMetrocopClearSentenceQueues = 0; extern int g_interactionHitByPlayerThrownPhysObj; +#ifdef EZ2 +extern int g_interactionStasisGrenadeFreeze; +extern int g_interactionStasisGrenadeUnfreeze; +#endif ConVar sk_metropolice_stitch_reaction( "sk_metropolice_stitch_reaction","1.0"); ConVar sk_metropolice_stitch_tight_hitcount( "sk_metropolice_stitch_tight_hitcount","2"); @@ -550,10 +554,12 @@ void CNPC_MetroPolice::OnScheduleChange() { BaseClass::OnScheduleChange(); +#ifndef MAPBASE // Moved to Event_KilledOther() if ( GetEnemy() && HasCondition( COND_ENEMY_DEAD ) ) { AnnounceEnemyKill( GetEnemy() ); } +#endif } @@ -3135,7 +3141,11 @@ void CNPC_MetroPolice::DeathSound( const CTakeDamageInfo &info ) // Input : // Output : //----------------------------------------------------------------------------- +#ifdef MAPBASE +void CNPC_MetroPolice::LostEnemySound( CBaseEntity *pEnemy ) +#else void CNPC_MetroPolice::LostEnemySound( void) +#endif { // Don't announce enemies when the player isn't a criminal if ( !PlayerIsCriminal() ) @@ -3145,7 +3155,12 @@ void CNPC_MetroPolice::LostEnemySound( void) return; #ifdef METROPOLICE_USES_RESPONSE_SYSTEM - if (SpeakIfAllowed(TLK_COP_LOSTENEMY)) + AI_CriteriaSet modifiers; + ModifyOrAppendEnemyCriteria( modifiers, pEnemy ); + + modifiers.AppendCriteria( "lastseenenemy", gpGlobals->curtime - GetEnemies()->LastTimeSeen( pEnemy ) ); + + if (SpeakIfAllowed(TLK_COP_LOSTENEMY, modifiers )) { m_flNextLostSoundTime = gpGlobals->curtime + random->RandomFloat(5.0,15.0); } @@ -3174,14 +3189,21 @@ void CNPC_MetroPolice::LostEnemySound( void) // Input : // Output : //----------------------------------------------------------------------------- +#ifdef MAPBASE +void CNPC_MetroPolice::FoundEnemySound( CBaseEntity *pEnemy ) +#else void CNPC_MetroPolice::FoundEnemySound( void) +#endif { // Don't announce enemies when I'm in arrest behavior if ( HasSpawnFlags( SF_METROPOLICE_ARREST_ENEMY ) ) return; #ifdef METROPOLICE_USES_RESPONSE_SYSTEM - SpeakIfAllowed( TLK_COP_REFINDENEMY, SENTENCE_PRIORITY_HIGH ); + AI_CriteriaSet modifiers; + ModifyOrAppendEnemyCriteria( modifiers, pEnemy ); + + SpeakIfAllowed( TLK_COP_REFINDENEMY, modifiers, SENTENCE_PRIORITY_HIGH ); #else m_Sentences.Speak( "METROPOLICE_REFIND_ENEMY", SENTENCE_PRIORITY_HIGH ); #endif @@ -3729,6 +3751,21 @@ bool CNPC_MetroPolice::HandleInteraction(int interactionType, void *data, CBaseC return true; } +#ifdef EZ2 + if (interactionType == g_interactionStasisGrenadeFreeze) + { + CapabilitiesRemove(bits_CAP_TURN_HEAD); + // Handle unfreeze normally + return false; + } + else if (interactionType == g_interactionStasisGrenadeUnfreeze) + { + CapabilitiesAdd(bits_CAP_TURN_HEAD); + // Handle unfreeze normally + return false; + } +#endif + return BaseClass::HandleInteraction( interactionType, data, sourceEnt ); } @@ -3903,6 +3940,21 @@ void CNPC_MetroPolice::Event_Killed( const CTakeDamageInfo &info ) BaseClass::Event_Killed( info ); } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CNPC_MetroPolice::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ) +{ + BaseClass::Event_KilledOther( pVictim, info ); + +#ifdef MAPBASE // Moved from OnScheduleChange() + if ( pVictim && (pVictim->IsPlayer() || pVictim->IsNPC()) ) + { + AnnounceEnemyKill( pVictim ); + } +#endif +} + //----------------------------------------------------------------------------- // Try to enter a slot where we shoot a pistol //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/hl2/npc_metropolice.h b/sp/src/game/server/hl2/npc_metropolice.h index db82dd50e9b..94ec40284f8 100644 --- a/sp/src/game/server/hl2/npc_metropolice.h +++ b/sp/src/game/server/hl2/npc_metropolice.h @@ -79,6 +79,8 @@ class CNPC_MetroPolice : public CAI_BaseActor virtual void Event_Killed( const CTakeDamageInfo &info ); + virtual void Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ); + virtual void OnScheduleChange(); float GetIdealAccel( void ) const; @@ -175,8 +177,13 @@ class CNPC_MetroPolice : public CAI_BaseActor void SpeakAssaultSentence( int nSentenceType ); void SpeakStandoffSentence( int nSentenceType ); +#ifdef MAPBASE + virtual void LostEnemySound( CBaseEntity *pEnemy ); + virtual void FoundEnemySound( CBaseEntity *pEnemy ); +#else virtual void LostEnemySound( void ); virtual void FoundEnemySound( void ); +#endif virtual void AlertSound( void ); virtual void PainSound( const CTakeDamageInfo &info ); virtual void DeathSound( const CTakeDamageInfo &info ); diff --git a/sp/src/game/server/hl2/npc_turret_floor.cpp b/sp/src/game/server/hl2/npc_turret_floor.cpp index cb237608170..c3d736e2536 100644 --- a/sp/src/game/server/hl2/npc_turret_floor.cpp +++ b/sp/src/game/server/hl2/npc_turret_floor.cpp @@ -161,6 +161,9 @@ BEGIN_DATADESC( CNPC_FloorTurret ) DEFINE_OUTPUT( m_OnTipped, "OnTipped" ), DEFINE_OUTPUT( m_OnPhysGunPickup, "OnPhysGunPickup" ), DEFINE_OUTPUT( m_OnPhysGunDrop, "OnPhysGunDrop" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnStartTipped, "OnStartTipped" ), +#endif DEFINE_BASENPCINTERACTABLE_DATADESC(), @@ -1593,6 +1596,10 @@ bool CNPC_FloorTurret::PreThink( turretState_e state ) } #endif +#ifdef MAPBASE + m_OnStartTipped.FireOutput( this, this ); +#endif + //Stop being targetted SetState( NPC_STATE_DEAD ); m_lifeState = LIFE_DEAD; diff --git a/sp/src/game/server/hl2/npc_turret_floor.h b/sp/src/game/server/hl2/npc_turret_floor.h index 874aee4d7c5..6a458d601dd 100644 --- a/sp/src/game/server/hl2/npc_turret_floor.h +++ b/sp/src/game/server/hl2/npc_turret_floor.h @@ -298,6 +298,9 @@ class CNPC_FloorTurret : public CNPCBaseInteractive, public CDefaul COutputEvent m_OnTipped; COutputEvent m_OnPhysGunPickup; COutputEvent m_OnPhysGunDrop; +#ifdef MAPBASE + COutputEvent m_OnStartTipped; +#endif bool m_bHackedByAlyx; HSOUNDSCRIPTHANDLE m_ShotSounds; diff --git a/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp b/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp index 65d23587153..10441da6f06 100644 --- a/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp +++ b/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp @@ -2657,6 +2657,42 @@ bool CNPC_Vortigaunt::IsValidEnemy( CBaseEntity *pEnemy ) return BaseClass::IsValidEnemy( pEnemy ); } +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +Vector CNPC_Vortigaunt::GetShootEnemyDir( const Vector &shootOrigin, bool bNoisy ) +{ + Vector vecBase = BaseClass::GetShootEnemyDir( shootOrigin, bNoisy ); + +#ifdef EZ2 + CBaseEntity *pEnemy = GetEnemy(); + if ( IsPlayerAlly() && pEnemy ) + { + trace_t tr; + AI_TraceLine( shootOrigin, shootOrigin + (vecBase * InnateRange1MaxRange()), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); + + // If it won't reach, try the same thing as base class but with the head target + if (tr.m_pEnt != pEnemy) + { + Vector vecEnemyLKP = GetEnemyLKP(); + Vector vecEnemyOffset = pEnemy->HeadTarget( shootOrigin ) - pEnemy->GetAbsOrigin(); + + Vector retval = vecEnemyOffset + vecEnemyLKP - shootOrigin; + VectorNormalize( retval ); + AI_TraceLine( shootOrigin, shootOrigin + (retval * InnateRange1MaxRange()), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); + if (tr.m_pEnt == pEnemy) + { + return retval; + } + } + } +#endif + + return vecBase; +} + //----------------------------------------------------------------------------- // Purpose: Creates a blast where the beam has struck a target // Input : &vecOrigin - position to eminate from @@ -3316,6 +3352,7 @@ void CNPC_Vortigaunt::DispelAntlions( const Vector &vecOrigin, float flRadius, b // EXPLOSION! pPhysExplosion->Spawn(); pPhysExplosion->Explode( this, this ); + UTIL_Remove(pPhysExplosion); } // It's dangerous to go alone. Take this with you! @@ -3359,6 +3396,10 @@ void CNPC_Vortigaunt::DispelAntlions( const Vector &vecOrigin, float flRadius, b if ( pEntity == this || pEntity == NULL ) continue; + // Don't hit friendly or neutral targets + if ( IRelationType( pEntity ) >= D_LI ) + continue; + // Antlions react differently if ( IsAntlion( pEntity ) ) { @@ -3396,7 +3437,7 @@ void CNPC_Vortigaunt::DispelAntlions( const Vector &vecOrigin, float flRadius, b } } } - else if ( bDispel && pEntity->MyNPCPointer() != NULL && IRelationType( pEntity ) <= D_FR ) + else if ( bDispel && pEntity->MyNPCPointer() != NULL ) { CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); // Attempt to trace a line to hit the target @@ -3798,7 +3839,7 @@ AI_BEGIN_CUSTOM_NPC( npc_vortigaunt, CNPC_Vortigaunt ) " TASK_GET_PATH_TO_TARGET 0" " TASK_MOVE_TO_TARGET_RANGE 350" " TASK_STOP_MOVING 0" - " TASK_FACE_PLAYER 0" + " TASK_FACE_TARGET 0" " TASK_VORTIGAUNT_HEAL 0" "" " Interrupts" @@ -3992,6 +4033,48 @@ AI_BEGIN_CUSTOM_NPC( npc_vortigaunt, CNPC_Vortigaunt ) AI_END_CUSTOM_NPC() +#ifdef EZ2 +//============================================================================= +// +// Vortigaunt Standoff Behavior +// +//============================================================================= +int CNPC_Vortigaunt::CVortigauntStandoffBehavior::SelectScheduleUpdateWeapon( void ) +{ + if (GetOuter()->GetActiveWeapon()) + return BaseClass::SelectScheduleUpdateWeapon(); + + if ( HasCondition( COND_LIGHT_DAMAGE ) ) + { + // if hurt: + int iPercent = random->RandomInt(0,99); + + if ( iPercent <= GetParams().oddsCover && GetEnemy() != NULL) + { + // Vorts go into cover right away + SetReuseCurrentCover(); + GetOuter()->GetShotRegulator()->Reset( false ); + } + } + + return SCHED_NONE; +} + +int CNPC_Vortigaunt::CVortigauntStandoffBehavior::TranslateSchedule( int schedule ) +{ + int iBase = BaseClass::TranslateSchedule( schedule ); + + /*if (iBase == SCHED_ESTABLISH_LINE_OF_FIRE_FALLBACK) + { + // TODO: Vorts often fail LOS when enemies are in cover, do something different + return ???; + }*/ + + return iBase; +} +#endif + + //============================================================================= // // Charge Token diff --git a/sp/src/game/server/hl2/npc_vortigaunt_episodic.h b/sp/src/game/server/hl2/npc_vortigaunt_episodic.h index d0e290c5bf5..b1d16297f87 100644 --- a/sp/src/game/server/hl2/npc_vortigaunt_episodic.h +++ b/sp/src/game/server/hl2/npc_vortigaunt_episodic.h @@ -100,6 +100,7 @@ class CNPC_Vortigaunt : public CNPC_PlayerCompanion virtual int SelectSchedule( void ); virtual int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode ); virtual bool IsValidEnemy( CBaseEntity *pEnemy ); + virtual Vector GetShootEnemyDir( const Vector &shootOrigin, bool bNoisy = true ); bool IsLeading( void ) { return ( GetRunningBehavior() == &m_LeadBehavior && m_LeadBehavior.HasGoal() ); } void DeathSound( const CTakeDamageInfo &info ); @@ -306,6 +307,31 @@ class CNPC_Vortigaunt : public CNPC_PlayerCompanion COutputEvent m_OnFinishedChargingTarget; COutputEvent m_OnPlayerUse; +#ifdef EZ2 + class CVortigauntStandoffBehavior : public CAI_StandoffBehavior + { + typedef CAI_StandoffBehavior BaseClass; + + public: + virtual int SelectScheduleUpdateWeapon(); + virtual int SelectScheduleAttack() + { + int result = GetOuterVort()->SelectRangeAttack2Schedule(); + if ( result == SCHED_NONE ) + result = BaseClass::SelectScheduleAttack(); + return result; + } + + virtual int TranslateSchedule( int schedule ); + + inline CNPC_Vortigaunt *GetOuterVort() { return static_cast(GetOuter()); } + }; + + virtual CAI_StandoffBehavior &GetStandoffBehavior( void ) { return m_StandoffBehavior; } + + CVortigauntStandoffBehavior m_StandoffBehavior; +#endif + #ifdef EZ // Child classes need these attachments! protected: diff --git a/sp/src/game/server/hl2/prop_combine_ball.cpp b/sp/src/game/server/hl2/prop_combine_ball.cpp index 7fbd27d8caa..3b46370084a 100644 --- a/sp/src/game/server/hl2/prop_combine_ball.cpp +++ b/sp/src/game/server/hl2/prop_combine_ball.cpp @@ -1095,6 +1095,113 @@ void CPropCombineBall::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t R StopAnimating(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropCombineBall::SpawnerDestroyed( CBaseEntity *pActivator, bool *bSeekEnemy ) +{ + SetState( STATE_THROWN ); + WhizSoundThink(); + + m_bHeld = false; + m_bLaunched = true; + + // Stop with the dissolving + SetContextThink( NULL, gpGlobals->curtime, s_pHoldDissolveContext ); + + // We're ready to start colliding again. + SetCollisionGroup( HL2COLLISION_GROUP_COMBINE_BALL ); + + if ( m_pGlowTrail ) + { + m_pGlowTrail->TurnOn(); + m_pGlowTrail->SetRenderColor( 255, 255, 255, 255 ); + } + + // Set our desired speed to be launched at + SetSpeed( 1500.0f ); + + SetOwnerEntity( pActivator ); + SetWeaponLaunched( false ); + + if (!VPhysicsGetObject()) + return; + + if (pActivator->IsPlayer()) + { + PhysClearGameFlags( VPhysicsGetObject(), FVPHYSICS_NO_NPC_IMPACT_DMG ); + PhysSetGameFlags( VPhysicsGetObject(), FVPHYSICS_DMG_DISSOLVE | FVPHYSICS_HEAVY_OBJECT ); + } + else + { + // Don't do impact damage. Just touch them and do your dissolve damage and move on. + PhysSetGameFlags( VPhysicsGetObject(), FVPHYSICS_NO_NPC_IMPACT_DMG ); + } + + //if (pActivator->IsPlayer()) + //{ + // SetPlayerLaunched( ToBasePlayer( pActivator ) ); + //} + + Vector vecVelocity; + + if (bSeekEnemy) + { + CBaseEntity *pBestTarget = NULL; + CBaseEntity *list[256]; + + float distance; + float flBestDist = MAX_COORD_FLOAT; + int nCount = UTIL_EntitiesInSphere( list, 256, GetAbsOrigin(), sk_combine_ball_search_radius.GetFloat(), FL_NPC | FL_CLIENT ); + + for ( int i = 0; i < nCount; i++ ) + { + if ( !IsAttractiveTarget( list[i] ) ) + continue; + + distance = (list[i]->WorldSpaceCenter() - GetAbsOrigin()).LengthSqr(); + if ( distance < flBestDist ) + { + pBestTarget = list[i]; + flBestDist = distance; + } + } + + if ( pBestTarget ) + { + VectorSubtract( pBestTarget->WorldSpaceCenter(), GetAbsOrigin(), vecVelocity ); + VectorNormalize( vecVelocity ); + } + + *bSeekEnemy = (pBestTarget != NULL); + } + + if (bSeekEnemy == NULL || *bSeekEnemy == false) + { + // Choose a random direction based on current velocity + VPhysicsGetObject()->GetVelocity( &vecVelocity, NULL ); + VectorNormalize( vecVelocity ); + + QAngle shotAng; + VectorAngles( vecVelocity, shotAng ); + + // Offset by some small cone + shotAng[PITCH] += random->RandomInt( -75, 75 ); + shotAng[YAW] += random->RandomInt( -75, 75 ); + + AngleVectors( shotAng, &vecVelocity, NULL, NULL ); + } + + vecVelocity *= GetSpeed(); + + VPhysicsGetObject()->SetVelocity( &vecVelocity, &vec3_origin ); + + SetBallAsLaunched(); + StopAnimating(); +} +#endif + //------------------------------------------------------------------------------ // Stop looping sounds //------------------------------------------------------------------------------ @@ -1953,6 +2060,9 @@ BEGIN_DATADESC( CFuncCombineBallSpawner ) DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "Destroy", InputDestroy ), +#endif DEFINE_OUTPUT( m_OnBallGrabbed, "OnBallGrabbed" ), DEFINE_OUTPUT( m_OnBallReinserted, "OnBallReinserted" ), @@ -2105,6 +2215,35 @@ void CFuncCombineBallSpawner::InputDisable( inputdata_t &inputdata ) SetThink( NULL ); } +#ifdef MAPBASE +void CFuncCombineBallSpawner::InputDestroy( inputdata_t &inputdata ) +{ + if ( !m_bEnabled ) + { + UTIL_Remove( this ); + return; + } + + // One ball always seeks the nearest enemy + bool bSoughtEnemy = false; + + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "prop_combine_ball" ); + while (pEnt) + { + CPropCombineBall *pBall = static_cast(pEnt); + if (pBall && pBall->GetSpawner() == this) + { + BallGrabbed( pBall ); + pBall->SpawnerDestroyed( inputdata.pActivator, bSoughtEnemy ? NULL : &bSoughtEnemy ); + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "prop_combine_ball" ); + } + + UTIL_Remove( this ); +} +#endif + //----------------------------------------------------------------------------- // Choose a random point inside the cylinder diff --git a/sp/src/game/server/hl2/prop_combine_ball.h b/sp/src/game/server/hl2/prop_combine_ball.h index 1de05b6dc22..769c0cd96a8 100644 --- a/sp/src/game/server/hl2/prop_combine_ball.h +++ b/sp/src/game/server/hl2/prop_combine_ball.h @@ -103,6 +103,9 @@ class CPropCombineBall : public CBaseAnimating, public CDefaultPlayerPickupVPhys void SetSpawner( CFuncCombineBallSpawner *pSpawner ) { m_hSpawner = pSpawner; } void NotifySpawnerOfRemoval( void ); +#ifdef MAPBASE + void SpawnerDestroyed( CBaseEntity *pActivator, bool *bSeekEnemy ); +#endif float LastCaptureTime() const; @@ -248,6 +251,9 @@ class CFuncCombineBallSpawner : public CBaseEntity // Input void InputEnable( inputdata_t &inputdata ); void InputDisable( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputDestroy( inputdata_t &inputdata ); +#endif // Fire ball grabbed output void GrabBallTouch( CBaseEntity *pOther ); diff --git a/sp/src/game/server/hl2/weapon_357.cpp b/sp/src/game/server/hl2/weapon_357.cpp index e38c16495f7..2deb065c5b0 100644 --- a/sp/src/game/server/hl2/weapon_357.cpp +++ b/sp/src/game/server/hl2/weapon_357.cpp @@ -98,12 +98,6 @@ class CWeapon357 : public CBaseHLCombatWeapon virtual void SetActivity( Activity act, float duration ); bool CanDualWield() const { return true; } - CBaseAnimating *GetLeftHandGun() const { return m_hLeftHandGun; } - void SetLeftHandGun( CBaseAnimating *pGun ) { m_hLeftHandGun = pGun; } - -private: - - CHandle m_hLeftHandGun; #endif }; diff --git a/sp/src/game/server/hl2/weapon_flaregun.cpp b/sp/src/game/server/hl2/weapon_flaregun.cpp index 110f5c666d2..f2b74c46a4f 100644 --- a/sp/src/game/server/hl2/weapon_flaregun.cpp +++ b/sp/src/game/server/hl2/weapon_flaregun.cpp @@ -568,10 +568,13 @@ void CFlare::Start( float lifeTime ) //----------------------------------------------------------------------------- void CFlare::Die( float fadeTime ) { - m_flTimeBurnOut = gpGlobals->curtime + fadeTime; + if (m_bInActiveList) + { + m_flTimeBurnOut = gpGlobals->curtime + fadeTime; - SetThink( &CFlare::FlareThink ); - SetNextThink( gpGlobals->curtime + 0.1f ); + SetThink(&CFlare::FlareThink); + SetNextThink(gpGlobals->curtime + 0.1f); + } } //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/hl2/weapon_physcannon.cpp b/sp/src/game/server/hl2/weapon_physcannon.cpp index 606793d6235..559f3d8fffc 100644 --- a/sp/src/game/server/hl2/weapon_physcannon.cpp +++ b/sp/src/game/server/hl2/weapon_physcannon.cpp @@ -493,6 +493,9 @@ class CGrabController : public IMotionEvent float GetLoadWeight( void ) const { return m_flLoadWeight; } void SetAngleAlignment( float alignAngleCosine ) { m_angleAlignment = alignAngleCosine; } void SetIgnorePitch( bool bIgnore ) { m_bIgnoreRelativePitch = bIgnore; } +#ifdef MAPBASE + void SetDontUseListMass( bool bDontUse ) { m_bDontUseListMass = bDontUse; } +#endif QAngle TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer ); QAngle TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer ); @@ -539,6 +542,12 @@ class CGrabController : public IMotionEvent bool m_bIgnoreContact; #endif +#ifdef MAPBASE + // Prevents using the added mass of every part of the object + // (not saved due to only being used upon attach) + bool m_bDontUseListMass; +#endif + friend class CWeaponPhysCannon; }; @@ -592,6 +601,9 @@ CGrabController::CGrabController( void ) #ifdef EZ2 m_bIgnoreContact = false; #endif +#ifdef MAPBASE + m_bDontUseListMass = false; +#endif } CGrabController::~CGrabController( void ) @@ -814,12 +826,18 @@ void CGrabController::AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, { float mass = pList[i]->GetMass(); pList[i]->GetDamping( NULL, &m_savedRotDamping[i] ); - m_flLoadWeight += mass; m_savedMass[i] = mass; - // reduce the mass to prevent the player from adding crazy amounts of energy to the system - pList[i]->SetMass( REDUCED_CARRY_MASS / flFactor ); - pList[i]->SetDamping( NULL, &damping ); +#ifdef MAPBASE + if (!m_bDontUseListMass || pList[i] == pPhys) +#endif + { + m_flLoadWeight += mass; + + // reduce the mass to prevent the player from adding crazy amounts of energy to the system + pList[i]->SetMass( REDUCED_CARRY_MASS / flFactor ); + pList[i]->SetDamping( NULL, &damping ); + } } // NVNT setting m_pControllingPlayer to the player attached @@ -1117,7 +1135,24 @@ void CPlayerPickupController::Init( CBasePlayer *pPlayer, CBaseEntity *pObject ) Pickup_OnPhysGunPickup( pObject, m_pPlayer, PICKED_UP_BY_PLAYER ); +#ifdef MAPBASE + bool bUseGrabPos = false; + Vector vecGrabPos; + if ( dynamic_cast( pObject ) ) + { + m_grabController.SetDontUseListMass( true ); + + // Approximate where we're grabbing from + vecGrabPos = pPlayer->EyePosition() + (pPlayer->EyeDirection3D() * 16.0f); + bUseGrabPos = true; + } + else + m_grabController.SetDontUseListMass( false ); + + m_grabController.AttachEntity( pPlayer, pObject, pPhysics, false, vecGrabPos, bUseGrabPos ); +#else m_grabController.AttachEntity( pPlayer, pObject, pPhysics, false, vec3_origin, false ); +#endif // NVNT apply a downward force to simulate the mass of the held object. #if defined( WIN32 ) && !defined( _X360 ) HapticSetConstantForce(m_pPlayer,clamp(m_grabController.GetLoadWeight()*0.1,1,6)*Vector(0,-1,0)); @@ -2765,6 +2800,11 @@ CWeaponPhysCannon::FindObjectResult_t CWeaponPhysCannon::FindObject( void ) pullDir *= (mass + 0.5) * (1/50.0f); } + CPhysicsProp* pProp = dynamic_cast(pEntity); + if (pProp) { + pProp->OnPhysGunPull( pOwner ); + } + // Nudge it towards us pObj->ApplyForceCenter( pullDir ); return OBJECT_NOT_FOUND; diff --git a/sp/src/game/server/hl2/weapon_pistol.cpp b/sp/src/game/server/hl2/weapon_pistol.cpp index 00ede3df6c9..d206554eda5 100644 --- a/sp/src/game/server/hl2/weapon_pistol.cpp +++ b/sp/src/game/server/hl2/weapon_pistol.cpp @@ -92,7 +92,12 @@ class CWeaponPistol : public CHLMachineGun 1.0f ); // We lerp from very accurate to inaccurate over time +#ifdef EZ2 + // Twice as inaccurate when dual wielding + VectorLerp( VECTOR_CONE_1DEGREES, IsDualWielding() ? VECTOR_CONE_6DEGREES*2 : VECTOR_CONE_6DEGREES, ramp, cone ); +#else VectorLerp( VECTOR_CONE_1DEGREES, VECTOR_CONE_6DEGREES, ramp, cone ); +#endif } else { @@ -116,7 +121,7 @@ class CWeaponPistol : public CHLMachineGun virtual float GetFireRate( void ) { #ifdef EZ2 - if (m_hLeftHandGun != NULL) + if (IsDualWielding() && (GetOwner() && GetOwner()->IsNPC())) return 0.25f; #endif @@ -136,12 +141,6 @@ class CWeaponPistol : public CHLMachineGun virtual void SetActivity( Activity act, float duration ); bool CanDualWield() const { return true; } - CBaseAnimating *GetLeftHandGun() const { return m_hLeftHandGun; } - void SetLeftHandGun( CBaseAnimating *pGun ) { m_hLeftHandGun = pGun; } - -private: - - CHandle m_hLeftHandGun; #endif protected: @@ -370,7 +369,7 @@ void CWeaponPistol::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCh { #ifdef EZ2 // HACKHACK: Ignore the regular firing event while dual-wielding - if (GetLeftHandGun()) + if (IsDualWielding()) return; #endif @@ -467,7 +466,11 @@ void CWeaponPistol::PrimaryAttack( void ) } m_flLastAttackTime = gpGlobals->curtime; +#ifdef EZ2 + m_flSoonestPrimaryAttack = gpGlobals->curtime + (IsDualWielding() ? PISTOL_FASTEST_REFIRE_TIME * 0.5f : PISTOL_FASTEST_REFIRE_TIME); +#else m_flSoonestPrimaryAttack = gpGlobals->curtime + PISTOL_FASTEST_REFIRE_TIME; +#endif CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), SOUNDENT_VOLUME_PISTOL, 0.2, GetOwner() ); CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); @@ -664,7 +667,22 @@ class CWeaponPulsePistol : public CWeaponPistol virtual bool Reload( void ) { return false; } // The pulse pistol does not reload - virtual int GetMaxClip2( void ) const { int iBase = BaseClass::GetMaxClip2(); return iBase > 0 ? iBase : sv_pulse_pistol_max_charge.GetInt(); } + bool DualWieldOverridesSecondary() const { return false; } + + virtual int GetMaxClip2( void ) const + { + int iBase = BaseClass::GetMaxClip2(); + if (iBase > 0) + { + return iBase; + } + else + { + return IsDualWielding() ? (sv_pulse_pistol_max_charge.GetInt() * 2) : sv_pulse_pistol_max_charge.GetInt(); + } + } + + int GetMinShotClip() const { return IsDualWielding() ? 20 : 10; } virtual const Vector& GetBulletSpread( void ) { @@ -685,7 +703,12 @@ class CWeaponPulsePistol : public CWeaponPistol 1.0f ); // We lerp from very accurate to inaccurate over time +#ifdef EZ2 + // Twice as inaccurate when dual wielding + VectorLerp( VECTOR_CONE_2DEGREES, IsDualWielding() ? VECTOR_CONE_15DEGREES * 2 : VECTOR_CONE_15DEGREES, ramp, cone); +#else VectorLerp( VECTOR_CONE_2DEGREES, VECTOR_CONE_15DEGREES, ramp, cone ); +#endif return cone; } @@ -697,7 +720,7 @@ class CWeaponPulsePistol : public CWeaponPistol return 3.0f; } - if (GetLeftHandGun() != NULL) + if (IsDualWielding() != NULL) return 0.5f; return 1.0f; @@ -725,6 +748,7 @@ class CWeaponPulsePistol : public CWeaponPistol protected: CHandle m_hChargeSprite; + CHandle m_hChargeSprite2; // For dual pistols private: // For recharging the ammo @@ -811,7 +835,7 @@ bool CWeaponPulsePistol::IsChargePressed( int chargeButton, CBasePlayer * pOwner return false; // If "no charge hold" is set, we're out of ammo, but we have a charge, treat it as though the player let go of the attack button - if (sv_pulse_pistol_no_charge_hold.GetBool() && m_iClip2 > 0 && m_iClip1 <= 10) + if (sv_pulse_pistol_no_charge_hold.GetBool() && m_iClip2 > 0 && m_iClip1 <= GetMinShotClip()) return false; return (pOwner->m_nButtons & chargeButton) > 0; @@ -903,7 +927,7 @@ void CWeaponPulsePistol::Operator_HandleAnimEvent( animevent_t * pEvent, CBaseCo { case AE_WPN_INCREMENTAMMO: case AE_SLIDERETURN: - m_iClip1 = MAX( m_iClip1, sv_pulse_pistol_slide_return_charge.GetInt() ); + m_iClip1 = MAX( m_iClip1, IsDualWielding() ? sv_pulse_pistol_slide_return_charge.GetInt()*2 : sv_pulse_pistol_slide_return_charge.GetInt() ); WeaponSound( RELOAD, m_flNextPrimaryAttack ); break; @@ -946,14 +970,14 @@ void CWeaponPulsePistol::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, pOperator->FireBullets( info ); pOperator->DoMuzzleFlash(); - if (m_iClip1 < 10) + if (m_iClip1 < GetMinShotClip()) { // Reload immediately m_iClip1 = 0; } else { - m_iClip1 -= 10; + m_iClip1 -= GetMinShotClip(); // NPCs handle recharging after every shot RechargeAmmo(); @@ -1038,14 +1062,17 @@ void CWeaponPulsePistol::RechargeAmmo( void ) float flChargeInterval = gpGlobals->curtime - m_flLastChargeTime; m_flLastChargeTime = gpGlobals->curtime; - // This code is inherited from the airboat. I've changed it to work for the pistol. - int nMaxAmmo = 50; - if (m_iClip1 == nMaxAmmo) + if (m_iClip1 == GetMaxClip1()) { return; } + // This code is inherited from the airboat. I've changed it to work for the pistol. float flRechargeRate = 5; + + if (IsDualWielding()) + flRechargeRate *= 2.0f; + float flChargeAmount = flRechargeRate * flChargeInterval; if (m_flDrainRemainder != 0.0f) { @@ -1065,9 +1092,9 @@ void CWeaponPulsePistol::RechargeAmmo( void ) int nAmmoToAdd = (int)m_flChargeRemainder; m_flChargeRemainder -= nAmmoToAdd; m_iClip1 += nAmmoToAdd; - if (m_iClip1 > nMaxAmmo) + if (m_iClip1 > GetMaxClip1()) { - m_iClip1 = nMaxAmmo; + m_iClip1 = GetMaxClip1(); m_flChargeRemainder = 0.0f; } } @@ -1093,7 +1120,7 @@ void CWeaponPulsePistol::MakeTracer( const Vector &vecTracerSrc, const trace_t & //----------------------------------------------------------------------------- void CWeaponPulsePistol::PrimaryAttack( void ) { - if (m_iClip1 < 10) // Don't allow firing if we only have one shot left + if (m_iClip1 < GetMinShotClip()) // Don't allow firing if we only have one shot left { DryFire(); } @@ -1148,7 +1175,7 @@ void CWeaponPulsePistol::PrimaryAttack( void ) } // Subtract the charge - m_iClip1 = MAX(m_iClip1 - 10, 1); // Never drop the charge below 1 + m_iClip1 = MAX(m_iClip1 - GetMinShotClip(), 1); // Never drop the charge below 1 m_iClip2 = 0; m_iPrimaryAttacks++; @@ -1192,7 +1219,7 @@ void CWeaponPulsePistol::PrimaryAttack( void ) m_iPrimaryAttacks++; gamestats->Event_WeaponFired( pPlayer, true, GetClassname() ); - m_flSoonestPrimaryAttack = gpGlobals->curtime + PULSE_PISTOL_FASTEST_REFIRE_TIME; + m_flSoonestPrimaryAttack = gpGlobals->curtime + (IsDualWielding() ? PULSE_PISTOL_FASTEST_REFIRE_TIME * 0.5f : PULSE_PISTOL_FASTEST_REFIRE_TIME); m_flNextPrimaryAttack = gpGlobals->curtime + PULSE_PISTOL_FASTEST_REFIRE_TIME; } } @@ -1203,7 +1230,7 @@ void CWeaponPulsePistol::PrimaryAttack( void ) void CWeaponPulsePistol::ChargeAttack( void ) { // Play the slide rack animation if we're trying to charge but have no ammo - if ( m_iClip1 <= 10 && m_iClip2 <= 0 ) { + if ( m_iClip1 <= GetMinShotClip() && m_iClip2 <= 0) { if( m_flNextPrimaryAttack <= gpGlobals->curtime ) DryFire(); return; @@ -1211,7 +1238,7 @@ void CWeaponPulsePistol::ChargeAttack( void ) int nMaxCharge = GetMaxClip2(); // If there is only one shot left or the charge has reached maximum, do not charge! - if (m_iClip1 <= 10 || m_iClip2 == nMaxCharge) + if (m_iClip1 <= GetMinShotClip() || m_iClip2 == nMaxCharge) { RechargeAmmo(); return; @@ -1229,6 +1256,10 @@ void CWeaponPulsePistol::ChargeAttack( void ) // The charge attack charges twice as quickly as ammo recharges float flRechargeRate = 10; + + if (IsDualWielding()) + flRechargeRate *= 2.0f; + float flChargeAmount = flRechargeRate * flChargeInterval; m_flChargeRemainder += flChargeAmount; @@ -1337,6 +1368,18 @@ void CWeaponPulsePistol::StartChargeEffects() m_hChargeSprite->SetBrightness( 0, 0.1f ); m_hChargeSprite->SetScale( 0.05f, 0.05f ); m_hChargeSprite->TurnOn(); + + if (IsDualWielding()) + { + m_hChargeSprite2 = CSprite::SpriteCreate( "effects/fluttercore.vmt", GetAbsOrigin(), false ); + + m_hChargeSprite2->SetAsTemporary(); + m_hChargeSprite2->SetAttachment( pOwner->GetViewModel(), 3 ); + m_hChargeSprite2->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone ); + m_hChargeSprite2->SetBrightness( 0, 0.1f ); + m_hChargeSprite2->SetScale( 0.05f, 0.05f ); + m_hChargeSprite2->TurnOn(); + } } } @@ -1348,6 +1391,11 @@ void CWeaponPulsePistol::SetChargeEffectBrightness( float alpha ) if (m_hChargeSprite != NULL) { m_hChargeSprite->SetBrightness( m_iClip2, 0.1f ); + + if (m_hChargeSprite2 != NULL) + { + m_hChargeSprite2->SetBrightness( m_iClip2, 0.1f ); + } } } @@ -1361,6 +1409,12 @@ void CWeaponPulsePistol::KillChargeEffects() UTIL_Remove( m_hChargeSprite ); m_hChargeSprite = NULL; } + + if (m_hChargeSprite2 != NULL) + { + UTIL_Remove( m_hChargeSprite2 ); + m_hChargeSprite2 = NULL; + } } //----------------------------------------------------------------------------- @@ -1482,6 +1536,7 @@ PRECACHE_WEAPON_REGISTER( weapon_pulsepistol ); BEGIN_DATADESC( CWeaponPulsePistol ) DEFINE_FIELD( m_flLastChargeTime, FIELD_TIME ), DEFINE_FIELD( m_flLastChargeSoundTime, FIELD_TIME ), - DEFINE_FIELD( m_hChargeSprite, FIELD_EHANDLE ) + DEFINE_FIELD( m_hChargeSprite, FIELD_EHANDLE ), + DEFINE_FIELD( m_hChargeSprite2, FIELD_EHANDLE ), END_DATADESC() #endif diff --git a/sp/src/game/server/hl2/weapon_smg1.cpp b/sp/src/game/server/hl2/weapon_smg1.cpp index 6c7163dac14..d329e499b02 100644 --- a/sp/src/game/server/hl2/weapon_smg1.cpp +++ b/sp/src/game/server/hl2/weapon_smg1.cpp @@ -26,6 +26,10 @@ extern ConVar sk_plr_dmg_smg1_grenade; extern ConVar sk_npc_dmg_smg1_grenade; #endif +#ifdef EZ2 +ConVar weapon_smg1_use_ez1_accuracy( "weapon_smg1_use_ez1_accuracy", "0" ); +#endif + class CWeaponSMG1 : public CHLSelectFireMachineGun { DECLARE_DATADESC(); @@ -59,10 +63,12 @@ class CWeaponSMG1 : public CHLSelectFireMachineGun { #ifdef EZ1 static const Vector cone = VECTOR_CONE_2DEGREES; + return cone; #else + static const Vector coneEZ1 = VECTOR_CONE_2DEGREES; static const Vector cone = VECTOR_CONE_5DEGREES; + return weapon_smg1_use_ez1_accuracy.GetBool() ? coneEZ1 : cone; #endif - return cone; } const WeaponProficiencyInfo_t *GetProficiencyValues(); diff --git a/sp/src/game/server/mapbase/ai_grenade.h b/sp/src/game/server/mapbase/ai_grenade.h index db8bc1f58bc..2148880a033 100644 --- a/sp/src/game/server/mapbase/ai_grenade.h +++ b/sp/src/game/server/mapbase/ai_grenade.h @@ -416,7 +416,7 @@ bool CAI_GrenadeUser::CanAltFireEnemy( bool bUseFreeKnowledge ) return false; #ifdef EZ2 - if (!EntIsClass(this->GetActiveWeapon(), gm_isz_class_AR2) && !EntIsClass(this->GetActiveWeapon(), gm_isz_class_SMG1) && !FClassnameIs(this->GetActiveWeapon(), "weapon_pulsepistol")) + if (!EntIsClass(this->GetActiveWeapon(), gm_isz_class_AR2) && !EntIsClass(this->GetActiveWeapon(), gm_isz_class_SMG1) && !FClassnameIs(this->GetActiveWeapon(), "weapon_pulsepistol") && !FClassnameIs(this->GetActiveWeapon(), "weapon_oicw")) #else if (!EntIsClass(this->GetActiveWeapon(), gm_isz_class_AR2) && !EntIsClass(this->GetActiveWeapon(), gm_isz_class_SMG1)) #endif @@ -725,7 +725,11 @@ void CAI_GrenadeUser::DropGrenadeItemsOnDeath( const CTakeDamageInfo & CBaseEntity *pItem; if (this->GetActiveWeapon()) { +#ifdef EZ2 + if (FClassnameIs( this->GetActiveWeapon(), "weapon_smg1" ) || FClassnameIs( this->GetActiveWeapon(), "weapon_oicw" )) +#else if (FClassnameIs( this->GetActiveWeapon(), "weapon_smg1" )) +#endif pItem = this->DropItem( "item_ammo_smg1_grenade", this->WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) ); #ifdef EZ2 else if (FClassnameIs( this->GetActiveWeapon(), "weapon_pulsepistol" )) diff --git a/sp/src/game/server/mapbase/custom_weapon_factory.h b/sp/src/game/server/mapbase/custom_weapon_factory.h index 3abb17edfca..bf46880b1f6 100644 --- a/sp/src/game/server/mapbase/custom_weapon_factory.h +++ b/sp/src/game/server/mapbase/custom_weapon_factory.h @@ -107,7 +107,7 @@ class CDefaultCustomWeaponEntityFactory : public CCustomWeaponEntityFactoryBase< virtual void ReleaseData(const void* pData) const { - delete pData; + delete (Data*)pData; } }; diff --git a/sp/src/game/server/mod/npc_lost_soul.cpp b/sp/src/game/server/mod/npc_lost_soul.cpp index 154522a277d..c0c517bd42f 100644 --- a/sp/src/game/server/mod/npc_lost_soul.cpp +++ b/sp/src/game/server/mod/npc_lost_soul.cpp @@ -225,7 +225,7 @@ void CNPC_LostSoul::Spawn( void ) CEntityFlame *pFlame = CEntityFlame::Create(this); if (pFlame) { - pFlame->SetLifetime(HUGE_VAL); + pFlame->SetLifetime(FLT_MAX); SetEffectEntity(pFlame); pFlame->SetSize(8); pFlame->SetDamage(0.0f); diff --git a/sp/src/game/server/physics_prop_ragdoll.cpp b/sp/src/game/server/physics_prop_ragdoll.cpp index 9d73e4afcf9..0b50489fc69 100644 --- a/sp/src/game/server/physics_prop_ragdoll.cpp +++ b/sp/src/game/server/physics_prop_ragdoll.cpp @@ -34,6 +34,8 @@ #ifdef MAPBASE ConVar ragdoll_autointeractions("ragdoll_autointeractions", "1", FCVAR_NONE, "Controls whether we should rely on hardcoded keyvalues or automatic flesh checks for ragdoll physgun interactions."); #define IsBody() VPhysicsIsFlesh() + +ConVar ragdoll_always_allow_use( "ragdoll_always_allow_use", "0", FCVAR_NONE, "Allows all ragdolls to be used and, if they aren't explicitly set to prevent pickup, picked up." ); #endif #ifdef EZ2 @@ -68,6 +70,8 @@ const float ATTACHED_DAMPING_SCALE = 50.0f; #define SF_RAGDOLLPROP_STARTASLEEP 0x10000 #ifdef MAPBASE #define SF_RAGDOLLPROP_FIXED_CONSTRAINTS 0x20000 +#define SF_RAGDOLLPROP_ALLOW_USE 0x40000 +#define SF_RAGDOLLPROP_PREVENT_PICKUP 0x80000 #endif //----------------------------------------------------------------------------- @@ -123,6 +127,8 @@ BEGIN_DATADESC(CRagdollProp) #ifdef MAPBASE DEFINE_INPUTFUNC( FIELD_VOID, "Wake", InputWake ), DEFINE_INPUTFUNC( FIELD_VOID, "Sleep", InputSleep ), + DEFINE_INPUTFUNC( FIELD_VOID, "AddToLRU", InputAddToLRU ), + DEFINE_INPUTFUNC( FIELD_VOID, "RemoveFromLRU", InputRemoveFromLRU ), #endif DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputTurnOn ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputTurnOff ), @@ -147,6 +153,10 @@ BEGIN_DATADESC(CRagdollProp) #endif DEFINE_FIELD( m_bHasBeenPhysgunned, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnPlayerUse, "OnPlayerUse" ), +#endif + // think functions DEFINE_THINKFUNC( SetDebrisThink ), DEFINE_THINKFUNC( ClearFlagsThink ), @@ -466,8 +476,38 @@ void CRagdollProp::Precache( void ) int CRagdollProp::ObjectCaps() { - return BaseClass::ObjectCaps() | FCAP_WCEDIT_POSITION; + int caps = FCAP_WCEDIT_POSITION; + +#ifdef MAPBASE + if (HasSpawnFlags( SF_RAGDOLLPROP_ALLOW_USE ) || ragdoll_always_allow_use.GetBool()) + caps |= FCAP_IMPULSE_USE; +#endif + + return BaseClass::ObjectCaps() | caps; +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pActivator - +// *pCaller - +// useType - +// value - +//----------------------------------------------------------------------------- +void CRagdollProp::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + CBasePlayer *pPlayer = ToBasePlayer( pActivator ); + if (pPlayer) + { + m_OnPlayerUse.FireOutput( pActivator, this ); + + if (!HasSpawnFlags( SF_RAGDOLLPROP_PREVENT_PICKUP )) + { + pPlayer->PickupObject( this, false ); + } + } } +#endif //----------------------------------------------------------------------------- // Purpose: @@ -567,7 +607,7 @@ void CRagdollProp::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t r m_bHasBeenPhysgunned = true; #ifdef MAPBASE - if( (ragdoll_autointeractions.GetBool() == true && IsBody()) || HasPhysgunInteraction( "onpickup", "boogie" ) ) + if( ((ragdoll_autointeractions.GetBool() == true && IsBody()) || HasPhysgunInteraction( "onpickup", "boogie" )) && reason != PICKED_UP_BY_PLAYER ) #else if( HasPhysgunInteraction( "onpickup", "boogie" ) ) #endif @@ -609,7 +649,7 @@ void CRagdollProp::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reaso m_flLastPhysicsInfluenceTime = gpGlobals->curtime; #ifdef MAPBASE - if( (ragdoll_autointeractions.GetBool() == true && IsBody()) || HasPhysgunInteraction( "onpickup", "boogie" ) ) + if( ((ragdoll_autointeractions.GetBool() == true && IsBody()) || HasPhysgunInteraction( "onpickup", "boogie" )) && (Reason != DROPPED_BY_PLAYER && Reason != THROWN_BY_PLAYER) ) #else if( HasPhysgunInteraction( "onpickup", "boogie" ) ) #endif @@ -2002,6 +2042,23 @@ void CRagdollProp::GetAngleOverrideFromCurrentState( char *pOut, int size ) } } +#ifdef EZ2 +bool CRagdollProp::IsMotionEnabled(void) +{ + for (int iRagdoll = 0; iRagdoll < m_ragdoll.listCount; ++iRagdoll) + { + IPhysicsObject* pPhysicsObject = m_ragdoll.list[iRagdoll].pObject; + if (pPhysicsObject != NULL && !pPhysicsObject->IsMotionEnabled()) + { + return false; + } + } + + return true; +} +#endif + + void CRagdollProp::DisableMotion( void ) { for ( int iRagdoll = 0; iRagdoll < m_ragdoll.listCount; ++iRagdoll ) @@ -2080,6 +2137,24 @@ void CRagdollProp::InputSleep( inputdata_t &inputdata ) } } } + +//----------------------------------------------------------------------------- +// Purpose: Adds ragdoll to LRU. +//----------------------------------------------------------------------------- +void CRagdollProp::InputAddToLRU( inputdata_t &inputdata ) +{ + AddSpawnFlags( SF_RAGDOLLPROP_USE_LRU_RETIREMENT ); + s_RagdollLRU.MoveToTopOfLRU( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Removes ragdoll from LRU. +//----------------------------------------------------------------------------- +void CRagdollProp::InputRemoveFromLRU( inputdata_t &inputdata ) +{ + RemoveSpawnFlags( SF_RAGDOLLPROP_USE_LRU_RETIREMENT ); + s_RagdollLRU.RemoveFromLRU( this ); +} #endif void CRagdollProp::InputTurnOn( inputdata_t &inputdata ) diff --git a/sp/src/game/server/physics_prop_ragdoll.h b/sp/src/game/server/physics_prop_ragdoll.h index 3e8cabf9d07..fd162d4984d 100644 --- a/sp/src/game/server/physics_prop_ragdoll.h +++ b/sp/src/game/server/physics_prop_ragdoll.h @@ -42,6 +42,10 @@ class CRagdollProp : public CBaseAnimating, public CDefaultPlayerPickupVPhysics int ObjectCaps(); +#ifdef MAPBASE + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); +#endif + DECLARE_SERVERCLASS(); // Don't treat as a live target virtual bool IsAlive( void ) { return false; } @@ -123,6 +127,9 @@ class CRagdollProp : public CBaseAnimating, public CDefaultPlayerPickupVPhysics void SetKiller( CBaseEntity *pKiller ) { m_hKiller = pKiller; } void GetAngleOverrideFromCurrentState( char *pOut, int size ); +#ifdef EZ2 + bool IsMotionEnabled(void); +#endif void DisableMotion( void ); // Input/Output @@ -132,6 +139,8 @@ class CRagdollProp : public CBaseAnimating, public CDefaultPlayerPickupVPhysics #ifdef MAPBASE void InputWake( inputdata_t &inputdata ); void InputSleep( inputdata_t &inputdata ); + void InputAddToLRU( inputdata_t &inputdata ); + void InputRemoveFromLRU( inputdata_t &inputdata ); #endif void InputTurnOn( inputdata_t &inputdata ); void InputTurnOff( inputdata_t &inputdata ); @@ -190,6 +199,10 @@ class CRagdollProp : public CBaseAnimating, public CDefaultPlayerPickupVPhysics string_t m_strSourceClassName; bool m_bHasBeenPhysgunned; +#ifdef MAPBASE + COutputEvent m_OnPlayerUse; +#endif + // If not 1, then allow underlying sequence to blend in with simulated bone positions CNetworkVar( float, m_flBlendWeight ); CNetworkVar( int, m_nOverlaySequence ); diff --git a/sp/src/game/server/player.cpp b/sp/src/game/server/player.cpp index c7e4250fe94..492174b317a 100644 --- a/sp/src/game/server/player.cpp +++ b/sp/src/game/server/player.cpp @@ -505,6 +505,7 @@ END_DATADESC() #ifdef MAPBASE_VSCRIPT // TODO: Better placement? ScriptHook_t g_Hook_PlayerRunCommand; +ScriptHook_t g_Hook_FindUseEntity; BEGIN_ENT_SCRIPTDESC( CBasePlayer, CBaseCombatCharacter, "The player entity." ) @@ -559,6 +560,9 @@ BEGIN_ENT_SCRIPTDESC( CBasePlayer, CBaseCombatCharacter, "The player entity." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetEyeUp, "GetEyeUp", "Gets the player's up eye vector." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetViewModel, "GetViewModel", "Returns the viewmodel of the specified index." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetUseEntity, "GetUseEntity", "Gets the player's current use entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetHeldObject, "GetHeldObject", "Gets the player's currently held object IF it is being held by a gravity gun. To check for the player's held +USE object, use the standalone GetPlayerHeldEntity function." ) DEFINE_SCRIPTFUNC( GetBonusProgress, "Returns the player's bonus progress." ) DEFINE_SCRIPTFUNC( GetBonusChallenge, "Returns the player's bonus challenge." ) @@ -571,6 +575,11 @@ BEGIN_ENT_SCRIPTDESC( CBasePlayer, CBaseCombatCharacter, "The player entity." ) BEGIN_SCRIPTHOOK( g_Hook_PlayerRunCommand, "PlayerRunCommand", FIELD_VOID, "Called when running a player command on the server." ) DEFINE_SCRIPTHOOK_PARAM( "command", FIELD_HSCRIPT ) END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( g_Hook_FindUseEntity, "FindUseEntity", FIELD_HSCRIPT, "Called when finding an entity to use. The 'entity' parameter is for the entity found by the default function. If 'is_radius' is true, then this entity was found by searching in a radius around the cursor, rather than being directly used. Return a different entity to use something else." ) + DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT ) + DEFINE_SCRIPTHOOK_PARAM( "is_radius", FIELD_BOOLEAN ) + END_SCRIPTHOOK() END_SCRIPTDESC(); #else @@ -7142,8 +7151,16 @@ bool CBasePlayer::BumpWeapon( CBaseCombatWeapon *pWeapon ) // ---------------------------------------- // If I already have it just take the ammo // ---------------------------------------- - if (Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType())) + if (CBaseCombatWeapon *pOwnsThisType = Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType())) { +#ifdef EZ2 + if ( Weapon_EquipDual( pWeapon, pOwnsThisType ) ) + { + // Weapon has been merged with the existing weapon + return true; + } + else +#endif if( Weapon_EquipAmmoOnly( pWeapon ) ) { // Only remove me if I have no ammo left diff --git a/sp/src/game/server/player.h b/sp/src/game/server/player.h index d06bec662ef..2805a386e7c 100644 --- a/sp/src/game/server/player.h +++ b/sp/src/game/server/player.h @@ -419,6 +419,9 @@ class CBasePlayer : public CBaseCombatCharacter const Vector& ScriptGetEyeUp() { static Vector vecUp; EyeVectors( NULL, NULL, &vecUp ); return vecUp; } HSCRIPT ScriptGetViewModel( int viewmodelindex ); + + HSCRIPT ScriptGetUseEntity() { return ToHScript( GetUseEntity() ); } + HSCRIPT ScriptGetHeldObject() { return ToHScript( GetHeldObject() ); } #endif // View model prediction setup diff --git a/sp/src/game/server/point_spotlight.cpp b/sp/src/game/server/point_spotlight.cpp index d5901714ec1..71aad0b11a9 100644 --- a/sp/src/game/server/point_spotlight.cpp +++ b/sp/src/game/server/point_spotlight.cpp @@ -62,6 +62,7 @@ class CPointSpotlight : public CPointEntity private: bool m_bSpotlightOn; bool m_bEfficientSpotlight; + bool m_bIgnoreSolid; Vector m_vSpotlightTargetPos; Vector m_vSpotlightCurrentPos; Vector m_vSpotlightDir; @@ -100,6 +101,7 @@ BEGIN_DATADESC( CPointSpotlight ) DEFINE_FIELD( m_vSpotlightDir, FIELD_VECTOR ), DEFINE_FIELD( m_nHaloSprite, FIELD_INTEGER ), + DEFINE_KEYFIELD( m_bIgnoreSolid, FIELD_BOOLEAN, "IgnoreSolid" ), DEFINE_KEYFIELD( m_flSpotlightMaxLength,FIELD_FLOAT, "SpotlightLength"), DEFINE_KEYFIELD( m_flSpotlightGoalWidth,FIELD_FLOAT, "SpotlightWidth"), DEFINE_KEYFIELD( m_flHDRColorScale, FIELD_FLOAT, "HDRColorScale" ), @@ -138,6 +140,7 @@ CPointSpotlight::CPointSpotlight() #endif m_flHDRColorScale = 1.0f; m_nMinDXLevel = 0; + m_bIgnoreSolid = false; #ifdef MAPBASE m_flHaloScale = 60.0f; #endif @@ -380,12 +383,21 @@ void CPointSpotlight::SpotlightCreate(void) AngleVectors( GetAbsAngles(), &m_vSpotlightDir ); - trace_t tr; - UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + m_vSpotlightDir * m_flSpotlightMaxLength, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr); + Vector vTargetPos; + if ( m_bIgnoreSolid ) + { + vTargetPos = GetAbsOrigin() + m_vSpotlightDir * m_flSpotlightMaxLength; + } + else + { + trace_t tr; + UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + m_vSpotlightDir * m_flSpotlightMaxLength, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); + vTargetPos = tr.endpos; + } m_hSpotlightTarget = (CSpotlightEnd*)CreateEntityByName( "spotlight_end" ); m_hSpotlightTarget->Spawn(); - m_hSpotlightTarget->SetAbsOrigin( tr.endpos ); + m_hSpotlightTarget->SetAbsOrigin( vTargetPos ); m_hSpotlightTarget->SetOwnerEntity( this ); m_hSpotlightTarget->m_clrRender = m_clrRender; m_hSpotlightTarget->m_Radius = m_flSpotlightMaxLength; @@ -437,9 +449,17 @@ Vector CPointSpotlight::SpotlightCurrentPos(void) AngleVectors( GetAbsAngles(), &m_vSpotlightDir ); // Get beam end point. Only collide with solid objects, not npcs - trace_t tr; - UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + (m_vSpotlightDir * 2 * m_flSpotlightMaxLength), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); - return tr.endpos; + Vector vEndPos = GetAbsOrigin() + ( m_vSpotlightDir * 2 * m_flSpotlightMaxLength ); + if ( m_bIgnoreSolid ) + { + return vEndPos; + } + else + { + trace_t tr; + UTIL_TraceLine( GetAbsOrigin(), vEndPos, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); + return tr.endpos; + } } //------------------------------------------------------------------------------ diff --git a/sp/src/game/server/props.cpp b/sp/src/game/server/props.cpp index e68b47f701d..63debaa6126 100644 --- a/sp/src/game/server/props.cpp +++ b/sp/src/game/server/props.cpp @@ -50,6 +50,8 @@ #ifdef EZ2 #include "ez2/ez2_player.h" #include "eventqueue.h" +#include "RagdollBoogie.h" +#include "particle_parse.h" #endif // memdbgon must be the last include file in a .cpp file!!! @@ -107,6 +109,9 @@ ConVar mapbase_prop_consistency_noremove("mapbase_prop_consistency_noremove", "1 #ifdef EZ2 #define PROP_PHYSICS_KICK_MULTIPLIER 3 + + // Unique vort barrel boogie color + static const Vector g_vecVortBarrelBoogieColor( 0.1675, 0.90, 0.1675 ); #endif //----------------------------------------------------------------------------- @@ -280,6 +285,10 @@ void CBaseProp::Spawn( void ) #endif } +#ifdef EZ2 + PostPropDataPrecache(); +#endif + SetMoveType( MOVETYPE_PUSH ); m_takedamage = DAMAGE_NO; SetNextThink( TICK_NEVER_THINK ); @@ -831,6 +840,9 @@ bool CBreakableProp::HandleInteraction( int interactionType, void *data, CBaseCo // If we're an explosive barrel, DON'T explode violently! if ( HasInteraction( PROPINTER_PHYSGUN_BREAK_EXPLODE ) || +#ifdef EZ2 + HasInteraction( PROPINTER_PHYSGUN_BREAK_ZAP ) || +#endif HasInteraction( PROPINTER_PHYSGUN_FIRST_BREAK ) || HasInteraction( PROPINTER_FIRE_FLAMMABLE ) || HasInteraction( PROPINTER_FIRE_IGNITE_HALFHEALTH ) || @@ -1034,6 +1046,9 @@ void CBreakableProp::Spawn() if ( ( m_iHealth == 0 ) || ( !m_iNumBreakableChunks && !HasInteraction( PROPINTER_PHYSGUN_BREAK_EXPLODE ) && +#ifdef EZ2 + !HasInteraction( PROPINTER_PHYSGUN_BREAK_ZAP ) && +#endif !HasInteraction( PROPINTER_PHYSGUN_FIRST_BREAK ) && !HasInteraction( PROPINTER_FIRE_FLAMMABLE ) && !HasInteraction( PROPINTER_FIRE_IGNITE_HALFHEALTH ) && @@ -1049,6 +1064,9 @@ void CBreakableProp::Spawn() if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE ) { if ( HasInteraction( PROPINTER_PHYSGUN_BREAK_EXPLODE ) || +#ifdef EZ2 + HasInteraction( PROPINTER_PHYSGUN_BREAK_ZAP ) || +#endif HasInteraction( PROPINTER_FIRE_IGNITE_HALFHEALTH ) ) { // Exploding barrels, exploding gas cans @@ -1400,6 +1418,12 @@ int CBreakableProp::OnTakeDamage( const CTakeDamageInfo &inputInfo ) //----------------------------------------------------------------------------- void CBreakableProp::Event_Killed( const CTakeDamageInfo &info ) { +#ifdef MAPBASE_VSCRIPT + // False = Cheat death + if (ScriptDeathHook( const_cast(&info) ) == false) + return; +#endif + IPhysicsObject *pPhysics = VPhysicsGetObject(); if ( pPhysics && !pPhysics->IsMoveable() ) { @@ -1842,9 +1866,30 @@ void CBreakableProp::Precache() PrecacheScriptSound( STRING(m_iszPuntSound) ); } +#ifdef EZ2 + // Prop data isn't initialized until spawn, but this is needed for save/restore + PostPropDataPrecache(); +#endif + BaseClass::Precache(); } +#ifdef EZ2 +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBreakableProp::PostPropDataPrecache( void ) +{ + if (HasInteraction( PROPINTER_PHYSGUN_BREAK_ZAP )) + { + PrecacheScriptSound( "VortBarrel.Burst" ); + PrecacheParticleSystem( "vortbarrel_explode" ); + } + + BaseClass::PostPropDataPrecache(); +} +#endif + // Get the root physics object from which all broken pieces will // derive their positions and velocities IPhysicsObject *CBreakableProp::GetRootPhysicsObjectForBreak() @@ -1953,6 +1998,17 @@ void CBreakableProp::Break( CBaseEntity *pBreaker, const CTakeDamageInfo &info ) 0.0f, this ); EmitSound("PropaneTank.Burst"); } +#ifdef EZ2 + else if (HasInteraction( PROPINTER_PHYSGUN_BREAK_ZAP )) + { + ExplosionCreate( origin, angles, pAttacker, m_explodeDamage, m_explodeRadius, + SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE | SF_ENVEXPLOSION_NOFIREBALL | SF_ENVEXPLOSION_NOPARTICLES | SF_ENVEXPLOSION_NOSOUND, + 0.0f, this, DMG_SHOCK | DMG_BLAST ); + + DispatchParticleEffect( "vortbarrel_explode", GetAbsOrigin(), GetAbsAngles() ); + EmitSound( "VortBarrel.Burst" ); + } +#endif else { float flScale = GetModelScale(); @@ -2052,6 +2108,31 @@ void CBreakableProp::Break( CBaseEntity *pBreaker, const CTakeDamageInfo &info ) } } } +#ifdef EZ2 + else if (HasInteraction( PROPINTER_PHYSGUN_BREAK_ZAP )) + { + if ( bExploded == false ) + { + ExplosionCreate( origin, angles, pAttacker, m_explodeDamage, m_explodeRadius, + SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE | SF_ENVEXPLOSION_NOFIREBALL | SF_ENVEXPLOSION_NOPARTICLES | SF_ENVEXPLOSION_NOSOUND, + 0.0f, this, DMG_SHOCK | DMG_BLAST ); + + DispatchParticleEffect( "vortbarrel_explode", GetAbsOrigin(), GetAbsAngles() ); + EmitSound( "VortBarrel.Burst" ); + } + + // Find and boogie all dead NPCs within the radius + // TODO: Boogie ragdolls directly? That means ragdolls already on the ground would boogie + CBaseEntity *pEntity = NULL; + for ( CEntitySphereQuery sphere( origin, m_explodeRadius ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() ) + { + if( pEntity && !pEntity->IsEFlagSet( EFL_NO_MEGAPHYSCANNON_RAGDOLL ) && pEntity->MyCombatCharacterPointer() && pEntity->MyCombatCharacterPointer()->m_hDeathRagdoll ) + { + CRagdollBoogie::Create( pEntity->MyCombatCharacterPointer()->m_hDeathRagdoll, 200, gpGlobals->curtime, 4.0f, SF_RAGDOLL_BOOGIE_ELECTRICAL, &g_vecVortBarrelBoogieColor ); + } + } + } +#endif #ifndef HL2MP UTIL_Remove( this ); @@ -3083,6 +3164,7 @@ BEGIN_DATADESC( CPhysicsProp ) DEFINE_OUTPUT( m_MotionEnabled, "OnMotionEnabled" ), DEFINE_OUTPUT( m_OnPhysGunPickup, "OnPhysGunPickup" ), DEFINE_OUTPUT( m_OnPhysGunOnlyPickup, "OnPhysGunOnlyPickup" ), + DEFINE_OUTPUT( m_OnPhysGunPull, "OnPhysGunPull" ), DEFINE_OUTPUT( m_OnPhysGunPunt, "OnPhysGunPunt" ), DEFINE_OUTPUT( m_OnPhysGunDrop, "OnPhysGunDrop" ), DEFINE_OUTPUT( m_OnPlayerUse, "OnPlayerUse" ), @@ -3453,6 +3535,13 @@ void CPhysicsProp::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t r CheckRemoveRagdolls(); } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPhysicsProp::OnPhysGunPull( CBasePlayer* pPhysGunUser ) { + m_OnPhysGunPull.FireOutput(pPhysGunUser, this); +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -4269,6 +4358,12 @@ BEGIN_DATADESC(CBasePropDoor) #ifdef MAPBASE DEFINE_INPUTFUNC(FIELD_VOID, "AllowPlayerUse", InputAllowPlayerUse), DEFINE_INPUTFUNC(FIELD_VOID, "DisallowPlayerUse", InputDisallowPlayerUse), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetFullyOpenSound", InputSetFullyOpenSound ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetFullyClosedSound", InputSetFullyClosedSound ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetMovingSound", InputSetMovingSound ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetLockedSound", InputSetLockedSound ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetUnlockedSound", InputSetUnlockedSound ), #endif DEFINE_OUTPUT(m_OnBlockedOpening, "OnBlockedOpening"), @@ -4296,6 +4391,34 @@ END_DATADESC() IMPLEMENT_SERVERCLASS_ST(CBasePropDoor, DT_BasePropDoor) END_SEND_TABLE() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CBasePropDoor, CBaseAnimating, "The base class used by prop doors, such as prop_door_rotating." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptIsDoorOpen, "IsDoorOpen", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsDoorAjar, "IsDoorAjar", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsDoorOpening, "IsDoorOpening", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsDoorClosed, "IsDoorClosed", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsDoorClosing, "IsDoorClosing", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsDoorLocked, "IsDoorLocked", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsDoorBlocked, "IsDoorBlocked", "" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetActivator, "GetActivator", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetDoorList, "GetDoorList", "Get connected door entity by index." ) + DEFINE_SCRIPTFUNC( GetDoorListCount, "Get number of connected doors." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFullyOpenSound, "GetFullyOpenSound", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFullyClosedSound, "GetFullyClosedSound", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetMovingSound, "GetMovingSound", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetLockedSound, "GetLockedSound", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetUnlockedSound, "GetUnlockedSound", "" ) + + DEFINE_SCRIPTFUNC( DoorCanClose, "Return true if the door has room to close. Boolean is for whether or not this is an automatic close and not manually triggered by someone." ) + DEFINE_SCRIPTFUNC( DoorCanOpen, "Return true if there are other doors connected to this one." ) + DEFINE_SCRIPTFUNC( HasSlaves, "" ) + +END_SCRIPTDESC(); +#endif + CBasePropDoor::CBasePropDoor( void ) { m_hMaster = NULL; @@ -4769,6 +4892,54 @@ void CBasePropDoor::InputOpenAwayFrom(inputdata_t &inputdata) } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePropDoor::InputSetFullyOpenSound( inputdata_t &inputdata ) +{ + m_SoundOpen = inputdata.value.StringID(); + PrecacheScriptSound( STRING( m_SoundOpen ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePropDoor::InputSetFullyClosedSound( inputdata_t &inputdata ) +{ + m_SoundClose = inputdata.value.StringID(); + PrecacheScriptSound( STRING( m_SoundClose ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePropDoor::InputSetMovingSound( inputdata_t &inputdata ) +{ + m_SoundMoving = inputdata.value.StringID(); + PrecacheScriptSound( STRING( m_SoundMoving ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePropDoor::InputSetLockedSound( inputdata_t &inputdata ) +{ + m_ls.sLockedSound = inputdata.value.StringID(); + PrecacheScriptSound( STRING( m_ls.sLockedSound ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePropDoor::InputSetUnlockedSound( inputdata_t &inputdata ) +{ + m_ls.sUnlockedSound = inputdata.value.StringID(); + PrecacheScriptSound( STRING( m_ls.sUnlockedSound ) ); +} +#endif + + //----------------------------------------------------------------------------- // Purpose: // diff --git a/sp/src/game/server/props.h b/sp/src/game/server/props.h index b9e10dd8aae..09d29f1d861 100644 --- a/sp/src/game/server/props.h +++ b/sp/src/game/server/props.h @@ -44,6 +44,10 @@ class CBaseProp : public CBaseAnimating // Attempt to replace a dynamic_cast virtual bool IsPropPhysics() { return false; } #endif + +#ifdef EZ2 + virtual void PostPropDataPrecache( void ) {} +#endif }; @@ -61,6 +65,9 @@ class CBreakableProp : public CBaseProp, public IBreakableWithPropData, public C virtual void Spawn(); virtual void Precache(); +#ifdef EZ2 + virtual void PostPropDataPrecache( void ); +#endif virtual float GetAutoAimRadius() { return 24.0f; } #ifdef MAPBASE @@ -426,6 +433,7 @@ class CPhysicsProp : public CBreakableProp void EnableMotion( void ); bool CanBePickedUpByPhyscannon( void ); void OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ); + void OnPhysGunPull( CBasePlayer *pPhysGunUser ); void OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t reason ); bool GetPropDataAngles( const char *pKeyName, QAngle &vecAngles ); @@ -459,6 +467,7 @@ class CPhysicsProp : public CBreakableProp COutputEvent m_OnPhysGunPickup; COutputEvent m_OnPhysGunPunt; COutputEvent m_OnPhysGunOnlyPickup; + COutputEvent m_OnPhysGunPull; COutputEvent m_OnPhysGunDrop; COutputEvent m_OnPlayerUse; COutputEvent m_OnPlayerPickup; diff --git a/sp/src/game/server/sceneentity.cpp b/sp/src/game/server/sceneentity.cpp index ab70e1974fa..06f8f8fdf68 100644 --- a/sp/src/game/server/sceneentity.cpp +++ b/sp/src/game/server/sceneentity.cpp @@ -5298,6 +5298,23 @@ float GetSceneDuration( char const *pszScene ) { msecs = cachedData.msecs; } +#ifdef MAPBASE + else + { + // Raw scene file support + void *pBuffer = NULL; + if (filesystem->ReadFileEx( pszScene, "MOD", &pBuffer, true )) + { + g_TokenProcessor.SetBuffer((char*)pBuffer); + CChoreoScene *pScene = ChoreoLoadScene( pszScene, NULL, &g_TokenProcessor, LocalScene_Printf ); + g_TokenProcessor.SetBuffer(NULL); + + float flDuration = pScene->GetDuration(); + delete pScene; + return flDuration; + } + } +#endif return (float)msecs * 0.001f; } diff --git a/sp/src/game/server/scripted.cpp b/sp/src/game/server/scripted.cpp index c176cf905d6..f64d935d831 100644 --- a/sp/src/game/server/scripted.cpp +++ b/sp/src/game/server/scripted.cpp @@ -113,6 +113,9 @@ BEGIN_DATADESC( CAI_ScriptedSequence ) DEFINE_INPUTFUNC( FIELD_VOID, "MoveToPosition", InputMoveToPosition ), DEFINE_INPUTFUNC( FIELD_VOID, "BeginSequence", InputBeginSequence ), DEFINE_INPUTFUNC( FIELD_VOID, "CancelSequence", InputCancelSequence ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "StopActionLoop", InputStopActionLoop ), +#endif DEFINE_KEYFIELD( m_iPlayerDeathBehavior, FIELD_INTEGER, "onplayerdeath" ), DEFINE_INPUTFUNC( FIELD_VOID, "ScriptPlayerDeath", InputScriptPlayerDeath ), @@ -382,6 +385,14 @@ void CAI_ScriptedSequence::InputSetTarget( inputdata_t &inputdata ) m_iszEntity = AllocPooledString(inputdata.value.String()); m_hTargetEnt = NULL; } + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_ScriptedSequence::InputStopActionLoop( inputdata_t &inputdata ) +{ + StopActionLoop( false ); +} #endif diff --git a/sp/src/game/server/scripted.h b/sp/src/game/server/scripted.h index 7b1d825441f..82aae6b3605 100644 --- a/sp/src/game/server/scripted.h +++ b/sp/src/game/server/scripted.h @@ -112,6 +112,7 @@ class CAI_ScriptedSequence : public CBaseEntity void InputCancelSequence( inputdata_t &inputdata ); void InputMoveToPosition( inputdata_t &inputdata ); #ifdef MAPBASE + void InputStopActionLoop( inputdata_t &inputdata ); void InputSetTarget( inputdata_t &inputdata ); #endif diff --git a/sp/src/game/server/server_ez2.vpc b/sp/src/game/server/server_ez2.vpc index 1f53853544b..eca2e85e250 100644 --- a/sp/src/game/server/server_ez2.vpc +++ b/sp/src/game/server/server_ez2.vpc @@ -89,8 +89,11 @@ $Project "Server (Entropy Zero 2)" $File "ez2\npc_zombigaunt.h" $File "ez2\npc_zombigaunt.cpp" $File "ez2\prop_command_point.cpp" + $File "ez2\weapon_oicw.cpp" $File "ez2\weapon_displacer_pistol.h" $File "ez2\weapon_displacer_pistol.cpp" + + $File "$SRCDIR\game\shared\workshop_mount.cpp" [!$NO_STEAM] } $File "ai_eventresponse.cpp" $File "ai_eventresponse.h" diff --git a/sp/src/game/server/sound.cpp b/sp/src/game/server/sound.cpp index 4ee570c1934..a948283874f 100644 --- a/sp/src/game/server/sound.cpp +++ b/sp/src/game/server/sound.cpp @@ -938,7 +938,7 @@ void CAmbientGeneric::SendSound( SoundFlags_t flags) { #ifdef MAPBASE int iFlags = flags != SND_STOP ? ((int)flags | m_iSoundFlags) : flags; - char *szSoundFile = (char *)STRING( m_iszSound ); + const char *szSoundFile = STRING( m_iszSound ); CBaseEntity* pSoundSource = m_hSoundSource; if ( pSoundSource ) { diff --git a/sp/src/game/server/subs.cpp b/sp/src/game/server/subs.cpp index 0a37e4c126e..53ba8f06c65 100644 --- a/sp/src/game/server/subs.cpp +++ b/sp/src/game/server/subs.cpp @@ -22,10 +22,10 @@ void CPointEntity::Spawn( void ) } -class CNullEntity : public CBaseEntity +class CNullEntity : public CServerOnlyEntity { public: - DECLARE_CLASS( CNullEntity, CBaseEntity ); + DECLARE_CLASS( CNullEntity, CServerOnlyEntity ); void Spawn( void ); }; diff --git a/sp/src/game/server/triggers.cpp b/sp/src/game/server/triggers.cpp index db294b4a5d8..d92e2f04c22 100644 --- a/sp/src/game/server/triggers.cpp +++ b/sp/src/game/server/triggers.cpp @@ -51,6 +51,7 @@ #define DEBUG_TRANSITIONS_VERBOSE 2 ConVar g_debug_transitions( "g_debug_transitions", "0", FCVAR_NONE, "Set to 1 and restart the map to be warned if the map has no trigger_transition volumes. Set to 2 to see a dump of all entities & associated results during a transition." ); +ConVar noclip_changelevel("noclip_changelevel", "0", FCVAR_CHEAT); // Global list of triggers that care about weapon fire // Doesn't need saving, the triggers re-add themselves on restore. @@ -1890,7 +1891,8 @@ void CChangeLevel::TouchChangeLevel( CBaseEntity *pOther ) return; } - if ( !pPlayer->IsInAVehicle() && pPlayer->GetMoveType() == MOVETYPE_NOCLIP ) + + if ( !pPlayer->IsInAVehicle() && pPlayer->GetMoveType() == MOVETYPE_NOCLIP && !noclip_changelevel.GetBool()) { DevMsg("In level transition: %s %s\n", st_szNextMap, st_szNextSpot ); return; diff --git a/sp/src/game/server/vguiscreen.cpp b/sp/src/game/server/vguiscreen.cpp index e049da22b53..e46afbf585b 100644 --- a/sp/src/game/server/vguiscreen.cpp +++ b/sp/src/game/server/vguiscreen.cpp @@ -35,21 +35,21 @@ PRECACHE_REGISTER( vgui_screen ); //----------------------------------------------------------------------------- // Save/load //----------------------------------------------------------------------------- -BEGIN_DATADESC( CVGuiScreen ) +BEGIN_DATADESC(CVGuiScreen) - DEFINE_CUSTOM_FIELD( m_nPanelName, &g_VguiScreenStringOps ), - DEFINE_FIELD( m_nAttachmentIndex, FIELD_INTEGER ), +DEFINE_CUSTOM_FIELD(m_nPanelName, &g_VguiScreenStringOps), +DEFINE_FIELD(m_nAttachmentIndex, FIELD_INTEGER), // DEFINE_FIELD( m_nOverlayMaterial, FIELD_INTEGER ), - DEFINE_FIELD( m_fScreenFlags, FIELD_INTEGER ), - DEFINE_KEYFIELD( m_flWidth, FIELD_FLOAT, "width" ), - DEFINE_KEYFIELD( m_flHeight, FIELD_FLOAT, "height" ), - DEFINE_KEYFIELD( m_strOverlayMaterial, FIELD_STRING, "overlaymaterial" ), - DEFINE_FIELD( m_hPlayerOwner, FIELD_EHANDLE ), +DEFINE_FIELD(m_fScreenFlags, FIELD_INTEGER), +DEFINE_KEYFIELD(m_flWidth, FIELD_FLOAT, "width"), +DEFINE_KEYFIELD(m_flHeight, FIELD_FLOAT, "height"), +DEFINE_KEYFIELD(m_strOverlayMaterial, FIELD_STRING, "overlaymaterial"), +DEFINE_FIELD(m_hPlayerOwner, FIELD_EHANDLE), - DEFINE_INPUTFUNC( FIELD_VOID, "SetActive", InputSetActive ), - DEFINE_INPUTFUNC( FIELD_VOID, "SetInactive", InputSetInactive ), +DEFINE_INPUTFUNC(FIELD_VOID, "SetActive", InputSetActive), +DEFINE_INPUTFUNC(FIELD_VOID, "SetInactive", InputSetInactive), -END_DATADESC() +END_DATADESC(); //----------------------------------------------------------------------------- @@ -75,6 +75,24 @@ bool CVGuiScreen::KeyValue( const char *szKeyName, const char *szValue ) *s = '\0'; } +#ifdef MAPBASE + // Named command outputs + if (szKeyName[0] == '~' && szKeyName[1]) + { + const char* pszOutputName = szKeyName + 1; + int i = m_PanelOutputs.Find(pszOutputName); + if (!m_PanelOutputs.IsValidIndex(i)) + { + auto pMem = new COutputEvent; + V_memset(pMem, 0, sizeof(COutputEvent)); + i = m_PanelOutputs.Insert(pszOutputName, pMem); + } + + m_PanelOutputs[i]->ParseEventAction(szValue); + return true; + } +#endif // MAPBASE + if ( FStrEq( szKeyName, "panelname" )) { SetPanelName( szValue ); @@ -158,6 +176,106 @@ void CVGuiScreen::OnRestore() BaseClass::OnRestore(); } +#ifdef MAPBASE +CVGuiScreen::~CVGuiScreen() +{ + m_PanelOutputs.PurgeAndDeleteElements(); +} + +int CVGuiScreen::Save(ISave& save) +{ +#if MAPBASE_VER_INT < 8000 + // HACKHACK: Until v8.0, mark this screen as using the new save system to prevent existing saves with vgui_screen from crashing + AddContext( "uses_new_save", "1" ); +#endif + + int status = BaseClass::Save(save); + if (!status) + return 0; + + const int iCount = m_PanelOutputs.Count(); + save.WriteInt(&iCount); + for (int i = 0; i < iCount; i++) + { + CBaseEntityOutput* output = m_PanelOutputs[i]; + const int nElems = output->NumberOfElements(); + save.WriteString(m_PanelOutputs.GetElementName(i)); + save.WriteInt(&nElems); + if (!output->Save(save)) + return 0; + } + + return status; +} + +int CVGuiScreen::Restore(IRestore& restore) +{ + int status = BaseClass::Restore(restore); + if (!status) + return 0; + +#if MAPBASE_VER_INT < 8000 + // HACKHACK: Until v8.0, mark this screen as using the new save system to prevent existing saves with vgui_screen from crashing + if (!HasContext( "uses_new_save", "1" )) + return status; +#endif + + const int iCount = restore.ReadInt(); + m_PanelOutputs.EnsureCapacity(iCount); + for (int i = 0; i < iCount; i++) + { + char cName[MAX_KEY]; + restore.ReadString(cName, MAX_KEY, 0); + const int iIndex = m_PanelOutputs.Insert(cName, new COutputEvent); + const int nElems = restore.ReadInt(); + if (!m_PanelOutputs[iIndex]->Restore(restore, nElems)) + return 0; + } + + return status; +} + +// Handle a command from the client-side vgui panel. +bool CVGuiScreen::HandleEntityCommand(CBasePlayer* pClient, KeyValues* pKeyValues) +{ +#if defined(HL2MP) // Enable this in multiplayer. + // Restrict to commands from our owning player. + if ((m_fScreenFlags & VGUI_SCREEN_ONLY_USABLE_BY_OWNER) && pClient != m_hPlayerOwner.Get()) + return false; +#endif + + // Give the owning entity a chance to handle the command. + if (GetOwnerEntity() && GetOwnerEntity()->HandleEntityCommand(pClient, pKeyValues)) + return true; + + // See if we have an output for this command. + const int i = m_PanelOutputs.Find(pKeyValues->GetString()); + if (m_PanelOutputs.IsValidIndex(i)) + { + variant_t Val; + Val.Set(FIELD_VOID, NULL); + m_PanelOutputs[i]->FireOutput(Val, pClient, this); + return true; + } + + return false; +} + +CBaseEntityOutput* CVGuiScreen::FindNamedOutput(const char* pszOutput) +{ + if (pszOutput && pszOutput[0] == '~' && pszOutput[1]) + { + const int i = m_PanelOutputs.Find(pszOutput + 1); + if (m_PanelOutputs.IsValidIndex(i)) + return m_PanelOutputs[i]; + + return NULL; + } + + return BaseClass::FindNamedOutput(pszOutput); +} +#endif // MAPBASE + void CVGuiScreen::SetAttachmentIndex( int nIndex ) { m_nAttachmentIndex = nIndex; diff --git a/sp/src/game/server/vguiscreen.h b/sp/src/game/server/vguiscreen.h index cf72091653d..557cacd7977 100644 --- a/sp/src/game/server/vguiscreen.h +++ b/sp/src/game/server/vguiscreen.h @@ -32,6 +32,15 @@ class CVGuiScreen : public CBaseEntity virtual void Activate(); virtual void OnRestore(); +#ifdef MAPBASE + ~CVGuiScreen(); + virtual int Save(ISave& save); + virtual int Restore(IRestore& restore); + + virtual bool HandleEntityCommand(CBasePlayer* pClient, KeyValues* pKeyValues); + virtual CBaseEntityOutput* FindNamedOutput(const char* pszOutput); +#endif // MAPBASE + const char *GetPanelName() const; // Sets the screen size + resolution @@ -75,6 +84,10 @@ class CVGuiScreen : public CBaseEntity CNetworkVar( int, m_fScreenFlags ); CNetworkVar( EHANDLE, m_hPlayerOwner ); +#ifdef MAPBASE + CUtlDict m_PanelOutputs; +#endif // MAPBASE + friend CVGuiScreen *CreateVGuiScreen( const char *pScreenClassname, const char *pScreenType, CBaseEntity *pAttachedTo, CBaseEntity *pOwner, int nAttachmentIndex ); }; diff --git a/sp/src/game/shared/SoundEmitterSystem.cpp b/sp/src/game/shared/SoundEmitterSystem.cpp index 6b10c6fead6..131639bd84d 100644 --- a/sp/src/game/shared/SoundEmitterSystem.cpp +++ b/sp/src/game/shared/SoundEmitterSystem.cpp @@ -1001,7 +1001,11 @@ class CSoundEmitterSystem : public CBaseGameSystem if ( duration ) { - *duration = enginesound->GetSoundDuration( pSample ); + if ( Q_stristr( pSample, ".mp3" ) ) { + *duration = 0; + } else { + *duration = enginesound->GetSoundDuration( pSample ); + } } TraceEmitSound( "EmitAmbientSound: Raw wave emitted '%s' (ent %i)\n", diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 8a60ed98986..7eebd09daae 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -525,6 +525,18 @@ bool CBaseCombatWeapon::UsesHands() const } #endif +#ifdef EZ2 +float CBaseCombatWeapon::GetDynamicScopeFOV() const +{ + return GetWpnData().m_flDynamicScopeFOV; +} + +float CBaseCombatWeapon::GetDynamicScopeSuitFOV() const +{ + return GetWpnData().m_flDynamicScopeSuitFOV; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/sp/src/game/shared/basecombatweapon_shared.h b/sp/src/game/shared/basecombatweapon_shared.h index f807d6e3119..e5e083ce2d3 100644 --- a/sp/src/game/shared/basecombatweapon_shared.h +++ b/sp/src/game/shared/basecombatweapon_shared.h @@ -427,6 +427,11 @@ class CBaseCombatWeapon : public BASECOMBATWEAPON_DERIVED_FROM virtual const char *GetDroppedModel( void ) const; bool UsesHands( void ) const; #endif +#ifdef EZ2 + float GetDynamicScopeFOV( void ) const; + float GetDynamicScopeSuitFOV( void ) const; + virtual bool IsDynamicScopeZoomed( void ) const { return false; } +#endif // derive this function if you mod uses encrypted weapon info files virtual const unsigned char *GetEncryptionKey( void ); diff --git a/sp/src/game/shared/baseentity_shared.cpp b/sp/src/game/shared/baseentity_shared.cpp index b2cd6bdc7a0..5f95761216e 100644 --- a/sp/src/game/shared/baseentity_shared.cpp +++ b/sp/src/game/shared/baseentity_shared.cpp @@ -2811,6 +2811,14 @@ HSCRIPT CBaseEntity::ScriptGetPhysicsObject( void ) return NULL; } +//----------------------------------------------------------------------------- +// Vscript: Gets the entity's physics object if it has one +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptPhysicsInitNormal( int nSolidType, int nSolidFlags, bool createAsleep ) +{ + VPhysicsInitNormal( (SolidType_t)nSolidType, nSolidFlags, createAsleep ); +} + #ifdef GAME_DLL #define SCRIPT_NEVER_THINK TICK_NEVER_THINK diff --git a/sp/src/game/shared/baseplayer_shared.cpp b/sp/src/game/shared/baseplayer_shared.cpp index 5ad7d453932..9296bfcfad4 100644 --- a/sp/src/game/shared/baseplayer_shared.cpp +++ b/sp/src/game/shared/baseplayer_shared.cpp @@ -1069,6 +1069,10 @@ float IntervalDistance( float x, float x0, float x1 ) return 0; } +#if !defined(CLIENT_DLL) && defined(MAPBASE_VSCRIPT) +extern ScriptHook_t g_Hook_FindUseEntity; +#endif + CBaseEntity *CBasePlayer::FindUseEntity() { Vector forward, up; @@ -1160,7 +1164,24 @@ CBaseEntity *CBasePlayer::FindUseEntity() // if this is directly under the cursor just return it now if ( i == 0 ) + { +#if !defined(CLIENT_DLL) && defined(MAPBASE_VSCRIPT) + if (m_ScriptScope.IsInitialized() && g_Hook_FindUseEntity.CanRunInScope( m_ScriptScope )) + { + // entity, is_radius + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ToHScript( pNearest ), false }; + if (g_Hook_FindUseEntity.Call( m_ScriptScope, &functionReturn, args )) + { + pObject = ToEnt( functionReturn.m_hScript ); + pNearest = pObject; + } + } + + if (pObject) +#endif return pObject; + } } } } @@ -1245,6 +1266,19 @@ CBaseEntity *CBasePlayer::FindUseEntity() { pNearest = DoubleCheckUseNPC( pNearest, searchCenter, forward ); } + +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_FindUseEntity.CanRunInScope(m_ScriptScope)) + { + // entity, is_radius + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ToHScript( pNearest ), true }; + if (g_Hook_FindUseEntity.Call( m_ScriptScope, &functionReturn, args )) + { + pNearest = ToEnt( functionReturn.m_hScript ); + } + } +#endif if ( sv_debug_player_use.GetBool() ) { diff --git a/sp/src/game/shared/baseviewmodel_shared.cpp b/sp/src/game/shared/baseviewmodel_shared.cpp index 5f9c0f10c64..d6124263572 100644 --- a/sp/src/game/shared/baseviewmodel_shared.cpp +++ b/sp/src/game/shared/baseviewmodel_shared.cpp @@ -581,6 +581,15 @@ void CBaseViewModel::CalcViewModelLag( Vector& origin, QAngle& angles, QAngle& o angles = vOriginalAngles; } +#ifdef EZ2 + CBaseCombatWeapon *pWeapon = m_hWeapon.Get(); + if (pWeapon && pWeapon->GetDynamicScopeFOV() != 0.0f && pWeapon->IsDynamicScopeZoomed()) + { + // Ignore the below + return; + } +#endif + //FIXME: These are the old settings that caused too many exposed polys on some models VectorMA( origin, -pitch * 0.035f, forward, origin ); VectorMA( origin, -pitch * 0.03f, right, origin ); diff --git a/sp/src/game/shared/gamerules.cpp b/sp/src/game/shared/gamerules.cpp index 3e9268407c8..4d308e5a920 100644 --- a/sp/src/game/shared/gamerules.cpp +++ b/sp/src/game/shared/gamerules.cpp @@ -963,3 +963,28 @@ CTacticalMissionManager *CGameRules::TacticalMissionManagerFactory( void ) } #endif + +#ifdef MAPBASE +void CGameRules::ClientCommandKeyValues(edict_t* pEntity, KeyValues* pKeyValues) +{ +#ifndef CLIENT_DLL + static int s_nEntityCommandSymbol = KeyValues::CallGetSymbolForString("EntityCommand"); + static int s_nEntIndexSymbol = KeyValues::CallGetSymbolForString("entindex"); + static int s_nCommandDataSymbol = KeyValues::CallGetSymbolForString("command_data"); + + CBasePlayer* pPlayer = (CBasePlayer*)GetContainingEntity(pEntity); + if (!pPlayer) + return; + + if (pKeyValues->GetNameSymbol() == s_nEntityCommandSymbol) + { + CBaseEntity* pEntity = CBaseEntity::Instance(pKeyValues->GetInt(s_nEntIndexSymbol)); + KeyValues* pkvCommand = pKeyValues->FindKey(s_nCommandDataSymbol); + if (pEntity && pkvCommand) + { + pEntity->HandleEntityCommand(pPlayer, pkvCommand); + } + } +#endif // GAME_DLL +} +#endif // MAPBASE diff --git a/sp/src/game/shared/gamerules.h b/sp/src/game/shared/gamerules.h index 2754b92b5f0..0db276960a4 100644 --- a/sp/src/game/shared/gamerules.h +++ b/sp/src/game/shared/gamerules.h @@ -178,7 +178,12 @@ abstract_class CGameRules : public CAutoGameSystemPerFrame //Allow thirdperson camera. virtual bool AllowThirdPersonCamera( void ) { return false; } - virtual void ClientCommandKeyValues( edict_t *pEntity, KeyValues *pKeyValues ) {} +#ifdef MAPBASE + virtual void ClientCommandKeyValues(edict_t* pEntity, KeyValues* pKeyValues); +#else + virtual void ClientCommandKeyValues(edict_t* pEntity, KeyValues* pKeyValues) {} +#endif // MAPBASE + // IsConnectedUserInfoChangeAllowed allows the clients to change // cvars with the FCVAR_NOT_CONNECTED rule if it returns true diff --git a/sp/src/game/shared/hl2/basehlcombatweapon_shared.cpp b/sp/src/game/shared/hl2/basehlcombatweapon_shared.cpp index b9ba06cf433..67dcaab8f53 100644 --- a/sp/src/game/shared/hl2/basehlcombatweapon_shared.cpp +++ b/sp/src/game/shared/hl2/basehlcombatweapon_shared.cpp @@ -8,6 +8,7 @@ #include "basehlcombatweapon_shared.h" #if defined(EZ2) && defined(GAME_DLL) #include "npcevent.h" +#include "in_buttons.h" #endif #include "hl2_player_shared.h" @@ -22,8 +23,14 @@ IMPLEMENT_NETWORKCLASS_ALIASED( BaseHLCombatWeapon , DT_BaseHLCombatWeapon ) BEGIN_NETWORK_TABLE( CBaseHLCombatWeapon , DT_BaseHLCombatWeapon ) #if !defined( CLIENT_DLL ) // SendPropInt( SENDINFO( m_bReflectViewModelAnimations ), 1, SPROP_UNSIGNED ), +#ifdef EZ2 + SendPropEHandle( SENDINFO( m_hLeftHandGun ) ), +#endif #else // RecvPropInt( RECVINFO( m_bReflectViewModelAnimations ) ), +#ifdef EZ2 + RecvPropEHandle( RECVINFO( m_hLeftHandGun ) ), +#endif #endif END_NETWORK_TABLE() @@ -43,6 +50,11 @@ BEGIN_DATADESC( CBaseHLCombatWeapon ) DEFINE_FIELD( m_iPrimaryAttacks, FIELD_INTEGER ), DEFINE_FIELD( m_iSecondaryAttacks, FIELD_INTEGER ), +#ifdef EZ2 + DEFINE_INPUTFUNC( FIELD_VOID, "CreateLeftHandGun", InputCreateLeftHandGun ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetLeftHandGun", InputSetLeftHandGun ), +#endif + END_DATADESC() #endif @@ -250,7 +262,77 @@ void CBaseHLCombatWeapon::WeaponIdle( void ) } } -#if defined(EZ2) && defined(GAME_DLL) +#ifdef EZ2 +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseHLCombatWeapon::ItemPostFrame( void ) +{ +#ifdef GAME_DLL + // When dual wielding, secondary attack is synonymous with primary attack + if (IsDualWielding() && DualWieldOverridesSecondary()) + { + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + if ( pOwner != NULL ) + { + if (pOwner->m_nButtons & IN_ATTACK2) + { + pOwner->m_nButtons |= IN_ATTACK; + pOwner->m_nButtons &= ~IN_ATTACK2; + } + + if (pOwner->m_afButtonPressed & IN_ATTACK2) + { + pOwner->m_afButtonPressed |= IN_ATTACK; + pOwner->m_afButtonPressed &= ~IN_ATTACK2; + } + + if (pOwner->m_afButtonReleased & IN_ATTACK2) + { + pOwner->m_afButtonReleased |= IN_ATTACK; + pOwner->m_afButtonReleased &= ~IN_ATTACK2; + } + } + } +#endif + + BaseClass::ItemPostFrame(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CBaseHLCombatWeapon::GetViewModel( int viewmodelindex ) const +{ + if (GetLeftHandGun() && GetWpnData().szViewModelDual[0]) + return GetWpnData().szViewModelDual; + + return BaseClass::GetViewModel( viewmodelindex ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CHudTexture const *CBaseHLCombatWeapon::GetSpriteActive( void ) const +{ + if (GetLeftHandGun() && GetWpnData().iconActiveDual) + return GetWpnData().iconActiveDual; + + return GetWpnData().iconActive; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CHudTexture const *CBaseHLCombatWeapon::GetSpriteInactive( void ) const +{ + if (GetLeftHandGun() && GetWpnData().iconInactiveDual) + return GetWpnData().iconInactiveDual; + + return GetWpnData().iconInactive; +} + +#ifdef GAME_DLL // HACKHACK: Draws directly from npc_assassin private animevents extern int AE_PISTOL_FIRE_LEFT; extern int AE_PISTOL_FIRE_RIGHT; @@ -412,6 +494,148 @@ Activity CBaseHLCombatWeapon::ActivityOverride( Activity baseAct, bool *pRequire return BaseClass::ActivityOverride( baseAct, pRequired ); } + +//----------------------------------------------------------------------------- +// Purpose: Drop/throw the weapon with the given velocity. +//----------------------------------------------------------------------------- +void CBaseHLCombatWeapon::Drop( const Vector &vecVelocity ) +{ + if (GetLeftHandGun()) + { + m_iClip1 /= 2; + m_iClip2 /= 2; + } + + CBaseCombatCharacter *pOwner = GetOwner(); + + BaseClass::Drop( vecVelocity ); + + if (pOwner && pOwner->IsPlayer()) + { + // Spawn left hand gun when regular one is dropped + if (GetLeftHandGun()) + { + // Drop the fake gun + CBaseEntity *pRealGun = CreateNoSpawn( GetClassname(), GetAbsOrigin() - Vector( 0, 0, BoundingRadius() ), GetAbsAngles(), this ); + DispatchSpawn( pRealGun ); + + if (pRealGun) + { + if (pRealGun->VPhysicsGetObject()) + { + // Copy velocity + Vector vecVelocity; + AngularImpulse vecAngVelocity; + VPhysicsGetObject()->GetVelocity( &vecVelocity, &vecAngVelocity ); + pRealGun->VPhysicsGetObject()->SetVelocity( &vecVelocity, &vecAngVelocity ); + } + + pRealGun->MyCombatWeaponPointer()->m_iClip1 = m_iClip1; + pRealGun->MyCombatWeaponPointer()->m_iClip2 = m_iClip2; + + // Act as if it was dropped the same way + pRealGun->SetThink( &CBaseCombatWeapon::SetPickupTouch ); + pRealGun->SetTouch( NULL ); + pRealGun->SetNextThink( gpGlobals->curtime + 1.0f ); + } + + UTIL_Remove( GetLeftHandGun() ); + + SetLeftHandGun( NULL ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CBaseAnimating *CBaseHLCombatWeapon::CreateLeftHandGun() +{ + if (!GetOwner()) + { + Warning( "%s cannot create left hand gun because it has no owner\n", GetDebugName() ); + return NULL; + } + + if (!CanDualWield()) + { + Warning( "%s not capable of dual wielding\n", GetDebugName() ); + return NULL; + } + + if (GetLeftHandGun()) + { + Warning("%s already has a left-handed weapon (%s)\n", GetDebugName(), GetLeftHandGun()->GetDebugName()); + return NULL; + } + + // Create a fake second gun + CBaseEntity *pEnt = CBaseEntity::CreateNoSpawn( "prop_dynamic_override", this->GetLocalOrigin(), this->GetLocalAngles(), this ); + if (pEnt) + { + char szLeftModel[MAX_PATH]; + if (GetWpnData().szWorldModelDual[0]) + { + V_strncpy( szLeftModel, GetWpnData().szWorldModelDual, sizeof( szLeftModel ) ); + } + else + { + // If there is no specific worldmodel, then just try adding "_left" to the end of the model name + V_StripExtension( GetWorldModel(), szLeftModel, sizeof( szLeftModel ) ); + V_strncat( szLeftModel, "_left.mdl", sizeof( szLeftModel ) ); + } + + pEnt->SetModelName( MAKE_STRING( szLeftModel ) ); + pEnt->SetRenderMode( kRenderTransColor ); + DispatchSpawn( pEnt ); + pEnt->FollowEntity( GetOwner(), true ); + pEnt->SetOwnerEntity( this ); + pEnt->AddEffects( EF_NOSHADOW ); + + CBaseAnimating *pAnimating = static_cast(pEnt); + SetLeftHandGun( pAnimating ); + + if (GetOwner()->IsPlayer()) + { + SetModel( GetViewModel() ); + + if (GetOwner()->GetActiveWeapon() == this) + { + // Play the first draw animation and re-deploy the model + m_bFirstDraw = true; + DefaultDeploy( (char *)GetViewModel(), (char *)GetWorldModel(), GetDrawActivity(), (char *)GetAnimPrefix() ); + } + } + + return pAnimating; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseHLCombatWeapon::InputCreateLeftHandGun( inputdata_t &inputdata ) +{ + CreateLeftHandGun(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseHLCombatWeapon::InputSetLeftHandGun( inputdata_t &inputdata ) +{ + CBaseEntity *pGun = inputdata.value.Entity(); + if (!pGun || !pGun->GetBaseAnimating()) + { + SetLeftHandGun( NULL ); + return; + } + + SetLeftHandGun( pGun->GetBaseAnimating() ); +} +#endif #endif float g_lateralBob; diff --git a/sp/src/game/shared/hl2/basehlcombatweapon_shared.h b/sp/src/game/shared/hl2/basehlcombatweapon_shared.h index 06b71663fba..670eae0c472 100644 --- a/sp/src/game/shared/hl2/basehlcombatweapon_shared.h +++ b/sp/src/game/shared/hl2/basehlcombatweapon_shared.h @@ -53,30 +53,55 @@ class CBaseHLCombatWeapon : public CBaseCombatWeapon virtual void ItemHolsterFrame( void ); -#if defined(EZ2) && defined(GAME_DLL) - virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); - virtual void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ) {} - virtual Activity ActivityOverride( Activity baseAct, bool *pRequired ); +#ifdef EZ2 + // + // Dual wielding + // + virtual void ItemPostFrame( void ); + virtual const char *GetViewModel( int viewmodelindex = 0 ) const; + + virtual CHudTexture const *GetSpriteActive( void ) const; + virtual CHudTexture const *GetSpriteInactive( void ) const; - // Dual wielding stubs (used for the Combine assassin) virtual bool CanDualWield() const { return false; } - virtual CBaseAnimating *GetLeftHandGun() const { return NULL; } - virtual void SetLeftHandGun( CBaseAnimating *pGun ) {} + virtual bool DualWieldOverridesSecondary() const { return true; } + bool IsDualWielding() const { return m_hLeftHandGun != NULL; } + + CBaseAnimating *GetLeftHandGun() const { return m_hLeftHandGun; } + void SetLeftHandGun( CBaseAnimating *pGun ) { m_hLeftHandGun = pGun; } int GetMaxClip1( void ) const { - return (GetLeftHandGun() != NULL) ? BaseClass::GetMaxClip1() * 2 : BaseClass::GetMaxClip1(); + return IsDualWielding() ? BaseClass::GetMaxClip1() * 2 : BaseClass::GetMaxClip1(); } int GetDefaultClip1( void ) const { - return (GetLeftHandGun() != NULL) ? BaseClass::GetDefaultClip1() * 2 : BaseClass::GetDefaultClip1(); + return IsDualWielding() ? BaseClass::GetDefaultClip1() * 2 : BaseClass::GetDefaultClip1(); } +#ifdef GAME_DLL + virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); + virtual void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ) {} + virtual Activity ActivityOverride( Activity baseAct, bool *pRequired ); + + virtual void Drop( const Vector &vecVelocity ); + + CBaseAnimating *CreateLeftHandGun(); + void InputCreateLeftHandGun( inputdata_t &inputdata ); + void InputSetLeftHandGun( inputdata_t &inputdata ); + +protected: + + CNetworkHandle( CBaseAnimating, m_hLeftHandGun ); + private: static acttable_t m_dual_acttable[]; public: +#else + CHandle m_hLeftHandGun; +#endif #endif int m_iPrimaryAttacks; // # of primary attacks performed with this weapon diff --git a/sp/src/game/shared/hl2mp/weapon_stunstick.cpp b/sp/src/game/shared/hl2mp/weapon_stunstick.cpp index 0cf9b49f014..660d2bc5876 100644 --- a/sp/src/game/shared/hl2mp/weapon_stunstick.cpp +++ b/sp/src/game/shared/hl2mp/weapon_stunstick.cpp @@ -512,7 +512,7 @@ void CWeaponStunStick::Hit( trace_t &traceHit, Activity nHitActivity, bool bIsSe // If the hit object is an NPC, and that NPC is now dead - become a server ragdoll and electrify! CAI_BaseNPC * pNPC = pHitEntity->MyNPCPointer(); - if ( flDamage > flBaseDamage && pHitEntity->IsNPC() && pNPC != NULL && pNPC->CanBecomeServerRagdoll() && !pNPC->IsEFlagSet( EFL_NO_MEGAPHYSCANNON_RAGDOLL ) && pNPC->m_iHealth - info.GetDamage() <= 0.0f) + if ( flDamage > flBaseDamage && pHitEntity->IsNPC() && pNPC != NULL && pNPC->CanBecomeServerRagdoll() && pNPC->PassesDamageFilter(info) && !pNPC->IsEFlagSet( EFL_NO_MEGAPHYSCANNON_RAGDOLL ) && pNPC->m_iHealth - info.GetDamage() <= 0.0f && pNPC->Classify() != CLASS_BARNACLE) { pNPC->BecomeRagdollBoogie( GetOwner(), info.GetDamageForce(), 5.0f, SF_RAGDOLL_BOOGIE_ELECTRICAL ); } diff --git a/sp/src/game/shared/in_buttons.h b/sp/src/game/shared/in_buttons.h index 870961f8fab..74e2ca5faa5 100644 --- a/sp/src/game/shared/in_buttons.h +++ b/sp/src/game/shared/in_buttons.h @@ -43,6 +43,10 @@ #define IN_GRENADE2 (1 << 24) // grenade 2 #define IN_ATTACK3 (1 << 25) +#ifdef MAPBASE +#define IN_VGUIMODE (1 << 26) +#endif // MAPBASE + #ifdef VGUI_SCREEN_FIX #define IN_VALIDVGUIINPUT (1 << 23) //bitflag for vgui fix #endif diff --git a/sp/src/game/shared/mapbase/mapbase_shared.cpp b/sp/src/game/shared/mapbase/mapbase_shared.cpp index 500c5e2c394..ba0ff5dfb7b 100644 --- a/sp/src/game/shared/mapbase/mapbase_shared.cpp +++ b/sp/src/game/shared/mapbase/mapbase_shared.cpp @@ -254,10 +254,19 @@ class CMapbaseSystem : public CAutoGameSystem } #ifdef GAME_DLL +#ifdef EZ2 + Q_strncpy( g_szDefaultPlayerModel, gameinfo->GetString( "player_default_model", "models/bad_cop.mdl" ), sizeof( g_szDefaultPlayerModel ) ); +#else Q_strncpy( g_szDefaultPlayerModel, gameinfo->GetString( "player_default_model", "models/player.mdl" ), sizeof( g_szDefaultPlayerModel ) ); +#endif g_bDefaultPlayerDrawExternally = gameinfo->GetBool( "player_default_draw_externally", false ); +#ifdef EZ2 + extern ConVar sv_player_hands_modelname; + Q_strncpy( g_szDefaultHandsModel, gameinfo->GetString( "player_default_hands", sv_player_hands_modelname.GetString() ), sizeof( g_szDefaultHandsModel ) ); +#else Q_strncpy( g_szDefaultHandsModel, gameinfo->GetString( "player_default_hands", "models/weapons/v_hands.mdl" ), sizeof( g_szDefaultHandsModel ) ); +#endif g_iDefaultHandsSkin = gameinfo->GetInt( "player_default_hands_skin", 0 ); g_iDefaultHandsBody = gameinfo->GetInt( "player_default_hands_body", 0 ); #endif diff --git a/sp/src/game/shared/mapbase/vscript_consts_shared.cpp b/sp/src/game/shared/mapbase/vscript_consts_shared.cpp index 2ced70863e8..1a5d12a9d4f 100644 --- a/sp/src/game/shared/mapbase/vscript_consts_shared.cpp +++ b/sp/src/game/shared/mapbase/vscript_consts_shared.cpp @@ -290,6 +290,17 @@ void RegisterSharedScriptConstants() ScriptRegisterConstant( g_pScriptVM, EF_ITEM_BLINK, "Effect flag used in GetEffects(), etc." ); ScriptRegisterConstant( g_pScriptVM, EF_PARENT_ANIMATES, "Effect flag used in GetEffects(), etc." ); + // + // Solid Types + // + ScriptRegisterConstant( g_pScriptVM, SOLID_NONE, "Solid type used by VPhysics" ); + ScriptRegisterConstant( g_pScriptVM, SOLID_BSP, "Solid type used by VPhysics" ); + ScriptRegisterConstant( g_pScriptVM, SOLID_BBOX, "Solid type used by VPhysics" ); + ScriptRegisterConstant( g_pScriptVM, SOLID_OBB, "Solid type used by VPhysics" ); + ScriptRegisterConstant( g_pScriptVM, SOLID_OBB_YAW, "Solid type used by VPhysics" ); + ScriptRegisterConstant( g_pScriptVM, SOLID_CUSTOM, "Solid type used by VPhysics" ); + ScriptRegisterConstant( g_pScriptVM, SOLID_VPHYSICS, "Solid type used by VPhysics" ); + // // Solid Flags // @@ -523,14 +534,14 @@ void RegisterSharedScriptConstants() ScriptRegisterConstant( g_pScriptVM, CLASS_ANTLION, "Used by antlions, antlion guards, etc." ); ScriptRegisterConstant( g_pScriptVM, CLASS_BARNACLE, "Used by barnacles." ); ScriptRegisterConstant( g_pScriptVM, CLASS_BULLSEYE, "Used by npc_bullseye." ); - //ScriptRegisterConstant( g_pScriptVM, CLASS_BULLSQUID, "Used by bullsquids." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_BULLSQUID, "Used by bullsquids." ); ScriptRegisterConstant( g_pScriptVM, CLASS_CITIZEN_PASSIVE, "Used by citizens when the \"gordon_precriminal\" or \"citizens_passive\" states are enabled." ); ScriptRegisterConstant( g_pScriptVM, CLASS_CITIZEN_REBEL, "UNUSED IN HL2. Rebels normally use CLASS_PLAYER_ALLY." ); ScriptRegisterConstant( g_pScriptVM, CLASS_COMBINE, "Used by Combine soldiers, Combine turrets, and other misc. Combine NPCs." ); ScriptRegisterConstant( g_pScriptVM, CLASS_COMBINE_GUNSHIP, "Used by Combine gunships, helicopters, etc." ); ScriptRegisterConstant( g_pScriptVM, CLASS_CONSCRIPT, "UNUSED IN HL2. Would've been used by conscripts." ); ScriptRegisterConstant( g_pScriptVM, CLASS_HEADCRAB, "Used by headcrabs." ); - //ScriptRegisterConstant( g_pScriptVM, CLASS_HOUNDEYE, "Used by houndeyes." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_HOUNDEYE, "Used by houndeyes." ); ScriptRegisterConstant( g_pScriptVM, CLASS_MANHACK, "Used by Combine manhacks." ); ScriptRegisterConstant( g_pScriptVM, CLASS_METROPOLICE, "Used by Combine metrocops." ); ScriptRegisterConstant( g_pScriptVM, CLASS_MILITARY, "In HL2, this is only used by npc_combinecamera and func_guntarget. This appears to be recognized as a Combine class." ); @@ -544,6 +555,15 @@ void RegisterSharedScriptConstants() ScriptRegisterConstant( g_pScriptVM, CLASS_EARTH_FAUNA, "Used by birds and other terrestrial animals." ); ScriptRegisterConstant( g_pScriptVM, CLASS_HACKED_ROLLERMINE, "Used by rollermines which were hacked by Alyx." ); ScriptRegisterConstant( g_pScriptVM, CLASS_COMBINE_HUNTER, "Used by Combine hunters." ); +#ifdef EZ + ScriptRegisterConstant( g_pScriptVM, CLASS_ALIEN_FAUNA, "Used by boids and other passive Xen fauna." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_ALIEN_PREDATOR, "Used by Xen predators." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_ALIEN_FLORA, "Used by Xen plants." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_RACE_X, "Used by Race X." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_COMBINE_NEMESIS, "Used by Clone Cop and his manhacks." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_ARBEIT_TECH, "Used by Wilson and other Arbeit technology." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_COMBINE_HUSK, "Used by husks." ); +#endif #elif defined( HL1_DLL ) diff --git a/sp/src/game/shared/mapbase/vscript_funcs_hl2.cpp b/sp/src/game/shared/mapbase/vscript_funcs_hl2.cpp index 189f8883d89..529d02913dc 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_hl2.cpp +++ b/sp/src/game/shared/mapbase/vscript_funcs_hl2.cpp @@ -10,6 +10,8 @@ #include "hl2_gamerules.h" #ifndef CLIENT_DLL #include "eventqueue.h" +#include "weapon_physcannon.h" +#include "player_pickup.h" #endif // memdbgon must be the last include file in a .cpp file!!! @@ -47,6 +49,48 @@ bool ScriptMegaPhyscannonActive() { return HL2GameRules()->MegaPhyscannonActive(); } + +void ScriptPickup_ForcePlayerToDropThisObject( HSCRIPT hTarget ) +{ + Pickup_ForcePlayerToDropThisObject( ToEnt( hTarget ) ); +} + +float ScriptPlayerPickupGetHeldObjectMass( HSCRIPT hPickupControllerEntity, HSCRIPT hHeldObject ) +{ + IPhysicsObject *pPhysObj = HScriptToClass( hHeldObject ); + if (!pPhysObj) + { + CBaseEntity *pEnt = ToEnt( hHeldObject ); + if (pEnt) + pPhysObj = pEnt->VPhysicsGetObject(); + } + + if (!pPhysObj) + { + Warning( "PlayerPickupGetHeldObjectMass: Invalid physics object\n" ); + return 0.0f; + } + + return PlayerPickupGetHeldObjectMass( ToEnt( hPickupControllerEntity ), pPhysObj ); +} + +HSCRIPT ScriptGetPlayerHeldEntity( HSCRIPT hPlayer ) +{ + CBasePlayer *pPlayer = ToBasePlayer( ToEnt( hPlayer ) ); + if (!pPlayer) + return NULL; + + return ToHScript( GetPlayerHeldEntity( pPlayer ) ); +} + +HSCRIPT ScriptPhysCannonGetHeldEntity( HSCRIPT hWeapon ) +{ + CBaseEntity *pEnt = ToEnt( hWeapon ); + if (!pEnt) + return NULL; + + return ToHScript( PhysCannonGetHeldEntity( pEnt->MyCombatWeaponPointer() ) ); +} #endif //----------------------------------------------------------------------------- @@ -59,5 +103,10 @@ void CHalfLife2::RegisterScriptFunctions( void ) #ifndef CLIENT_DLL ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGameOver, "GameOver", "Ends the game and reloads the last save." ); ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMegaPhyscannonActive, "MegaPhyscannonActive", "Checks if supercharged gravity gun mode is enabled." ); + + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPickup_ForcePlayerToDropThisObject, "Pickup_ForcePlayerToDropThisObject", "If the specified entity is being carried, instantly drops it." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPlayerPickupGetHeldObjectMass, "PlayerPickupGetHeldObjectMass", "Gets the mass of the specified player_pickup controller, with the second parameter the held object's physics object." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGetPlayerHeldEntity, "GetPlayerHeldEntity", "Gets the specified player's currently held entity." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPhysCannonGetHeldEntity, "PhysCannonGetHeldEntity", "Gets the specified gravity gun's currently held entity." ); #endif } diff --git a/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp index 4c6d4817943..227103dff83 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp +++ b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp @@ -771,6 +771,26 @@ static void AddPhysVelocity( HSCRIPT hPhys, const Vector& vecVelocity, const Vec pPhys->AddVelocity( &vecVelocity, &vecAngVelocity ); } +static void ScriptPhysEnableEntityCollisions( HSCRIPT hPhys1, HSCRIPT hPhys2 ) +{ + IPhysicsObject *pPhys1 = HScriptToClass( hPhys1 ); + IPhysicsObject *pPhys2 = HScriptToClass( hPhys2 ); + if (!pPhys1 || !pPhys2) + return; + + PhysEnableEntityCollisions( pPhys1, pPhys2 ); +} + +static void ScriptPhysDisableEntityCollisions( HSCRIPT hPhys1, HSCRIPT hPhys2 ) +{ + IPhysicsObject *pPhys1 = HScriptToClass( hPhys1 ); + IPhysicsObject *pPhys2 = HScriptToClass( hPhys2 ); + if (!pPhys1 || !pPhys2) + return; + + PhysDisableEntityCollisions( pPhys1, pPhys2 ); +} + //============================================================================= //============================================================================= @@ -1045,6 +1065,8 @@ void RegisterSharedScriptFunctions() ScriptRegisterFunction( g_pScriptVM, GetPhysAngVelocity, "Gets physics angular velocity for the given VPhysics object" ); ScriptRegisterFunction( g_pScriptVM, SetPhysVelocity, "Sets physics velocity for the given VPhysics object" ); ScriptRegisterFunction( g_pScriptVM, AddPhysVelocity, "Adds physics velocity for the given VPhysics object" ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPhysEnableEntityCollisions, "PhysEnableEntityCollisions", "Enables collisions between two VPhysics objects"); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPhysDisableEntityCollisions, "PhysDisableEntityCollisions", "Disables collisions between two VPhysics objects"); // // Precaching diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index c59a03ae8d7..9bbcb11e5ba 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -14,7 +14,13 @@ #include "ammodef.h" #include "tier1/utlcommon.h" +#include "soundenvelope.h" +#include "saverestore_utlvector.h" +#include "stdstring.h" + #ifndef CLIENT_DLL +#include "ai_speech.h" +#include "ai_memory.h" #include "ai_squad.h" #endif // !CLIENT_DLL @@ -48,285 +54,2492 @@ extern IScriptManager *scriptmanager; + +#ifdef GAME_DLL + extern void SendProxy_StringT_To_String(const SendProp*, const void*, const void*, DVariant*, int, int); + extern void SendProxy_UtlVectorLength(const SendProp*, const void*, const void*, DVariant*, int, int); + class CSendProxyRecipients; + extern void* SendProxy_LengthTable(const SendProp*, const void*, const void* pData, CSendProxyRecipients*, int); + #define DataTableProxy_EHandle SendProxy_EHandleToInt + #define DataTableProxy_String SendProxy_StringToString + #define DataTableProxy_TableLength SendProxy_LengthTable + #define DataTableProxy_UtlVectorLength SendProxy_UtlVectorLength +#else + extern void RecvProxy_UtlVectorLength(const CRecvProxyData*, void*, void*); + extern void DataTableRecvProxy_LengthProxy(const RecvProp*, void**, void*, int); + #define DataTableProxy_EHandle RecvProxy_IntToEHandle + #define DataTableProxy_String RecvProxy_StringToString + #define DataTableProxy_TableLength DataTableRecvProxy_LengthProxy + #define DataTableProxy_UtlVectorLength RecvProxy_UtlVectorLength +#endif +extern ISaveRestoreOps* GetPhysObjSaveRestoreOps( PhysInterfaceId_t ); +extern ISaveRestoreOps* ActivityDataOps(); +extern ISaveRestoreOps* GetSoundSaveRestoreOps(); +extern ISaveRestoreOps* GetStdStringDataOps(); +#ifdef GAME_DLL + #define UTLVECTOR_DATAOPS( fieldType, dataType )\ + CUtlVectorDataopsInstantiator< fieldType >::GetDataOps( (CUtlVector< dataType >*)0 ) + #define IS_EHANDLE_UTLVECTOR( td )\ + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBaseEntity > ) ||\ + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBaseFlex > ) ||\ + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBaseAnimating > ) ||\ + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBaseCombatWeapon > ) ||\ + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBasePlayer > ) ||\ + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CAI_BaseNPC > ) ||\ + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CSceneEntity > ) ||\ + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CSceneListManager > ) ||\ + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CRagdollBoogie > ) ||\ + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CFish > ) ||\ + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CVGuiScreen > ) + + class CSceneListManager; + class CRagdollBoogie; + class CFish; + #ifdef _DEBUG + class CStringTableSaveRestoreOps; + extern CStringTableSaveRestoreOps g_VguiScreenStringOps; + extern INetworkStringTable *g_pStringTableVguiScreen; + extern ISaveRestoreOps *thinkcontextFuncs; + class CAI_EnemiesListSaveRestoreOps; + extern CAI_EnemiesListSaveRestoreOps g_AI_MemoryListSaveRestoreOps; + class CConceptHistoriesDataOps; + extern CConceptHistoriesDataOps g_ConceptHistoriesSaveDataOps; + #endif +#endif + //============================================================================= // Net Prop Manager // Based on L4D2 API //============================================================================= class CScriptNetPropManager { -public: +private: +#if GAME_DLL + typedef SendProp NetProp; + typedef SendTable NetTable; + typedef ServerClass NetworkClass; -#ifdef CLIENT_DLL - RecvProp *RecurseTable( RecvTable *pTable, const char *pszPropName ) + NetworkClass *GetNetworkClass( CBaseEntity* p ) { return p->GetServerClass(); } + NetTable *GetNetTable( NetworkClass* p ) { return p->m_pTable; } + + void NetworkStateChanged( CBaseEntity* p, int o ) { p->NetworkProp()->NetworkStateChanged( o ); } #else - SendProp *RecurseTable( SendTable *pTable, const char *pszPropName ) + typedef RecvProp NetProp; + typedef RecvTable NetTable; + typedef ClientClass NetworkClass; + + NetworkClass *GetNetworkClass( CBaseEntity* p ) { return p->GetClientClass(); } + NetTable *GetNetTable( NetworkClass* p ) { return p->m_pRecvTable; } + + void NetworkStateChanged( CBaseEntity*, int ) {} #endif + + int GetClassID( CBaseEntity *p ) { -#ifdef CLIENT_DLL - RecvProp *pProp = NULL; + return GetNetworkClass( p )->m_ClassID; + } + + int GetIntPropSize( NetProp *pProp ) + { + Assert( pProp->GetType() == DPT_Int ); + +#ifdef GAME_DLL + extern void SendProxy_UInt8ToInt32( const SendProp*, const void*, const void*, DVariant*, int, int ); + extern void SendProxy_UInt16ToInt32( const SendProp*, const void*, const void*, DVariant*, int, int ); + extern void SendProxy_UInt32ToInt32( const SendProp*, const void*, const void*, DVariant*, int, int ); + + SendVarProxyFn proxy = pProp->GetProxyFn(); + + if ( proxy == SendProxy_Int8ToInt32 || proxy == SendProxy_UInt8ToInt32 ) + return 8; + if ( proxy == SendProxy_Int16ToInt32 || proxy == SendProxy_UInt16ToInt32 ) + return 16; + if ( proxy == SendProxy_Int32ToInt32 || proxy == SendProxy_UInt32ToInt32 ) + return 32; + + return pProp->m_nBits; #else - SendProp *pProp = NULL; + RecvVarProxyFn proxy = pProp->GetProxyFn(); + + if ( proxy == RecvProxy_Int32ToInt8 ) + return 8; + if ( proxy == RecvProxy_Int32ToInt16 ) + return 16; + if ( proxy == RecvProxy_Int32ToInt32 ) + return 32; + + return 0; #endif - for (int i = 0; i < pTable->GetNumProps(); i++) - { - pProp = pTable->GetProp( i ); - if (pProp->GetType() == DPT_DataTable) - { - pProp = RecurseTable(pProp->GetDataTable(), pszPropName); - if (pProp) - return pProp; - } - else - { - if (FStrEq( pProp->GetName(), pszPropName )) - return pProp; - } - } + } - return NULL; + bool IsEHandle( NetProp *pProp ) + { + return ( pProp->GetProxyFn() == DataTableProxy_EHandle ); } -#ifdef CLIENT_DLL - RecvProp *RecurseNetworkClass( ClientClass *pClass, const char *pszPropName ) -#else - SendProp *RecurseNetworkClass( ServerClass *pClass, const char *pszPropName ) -#endif + bool IsUtlVector( NetProp *pProp ) { -#ifdef CLIENT_DLL - RecvProp *pProp = RecurseTable( pClass->m_pRecvTable, pszPropName ); +#ifdef GAME_DLL + SendVarProxyFn proxy = pProp->GetProxyFn(); #else - SendProp *pProp = RecurseTable( pClass->m_pTable, pszPropName ); + RecvVarProxyFn proxy = pProp->GetProxyFn(); #endif - if (pProp) - return pProp; - if (pClass->m_pNext) - return RecurseNetworkClass( pClass->m_pNext, pszPropName ); - else - return NULL; + return ( proxy == DataTableProxy_UtlVectorLength ); } -#ifdef CLIENT_DLL - RecvProp *GetPropByName( CBaseEntity *pEnt, const char *pszPropName ) +private: + enum types + { + _INT1 = ( 1 << 0 ), + _INT8 = ( 1 << 1 ), + _INT16 = ( 1 << 2 ), + _INT32 = ( 1 << 3 ), + _FLOAT = ( 1 << 4 ), + _VEC3 = ( 1 << 5 ), + _VEC2 = ( 1 << 6 ), + _EHANDLE = ( 1 << 7 ), + _CLASSPTR = ( 1 << 8 ), + _EDICT = ( 1 << 9 ), + _CSTRING = ( 1 << 10 ), + _STRING_T = ( 1 << 11 ), + _ARRAY = ( 1 << 12 ), + _DATATABLE = ( 1 << 13 ), + + _PHYS = ( 1 << 14 ), + _STDSTRING = _CSTRING | _STRING_T, + + _DAR_EHANDLE = _EHANDLE | _ARRAY, + _DAR_CLASSPTR = _CLASSPTR | _ARRAY, + _DAR_INT = _INT32 | _ARRAY, + _DAR_FLOAT = _FLOAT | _ARRAY, + + //_MAX = ( 1 << 15 ) + }; + + // UNDONE: Special case for GetPropType() to be able to return the table/array itself + #define INDEX_GET_TYPE 0 + + #define MASK_INT_SIZE( _size ) ( ( 1 << (_size - 1) ) | ( (1 << (_size - 1)) - 1 ) ) + #define MASK_NEAREST_BYTE( _bits ) ( ( (1 << ALIGN_TO_NEAREST_BYTE(_bits)) - 1 ) & ~((1 << _bits) - 1) ) + #define ALIGN_TO_NEAREST_BYTE( _bits ) ( (_bits + 7) & ~7 ) + #define VARINFO_ARRAYSIZE_BITS 12 + + struct varinfo_t { - if (pEnt) + int offset : 32; // actually a short + + union { - return RecurseNetworkClass( pEnt->GetClientClass(), pszPropName ); - } + int mask : 32; + int stringsize : 32; + }; - return NULL; - } -#else - SendProp *GetPropByName( CBaseEntity *pEnt, const char *pszPropName ) - { - if (pEnt) + enum types datatype : 16; + + // element size in bytes + unsigned int elemsize : 8; + unsigned int arraysize : VARINFO_ARRAYSIZE_BITS; + + // Following are only used in integer netprops to handle unsigned and size casting + bool isUnsigned : 1; + bool isNotNetworked : 1; + + int GetOffset( int index ) { - return RecurseNetworkClass( pEnt->GetServerClass(), pszPropName ); + return offset + index * elemsize; } + }; - return NULL; - } -#endif + // Wrapper to be able to set case sensitive comparator in node insertion + class vardict_t : public CUtlDict< varinfo_t > + { + public: + vardict_t() : CUtlDict< varinfo_t >( k_eDictCompareTypeCaseSensitive ) {} + }; - int GetPropArraySize( HSCRIPT hEnt, const char *pszPropName ) + // NOTE: This is lazy and inefficient. + // Simply map highest level class id to unique caches. + CUtlVector< int > m_EntMap; + CUtlVector< vardict_t > m_VarDicts; + + varinfo_t* CacheNew( CBaseEntity *pEnt, const char *szProp ) { - CBaseEntity *pEnt = ToEnt( hEnt ); - auto *pProp = GetPropByName( pEnt, pszPropName ); - if (pProp) + int idx = m_EntMap.Find( GetClassID( pEnt ) ); + if ( idx == m_EntMap.InvalidIndex() ) { - // TODO: Is this what this function wants? - return pProp->GetNumElements(); + // Vector indices are kept in parallel as a workaround for encapsulating maps + idx = m_EntMap.AddToTail( GetClassID( pEnt ) ); + m_VarDicts.AddToTail(); } - return -1; + vardict_t &dict = m_VarDicts.Element( idx ); + + idx = dict.Find( szProp ); + if ( idx == dict.InvalidIndex() ) + idx = dict.Insert( szProp ); + + varinfo_t *pInfo = &dict.Element( idx ); + V_memset( pInfo, 0, sizeof( varinfo_t ) ); + return pInfo; } - #define GetPropFunc( name, varType, propType, defaultval ) \ - varType name( HSCRIPT hEnt, const char *pszPropName ) \ - { \ - CBaseEntity *pEnt = ToEnt( hEnt ); \ - auto *pProp = GetPropByName( pEnt, pszPropName ); \ - if (pProp && pProp->GetType() == propType) \ - { \ - return *(varType*)((char *)pEnt + pProp->GetOffset()); \ - } \ - return defaultval; \ - } \ - - #define GetPropFuncArray( name, varType, propType, defaultval ) \ - varType name( HSCRIPT hEnt, const char *pszPropName, int iArrayElement ) \ - { \ - CBaseEntity *pEnt = ToEnt( hEnt ); \ - auto *pProp = GetPropByName( pEnt, pszPropName ); \ - if (pProp && pProp->GetType() == propType) \ - { \ - return ((varType*)((char *)pEnt + pProp->GetOffset()))[iArrayElement]; \ - } \ - return defaultval; \ - } \ - - GetPropFunc( GetPropFloat, float, DPT_Float, -1 ); - GetPropFuncArray( GetPropFloatArray, float, DPT_Float, -1 ); - GetPropFunc( GetPropInt, int, DPT_Int, -1 ); - GetPropFuncArray( GetPropIntArray, int, DPT_Int, -1 ); - GetPropFunc( GetPropVector, Vector, DPT_Vector, vec3_invalid ); - GetPropFuncArray( GetPropVectorArray, Vector, DPT_Vector, vec3_invalid ); - - HSCRIPT GetPropEntity( HSCRIPT hEnt, const char *pszPropName ) + varinfo_t* CacheFetch( CBaseEntity *pEnt, const char *szProp ) { - CBaseEntity *pEnt = ToEnt( hEnt ); - auto *pProp = GetPropByName( pEnt, pszPropName ); - if (pProp && pProp->GetType() == DPT_Int) - { - return ToHScript( *(CHandle*)((char *)pEnt + pProp->GetOffset()) ); - } + int idx = m_EntMap.Find( GetClassID( pEnt ) ); + if ( idx == m_EntMap.InvalidIndex() ) + return NULL; - return NULL; + vardict_t &dict = m_VarDicts.Element( idx ); + idx = dict.Find( szProp ); + if ( idx == dict.InvalidIndex() ) + return NULL; + + varinfo_t *pInfo = &dict.Element( idx ); + return pInfo; } - HSCRIPT GetPropEntityArray( HSCRIPT hEnt, const char *pszPropName, int iArrayElement ) +public: + ~CScriptNetPropManager() { - CBaseEntity *pEnt = ToEnt( hEnt ); - auto *pProp = GetPropByName( pEnt, pszPropName ); - if (pProp && pProp->GetType() == DPT_Int) - { - return ToHScript( ((CHandle*)((char *)pEnt + pProp->GetOffset()))[iArrayElement] ); - } + PurgeCache(); + } - return NULL; + void PurgeCache() + { + m_EntMap.Purge(); + m_VarDicts.Purge(); } - const char *GetPropString( HSCRIPT hEnt, const char *pszPropName ) +private: + typedescription_t *FindField( char *pBase, datamap_t *map, const char *szName, int *offset ) { - CBaseEntity *pEnt = ToEnt( hEnt ); - auto *pProp = GetPropByName( pEnt, pszPropName ); - if (pProp && pProp->GetType() == DPT_Int) + if ( map->baseMap ) { - return (const char*)((char *)pEnt + pProp->GetOffset()); + typedescription_t* p = FindField( pBase, map->baseMap, szName, offset ); + if ( p ) + return p; } - return NULL; - } + typedescription_t *pFields = map->dataDesc; + int numFields = map->dataNumFields; - const char *GetPropStringArray( HSCRIPT hEnt, const char *pszPropName, int iArrayElement ) - { - CBaseEntity *pEnt = ToEnt( hEnt ); - auto *pProp = GetPropByName( pEnt, pszPropName ); - if (pProp && pProp->GetType() == DPT_Int) + for ( int i = 0; i < numFields; i++ ) { - return ((const char**)((char *)pEnt + pProp->GetOffset()))[iArrayElement]; + typedescription_t* td = &pFields[i]; + int fieldType = td->fieldType; + int fieldOffset = td->fieldOffset[ TD_OFFSET_NORMAL ]; + + if ( td->flags & (FTYPEDESC_FUNCTIONTABLE | FTYPEDESC_INPUT | FTYPEDESC_OUTPUT) ) + continue; + + if ( fieldType == FIELD_VOID || fieldType == FIELD_FUNCTION ) + continue; + + if ( !V_strcmp( td->fieldName, szName ) ) + { + *offset += fieldOffset; + + if ( td->flags & FTYPEDESC_PTR ) + { + // Follow the pointer + char * const pRef = *(char**)( pBase + *offset ); + Assert( pRef ); + *offset = pRef - pBase; + } + + return td; + } } return NULL; } - const char *GetPropType( HSCRIPT hEnt, const char *pszPropName ) + NetProp *FindProp( char *pBase, NetTable *pTable, const char *szName, int *offset ) { - CBaseEntity *pEnt = ToEnt( hEnt ); - auto *pProp = GetPropByName( pEnt, pszPropName ); - if (pProp) + int numProps = pTable->GetNumProps(); + + for ( int i = 0; i < numProps; i++ ) { - switch (pProp->GetType()) + NetProp* pProp = pTable->GetProp(i); + + if ( pProp->IsInsideArray() ) + continue; + + if ( !V_strcmp( pProp->GetName(), szName ) ) { - case DPT_Int: return "integer"; - case DPT_Float: return "float"; - case DPT_Vector: return "vector"; - case DPT_VectorXY: return "vector2d"; - case DPT_String: return "string"; - case DPT_Array: return "array"; - case DPT_DataTable: return "datatable"; + *offset += pProp->GetOffset(); + return pProp; + } + + // Go into inherited fields but not member tables, they are looked up explicitly + // This is only a problem with m_AnimOverlay + if ( ( pProp->GetFlags() & SPROP_COLLAPSIBLE ) || + ( pProp->GetType() == DPT_DataTable && pProp->GetOffset() == 0 ) ) + { + // Don't go into lengthproxy + if ( pProp->GetDataTableProxyFn() == DataTableProxy_TableLength ) + continue; + + NetProp *p = FindProp( pBase + pProp->GetOffset(), pProp->GetDataTable(), szName, offset ); + if ( p ) + { + *offset += pProp->GetOffset(); + return p; + } } } return NULL; } - bool HasProp( HSCRIPT hEnt, const char *pszPropName ) + typedescription_t *FindInDataMap( char * const pBase, datamap_t *map, const char *szFullProp, int *offset ) { - CBaseEntity *pEnt = ToEnt( hEnt ); - return GetPropByName( pEnt, pszPropName ) != NULL; - } - - #define SetPropFunc( name, varType, propType ) \ - void name( HSCRIPT hEnt, const char *pszPropName, varType value ) \ - { \ - CBaseEntity *pEnt = ToEnt( hEnt ); \ - auto *pProp = GetPropByName( pEnt, pszPropName ); \ - if (pProp && pProp->GetType() == propType) \ - { \ - *(varType*)((char *)pEnt + pProp->GetOffset()) = value; \ - } \ - } \ - - #define SetPropFuncArray( name, varType, propType ) \ - void name( HSCRIPT hEnt, const char *pszPropName, varType value, int iArrayElement ) \ - { \ - CBaseEntity *pEnt = ToEnt( hEnt ); \ - auto *pProp = GetPropByName( pEnt, pszPropName ); \ - if (pProp && pProp->GetType() == propType) \ - { \ - ((varType*)((char *)pEnt + pProp->GetOffset()))[iArrayElement] = value; \ - } \ - } \ - - SetPropFunc( SetPropFloat, float, DPT_Float ); - SetPropFuncArray( SetPropFloatArray, float, DPT_Float ); - SetPropFunc( SetPropInt, int, DPT_Int ); - SetPropFuncArray( SetPropIntArray, int, DPT_Int ); - SetPropFunc( SetPropVector, Vector, DPT_Vector ); - SetPropFuncArray( SetPropVectorArray, Vector, DPT_Vector ); - SetPropFunc( SetPropString, const char*, DPT_String ); - SetPropFuncArray( SetPropStringArray, const char*, DPT_String ); - - void SetPropEntity( HSCRIPT hEnt, const char *pszPropName, HSCRIPT value ) + *offset = 0; + + // Look for exact match + typedescription_t *pField = FindField( pBase, map, szFullProp, offset ); + if ( pField ) + return pField; + + // Look for members + const char *pszProp = szFullProp; + const char *pszPropEnd = V_strnchr( pszProp, '.', 512 ); + if ( !pszPropEnd ) + return NULL; + do + { + // this string comes from squirrel stringtable, it can be modified + *((char*)pszPropEnd) = 0; + pField = FindField( pBase, map, pszProp, offset ); + *((char*)pszPropEnd) = '.'; + pszProp = pszPropEnd + 1; + + if ( !pField || ( map = pField->td ) == NULL ) + return NULL; + + // Look for exact match again, just in case + pField = FindField( pBase, map, pszProp, offset ); + if ( pField ) + return pField; + } while ( ( pszPropEnd = V_strnchr( pszProp, '.', 512 ) ) != NULL ); + + return FindField( pBase, map, pszProp, offset ); + } + + NetProp *FindInNetTable( char * const pBase, NetTable *pTable, const char *szFullProp, int *offset ) { - CBaseEntity *pEnt = ToEnt( hEnt ); - auto *pProp = GetPropByName( pEnt, pszPropName ); - if (pProp && pProp->GetType() == DPT_Int) + *offset = 0; + + // Look for exact match + NetProp *pProp = FindProp( pBase, pTable, szFullProp, offset ); + if ( pProp ) + return pProp; + + // Look for members + const char *pszProp = szFullProp; + const char *pszPropEnd = V_strnchr( pszProp, '.', 512 ); + if ( !pszPropEnd ) + return NULL; + do { - *((CHandle*)((char *)pEnt + pProp->GetOffset())) = ToEnt(value); - } + // this string comes from squirrel stringtable, it can be modified + *((char*)pszPropEnd) = 0; + pProp = FindProp( pBase, pTable, pszProp, offset ); + *((char*)pszPropEnd) = '.'; + pszProp = pszPropEnd + 1; + + if ( !pProp || ( pTable = pProp->GetDataTable() ) == NULL ) + return NULL; + + // Look for exact match again for fields such as m_Local{m_skybox3d.scale} + pProp = FindProp( pBase, pTable, pszProp, offset ); + if ( pProp ) + return pProp; + } while ( ( pszPropEnd = V_strnchr( pszProp, '.', 512 ) ) != NULL ); + + return FindProp( pBase, pTable, pszProp, offset ); } - HSCRIPT SetPropEntityArray( HSCRIPT hEnt, const char *pszPropName, HSCRIPT value, int iArrayElement ) + // Searches NetTable first to handle overwritten member network variables - see + // CPlayerResource::m_iHealth and CBaseEntity::m_iHealth + varinfo_t *GetVarInfo( CBaseEntity *pEnt, const char *szProp, int index ) { - CBaseEntity *pEnt = ToEnt( hEnt ); - auto *pProp = GetPropByName( pEnt, pszPropName ); - if (pProp && pProp->GetType() == DPT_Int) + int offset = 0; + NetTable *pTable = GetNetTable( GetNetworkClass( pEnt ) ); + NetProp *pProp = FindInNetTable( (char*)pEnt, pTable, szProp, &offset ); + if ( pProp ) { - ((CHandle*)((char *)pEnt + pProp->GetOffset()))[iArrayElement] = ToEnt(value); + +#define SetVarInfo()\ + varinfo_t *pInfo = CacheNew( pEnt, szProp );\ + pInfo->isNotNetworked = 0;\ + pInfo->elemsize = pProp->GetElementStride();\ + pInfo->arraysize = pProp->GetNumElements();\ + pInfo->offset = offset; + + switch ( pProp->GetType() ) + { + case DPT_Int: + { + if ( IsUtlVector( pProp ) ) + { + return NULL; + } + + if ( index < 0 || index >= pProp->GetNumElements() ) + { + Warning( "NetProp element index out of range! %s[%d]\n", szProp, index ); + return NULL; + } + + Assert( index == 0 || pProp->GetElementStride() > 0 ); + + if ( IsEHandle( pProp ) ) + { + Assert( pProp->GetElementStride() == sizeof(int) || pProp->GetElementStride() < 0 ); + + SetVarInfo(); + pInfo->datatype = types::_EHANDLE; + return pInfo; + } + else + { + const int size = GetIntPropSize( pProp ); +#ifdef CLIENT_DLL + // Client might be reading any amount of bits in a custom RecvProxy + // Break and check the datamaps + if ( size == 0 ) + break; +#endif + Assert( size <= pProp->GetElementStride() || pProp->GetElementStride() < 0 ); + + SetVarInfo(); + pInfo->mask = MASK_INT_SIZE( size ); + pInfo->datatype = types::_INT32; + return pInfo; + } + } + case DPT_Float: + { + if ( index < 0 || index >= pProp->GetNumElements() ) + { + Warning( "NetProp element index out of range! %s[%d]\n", szProp, index ); + return NULL; + } + + Assert( index == 0 || pProp->GetElementStride() > 0 ); + Assert( pProp->GetElementStride() == sizeof(float) || pProp->GetElementStride() < 0 ); + + SetVarInfo(); + pInfo->datatype = types::_FLOAT; + return pInfo; + } + case DPT_Vector: + { + if ( index < 0 || index >= pProp->GetNumElements() ) + { + Warning( "NetProp element index out of range! %s[%d]\n", szProp, index ); + return NULL; + } + + Assert( index == 0 || pProp->GetElementStride() > 0 ); + Assert( pProp->GetElementStride() == sizeof(float)*3 || pProp->GetElementStride() < 0 ); + + SetVarInfo(); + pInfo->datatype = types::_VEC3; + return pInfo; + } + case DPT_VectorXY: + { + if ( index < 0 || index >= pProp->GetNumElements() ) + { + Warning( "NetProp element index out of range! %s[%d]\n", szProp, index ); + return NULL; + } + + Assert( index == 0 || pProp->GetElementStride() > 0 ); + Assert( pProp->GetElementStride() == sizeof(float)*2 || pProp->GetElementStride() < 0 ); + + SetVarInfo(); + pInfo->datatype = types::_VEC2; + return pInfo; + } + case DPT_String: + { + if ( index < 0 || index >= pProp->GetNumElements() ) + { + Warning( "NetProp element index out of range! %s[%d]\n", szProp, index ); + return NULL; + } + + Assert( index == 0 || pProp->GetElementStride() > 0 ); + + SetVarInfo(); +#ifdef GAME_DLL + pInfo->stringsize = 0; +#else + pInfo->stringsize = pProp->m_StringBufferSize; +#endif +#ifdef GAME_DLL + if ( pProp->GetProxyFn() == SendProxy_StringT_To_String ) + { + pInfo->datatype = types::_STRING_T; + } + else +#endif + { + Assert( pProp->GetProxyFn() == DataTableProxy_String ); + pInfo->datatype = types::_CSTRING; + } + return pInfo; + } + case DPT_DataTable: + { + NetTable* pArray = pProp->GetDataTable(); + + if ( V_strcmp( pProp->GetName(), pArray->GetName() ) != 0 ) + { + Warning( "DT is not an array! %s(%s)\n", pProp->GetName(), pArray->GetName() ); + return NULL; + } + + if ( index < 0 || index >= pArray->GetNumProps() ) + { + Warning( "NetProp element index out of range! %s[%d]\n", szProp, index ); + return NULL; + } + + pProp = pArray->GetProp( index ); + + switch ( pProp->GetType() ) + { + case DPT_Int: + { + if ( IsEHandle( pProp ) ) + { + varinfo_t *pInfo = CacheNew( pEnt, szProp ); + pInfo->elemsize = sizeof(int); + pInfo->arraysize = pArray->GetNumProps(); + pInfo->offset = offset; + pInfo->datatype = types::_EHANDLE; + return pInfo; + } + else + { + const int size = GetIntPropSize( pProp ); +#ifdef CLIENT_DLL + // Client might be reading any amount of bits in a custom RecvProxy + // Break and check the datamaps + if ( size == 0 ) + break; +#endif + varinfo_t *pInfo = CacheNew( pEnt, szProp ); + + if ( pArray->GetNumProps() > 1 ) + { + pInfo->elemsize = pArray->GetProp(1)->GetOffset() - pArray->GetProp(0)->GetOffset(); + } + else + { + // Doesn't matter for an array of a single element + pInfo->elemsize = 0; + } + + pInfo->arraysize = pArray->GetNumProps(); + pInfo->offset = offset; + pInfo->mask = MASK_INT_SIZE( size ); + pInfo->datatype = types::_INT32; + return pInfo; + } + } + case DPT_Float: + { + varinfo_t *pInfo = CacheNew( pEnt, szProp ); + pInfo->elemsize = sizeof(float); + pInfo->arraysize = pArray->GetNumProps(); + pInfo->offset = offset; + pInfo->datatype = types::_FLOAT; + return pInfo; + } + case DPT_Vector: + { + varinfo_t *pInfo = CacheNew( pEnt, szProp ); + pInfo->elemsize = sizeof(float)*3; + pInfo->arraysize = pArray->GetNumProps(); + pInfo->offset = offset; + pInfo->datatype = types::_VEC3; + return pInfo; + } + case DPT_VectorXY: + { + varinfo_t *pInfo = CacheNew( pEnt, szProp ); + pInfo->elemsize = sizeof(float)*2; + pInfo->arraysize = pArray->GetNumProps(); + pInfo->offset = offset; + pInfo->datatype = types::_VEC2; + return pInfo; + } + case DPT_DataTable: + { + AssertMsg( 0, "DT in DT" ); + return NULL; + } + case DPT_Array: + { + AssertMsg( 0, "Array in DT" ); + return NULL; + } + case DPT_String: + { + AssertMsg( 0, "String in DT" ); + return NULL; + } + default: UNREACHABLE(); + } +#ifdef CLIENT_DLL + // DPT_Int can break into here for datamap fallback + break; +#else + UNREACHABLE(); +#endif + } // DPT_DataTable + case DPT_Array: + { + Assert( pProp->GetArrayProp() ); + + NetProp *pArray = pProp->GetArrayProp(); + offset += pArray->GetOffset(); + + if ( index < 0 || index >= pProp->GetNumElements() ) + { + Warning( "NetProp element index out of range! %s[%d]\n", szProp, index ); + return NULL; + } + + switch ( pArray->GetType() ) + { + case DPT_Int: + { + Assert( index == 0 || pProp->GetElementStride() > 0 ); + + if ( IsEHandle( pArray ) ) + { + SetVarInfo(); + pInfo->datatype = types::_EHANDLE; + return pInfo; + } + else + { + const int size = GetIntPropSize( pArray ); +#ifdef CLIENT_DLL + // Client might be reading any amount of bits in a custom RecvProxy + // Break and check the datamaps + if ( size == 0 ) + break; +#endif + SetVarInfo(); + pInfo->mask = MASK_INT_SIZE( size ); + pInfo->datatype = types::_INT32; + return pInfo; + } + } + case DPT_Float: + { + SetVarInfo(); + pInfo->datatype = types::_FLOAT; + return pInfo; + } + case DPT_Vector: + { + SetVarInfo(); + pInfo->datatype = types::_VEC3; + return pInfo; + } + case DPT_VectorXY: + { + SetVarInfo(); + pInfo->datatype = types::_VEC2; + return pInfo; + } + case DPT_String: + { + AssertMsg( 0, "String array not implemented" ); + return NULL; + } + case DPT_Array: + case DPT_DataTable: AssertMsg( 0, "DT in array" ); + default: UNREACHABLE(); + } +#ifdef CLIENT_DLL + // DPT_Int can break into here for datamap fallback + break; +#else + UNREACHABLE(); +#endif + } // DPT_Array + default: UNREACHABLE(); + } + // ambigious int size on client, check the datamaps +#undef SetVarInfo } - return NULL; - } + datamap_t *map = pEnt->GetDataDescMap(); + typedescription_t *pField = FindInDataMap( (char*)pEnt, map, szProp, &offset ); + if ( pField ) + { +#ifdef CLIENT_DLL +find_field: +#endif + if ( index < 0 || index >= pField->fieldSize ) + { + Warning( "NetProp element index out of range! %s[%d]\n", szProp, index ); + return NULL; + } -private: -} g_ScriptNetPropManager; +#define SetVarInfo()\ + varinfo_t *pInfo = CacheNew( pEnt, szProp );\ + pInfo->isNotNetworked = 1;\ + pInfo->elemsize = pField->fieldSizeInBytes / pField->fieldSize;\ + pInfo->arraysize = pField->fieldSize;\ + pInfo->offset = offset; -BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptNetPropManager, "CNetPropManager", SCRIPT_SINGLETON "Allows reading and updating the network properties of an entity." ) - DEFINE_SCRIPTFUNC( GetPropArraySize, "Returns the size of an netprop array, or -1." ) - DEFINE_SCRIPTFUNC( GetPropEntity, "Reads an EHANDLE valued netprop (21 bit integer). Returns the script handle of the entity." ) - DEFINE_SCRIPTFUNC( GetPropEntityArray, "Reads an EHANDLE valued netprop (21 bit integer) from an array. Returns the script handle of the entity." ) - DEFINE_SCRIPTFUNC( GetPropFloat, "Reads a float valued netprop." ) - DEFINE_SCRIPTFUNC( GetPropFloatArray, "Reads a float valued netprop from an array." ) - DEFINE_SCRIPTFUNC( GetPropInt, "Reads an integer valued netprop." ) - DEFINE_SCRIPTFUNC( GetPropIntArray, "Reads an integer valued netprop from an array." ) - DEFINE_SCRIPTFUNC( GetPropString, "Reads a string valued netprop." ) - DEFINE_SCRIPTFUNC( GetPropStringArray, "Reads a string valued netprop from an array." ) - DEFINE_SCRIPTFUNC( GetPropVector, "Reads a 3D vector valued netprop." ) - DEFINE_SCRIPTFUNC( GetPropVectorArray, "Reads a 3D vector valued netprop from an array." ) - DEFINE_SCRIPTFUNC( GetPropType, "Returns the name of the netprop type as a string." ) - DEFINE_SCRIPTFUNC( HasProp, "Checks if a netprop exists." ) - DEFINE_SCRIPTFUNC( SetPropEntity, "Sets an EHANDLE valued netprop (21 bit integer) to reference the specified entity." ) - DEFINE_SCRIPTFUNC( SetPropEntityArray, "Sets an EHANDLE valued netprop (21 bit integer) from an array to reference the specified entity." ) - DEFINE_SCRIPTFUNC( SetPropFloat, "Sets a netprop to the specified float." ) - DEFINE_SCRIPTFUNC( SetPropFloatArray, "Sets a netprop from an array to the specified float." ) - DEFINE_SCRIPTFUNC( SetPropInt, "Sets a netprop to the specified integer." ) - DEFINE_SCRIPTFUNC( SetPropIntArray, "Sets a netprop from an array to the specified integer." ) - DEFINE_SCRIPTFUNC( SetPropString, "Sets a netprop to the specified string." ) - DEFINE_SCRIPTFUNC( SetPropStringArray, "Sets a netprop from an array to the specified string." ) - DEFINE_SCRIPTFUNC( SetPropVector, "Sets a netprop to the specified vector." ) - DEFINE_SCRIPTFUNC( SetPropVectorArray, "Sets a netprop from an array to the specified vector." ) + switch ( pField->fieldType ) + { + case FIELD_INTEGER: + case FIELD_MATERIALINDEX: + case FIELD_MODELINDEX: + case FIELD_COLOR32: + case FIELD_TICK: + case FIELD_BOOLEAN: + case FIELD_CHARACTER: + case FIELD_SHORT: + { + SetVarInfo(); + pInfo->isUnsigned = ( pField->flags & SPROP_UNSIGNED ) != 0; + pInfo->isNotNetworked = 1; + switch ( pField->fieldType ) + { + case FIELD_INTEGER: + case FIELD_MATERIALINDEX: + case FIELD_MODELINDEX: + case FIELD_COLOR32: + case FIELD_TICK: + pInfo->datatype = types::_INT32; break; + case FIELD_BOOLEAN: + pInfo->datatype = types::_INT1; break; + case FIELD_CHARACTER: + Assert( pField->fieldSizeInBytes == pField->fieldSize ); + pInfo->stringsize = pField->fieldSizeInBytes; + pInfo->datatype = types::_INT8; break; + case FIELD_SHORT: + pInfo->datatype = types::_INT16; break; + default: UNREACHABLE(); + } + return pInfo; + } + case FIELD_FLOAT: + case FIELD_TIME: + { + Assert( sizeof(float) == pField->fieldSizeInBytes / pField->fieldSize ); + + SetVarInfo(); + pInfo->datatype = types::_FLOAT; + return pInfo; + } + case FIELD_EHANDLE: + { + Assert( sizeof(int) == pField->fieldSizeInBytes / pField->fieldSize ); + + SetVarInfo(); + pInfo->datatype = types::_EHANDLE; + return pInfo; + } +#ifdef GAME_DLL + case FIELD_CLASSPTR: + { + Assert( sizeof(int*) == pField->fieldSizeInBytes / pField->fieldSize ); + + SetVarInfo(); + pInfo->datatype = types::_CLASSPTR; + return pInfo; + } + case FIELD_EDICT: + { + Assert( sizeof(int*) == pField->fieldSizeInBytes / pField->fieldSize ); + + SetVarInfo(); + pInfo->datatype = types::_EDICT; + return pInfo; + } +#endif + case FIELD_VECTOR: + case FIELD_POSITION_VECTOR: + { + Assert( sizeof(float)*3 == pField->fieldSizeInBytes / pField->fieldSize ); + + SetVarInfo(); + pInfo->datatype = types::_VEC3; + return pInfo; + } + case FIELD_STRING: + case FIELD_MODELNAME: + case FIELD_SOUNDNAME: + { + SetVarInfo(); + pInfo->stringsize = 0; + pInfo->datatype = types::_STRING_T; + return pInfo; + } + case FIELD_CUSTOM: + { + if ( pField->pSaveRestoreOps == GetPhysObjSaveRestoreOps( PIID_IPHYSICSOBJECT ) ) + { + SetVarInfo(); + pInfo->datatype = types::_PHYS; + return pInfo; + } + else if ( pField->pSaveRestoreOps == ActivityDataOps() ) + { + SetVarInfo(); + pInfo->datatype = types::_INT32; + return pInfo; + } +#ifdef GAME_DLL + else if ( IS_EHANDLE_UTLVECTOR( pField ) ) + { + SetVarInfo(); + pInfo->arraysize = ( 1 << VARINFO_ARRAYSIZE_BITS ) - 1; // dynamic, check on get + pInfo->datatype = types::_DAR_EHANDLE; + } + else if ( pField->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_CLASSPTR, CBaseEntity* ) ) + { + SetVarInfo(); + pInfo->arraysize = ( 1 << VARINFO_ARRAYSIZE_BITS ) - 1; // dynamic, check on get + pInfo->datatype = types::_DAR_CLASSPTR; + } + else if ( pField->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_INTEGER, int ) ) + { + SetVarInfo(); + pInfo->arraysize = ( 1 << VARINFO_ARRAYSIZE_BITS ) - 1; // dynamic, check on get + pInfo->datatype = types::_DAR_INT; + } + else if ( pField->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_FLOAT, float ) || + pField->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_TIME, float ) ) + { + SetVarInfo(); + pInfo->arraysize = ( 1 << VARINFO_ARRAYSIZE_BITS ) - 1; // dynamic, check on get + pInfo->datatype = types::_DAR_FLOAT; + } + // Only used by CAI_PlayerAlly::m_PendingConcept + else if ( pField->pSaveRestoreOps == GetStdStringDataOps() ) + { + SetVarInfo(); + pInfo->datatype = types::_STDSTRING; + return pInfo; + } +#endif + return NULL; + } + case FIELD_EMBEDDED: + return NULL; + default: + AssertMsg( 0, "Unknown type %d\n", pField->fieldType ); + return NULL; + } + UNREACHABLE(); +#undef SetVarInfo + } +#ifdef CLIENT_DLL + else + { + map = pEnt->GetPredDescMap(); + pField = FindInDataMap( (char*)pEnt, map, szProp, &offset ); + if ( pField ) + { + goto find_field; + } + } +#endif + return NULL; + } + +public: + // FIXME: Cannot get datatable/arrays at the moment + bool HasProp( HSCRIPT hEnt, const char *szProp ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + if ( !pEnt ) + return false; + + varinfo_t *pInfo = CacheFetch( pEnt, szProp ); + if ( !pInfo ) + { + pInfo = GetVarInfo( pEnt, szProp, INDEX_GET_TYPE ); + + if ( !pInfo ) + return false; + } + + return true; + } + + // FIXME: Cannot get datatable/arrays at the moment + const char *GetPropType( HSCRIPT hEnt, const char *szProp ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + if ( !pEnt ) + return NULL; + + varinfo_t *pInfo = CacheFetch( pEnt, szProp ); + if ( !pInfo ) + { + pInfo = GetVarInfo( pEnt, szProp, INDEX_GET_TYPE ); + + if ( !pInfo ) + return NULL; + } + + switch ( pInfo->datatype ) + { + case types::_INT1: + case types::_INT8: + case types::_INT16: + case types::_INT32: + return "integer"; + case types::_FLOAT: + return "float"; + case types::_VEC3: + return "vector"; + case types::_VEC2: + return "vector2d"; + case types::_CSTRING: + case types::_STRING_T: + case types::_STDSTRING: + return "string"; + case types::_EHANDLE: + case types::_CLASSPTR: + case types::_EDICT: + return "entity"; + case types::_PHYS: + return "phys"; + case types::_ARRAY: + return "array"; + case types::_DATATABLE: + return "datatable"; + } + + if ( pInfo->arraysize > 1 ) + return "array"; + + return ""; + } + + int GetPropArraySize( HSCRIPT hEnt, const char *szProp ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + if ( !pEnt ) + return -1; + + varinfo_t *pInfo = CacheFetch( pEnt, szProp ); + if ( !pInfo ) + { + pInfo = GetVarInfo( pEnt, szProp, INDEX_GET_TYPE ); + + if ( !pInfo ) + return -1; + } +#ifdef GAME_DLL + switch ( pInfo->datatype ) + { + case types::_DAR_EHANDLE: + { + CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)((char*)pEnt + pInfo->offset); + if ( !vec.Base() ) + return -1; + return vec.Count(); + } + case types::_DAR_CLASSPTR: + { + CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)((char*)pEnt + pInfo->offset); + if ( !vec.Base() ) + return -1; + return vec.Count(); + } + case types::_DAR_INT: + { + CUtlVector< int > &vec = *(CUtlVector< int >*)((char*)pEnt + pInfo->offset); + if ( !vec.Base() ) + return -1; + return vec.Count(); + } + case types::_DAR_FLOAT: + { + CUtlVector< float > &vec = *(CUtlVector< float >*)((char*)pEnt + pInfo->offset); + if ( !vec.Base() ) + return -1; + return vec.Count(); + } + } +#endif + return pInfo->arraysize; + } + +public: + int GetPropIntArray( HSCRIPT hEnt, const char *szProp, int index ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + if ( !pEnt ) + return -1; + + varinfo_t *pInfo = CacheFetch( pEnt, szProp ); + if ( !pInfo ) + { + pInfo = GetVarInfo( pEnt, szProp, index ); + + if ( !pInfo ) + return -1; + } + + if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + return -1; + + if ( pInfo->isNotNetworked ) + { + switch ( pInfo->datatype ) + { + case types::_INT32: + if ( pInfo->isUnsigned ) + return *(unsigned int*)((char*)pEnt + pInfo->GetOffset( index )); + return *(int*)((char*)pEnt + pInfo->GetOffset( index )); + case types::_INT1: + return *(bool*)((char*)pEnt + pInfo->GetOffset( index )); + case types::_INT8: + if ( pInfo->isUnsigned ) + return *(unsigned char*)((char*)pEnt + pInfo->GetOffset( index )); + return *(char*)((char*)pEnt + pInfo->GetOffset( index )); + case types::_INT16: + if ( pInfo->isUnsigned ) + return *(unsigned short*)((char*)pEnt + pInfo->GetOffset( index )); + return *(short*)((char*)pEnt + pInfo->GetOffset( index )); +#ifdef GAME_DLL + case types::_DAR_INT: + { + CUtlVector< int > &vec = *(CUtlVector< int >*)((char*)pEnt + pInfo->offset); + if ( !vec.Base() ) + return -1; + if ( index >= vec.Count() ) + return -1; + return vec[ index ]; + } +#endif + } + } + else + { + switch ( pInfo->datatype ) + { + case types::_INT32: + return (*(int*)((char*)pEnt + pInfo->GetOffset( index ))) & pInfo->mask; + } + } + + return -1; + } + + void SetPropIntArray( HSCRIPT hEnt, const char *szProp, int value, int index ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + if ( !pEnt ) + return; + + varinfo_t *pInfo = CacheFetch( pEnt, szProp ); + if ( !pInfo ) + { + pInfo = GetVarInfo( pEnt, szProp, index ); + + if ( !pInfo ) + return; + } + + if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + return; + + if ( pInfo->isNotNetworked ) + { + switch ( pInfo->datatype ) + { + case types::_INT32: + if ( pInfo->isUnsigned ) + { + *(unsigned int*)((char*)pEnt + pInfo->GetOffset( index )) = value; + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + break; + } + *(int*)((char*)pEnt + pInfo->GetOffset( index )) = value; + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + break; + case types::_INT1: + *(bool*)((char*)pEnt + pInfo->GetOffset( index )) = value; + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + break; + case types::_INT8: + if ( pInfo->isUnsigned ) + { + *(unsigned char*)((char*)pEnt + pInfo->GetOffset( index )) = value; + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + break; + } + *(char*)((char*)pEnt + pInfo->GetOffset( index )) = value; + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + break; + case types::_INT16: + if ( pInfo->isUnsigned ) + { + *(unsigned short*)((char*)pEnt + pInfo->GetOffset( index )) = value; + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + break; + } + *(short*)((char*)pEnt + pInfo->GetOffset( index )) = value; + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + break; +#ifdef GAME_DLL + case types::_DAR_INT: + { + CUtlVector< int > &vec = *(CUtlVector< int >*)((char*)pEnt + pInfo->offset); + if ( !vec.Base() ) + return; + if ( index >= vec.Count() ) + return; + vec[ index ] = value; + NetworkStateChanged( pEnt, pInfo->offset ); + break; + } +#endif + } + } + else + { + switch ( pInfo->datatype ) + { + case types::_INT32: + { + int *dest = (int*)((char*)pEnt + pInfo->GetOffset( index )); + *dest = (*dest & ~pInfo->mask) | (value & pInfo->mask); + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + break; + } + } + } + } + + float GetPropFloatArray( HSCRIPT hEnt, const char *szProp, int index ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + if ( !pEnt ) + return -1; + + varinfo_t *pInfo = CacheFetch( pEnt, szProp ); + if ( !pInfo ) + { + pInfo = GetVarInfo( pEnt, szProp, index ); + + if ( !pInfo ) + return -1; + } + + if ( pInfo->datatype == types::_VEC3 ) + index /= 3; + + if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + return -1; + + switch ( pInfo->datatype ) + { + case types::_VEC3: + case types::_FLOAT: + return *(float*)((char*)pEnt + pInfo->GetOffset( index )); +#ifdef GAME_DLL + case types::_DAR_FLOAT: + { + CUtlVector< float > &vec = *(CUtlVector< float >*)((char*)pEnt + pInfo->offset); + if ( !vec.Base() ) + return -1; + if ( index >= vec.Count() ) + return -1; + return vec[ index ]; + } +#endif + } + + return -1; + } + + void SetPropFloatArray( HSCRIPT hEnt, const char *szProp, float value, int index ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + if ( !pEnt ) + return; + + varinfo_t *pInfo = CacheFetch( pEnt, szProp ); + if ( !pInfo ) + { + pInfo = GetVarInfo( pEnt, szProp, index ); + + if ( !pInfo ) + return; + } + + if ( pInfo->datatype == types::_VEC3 ) + index /= 3; + + if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + return; + + switch ( pInfo->datatype ) + { + case types::_VEC3: + case types::_FLOAT: + *(float*)((char*)pEnt + pInfo->GetOffset( index )) = value; + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + break; +#ifdef GAME_DLL + case types::_DAR_FLOAT: + { + CUtlVector< float > &vec = *(CUtlVector< float >*)((char*)pEnt + pInfo->offset); + if ( !vec.Base() ) + return; + if ( index >= vec.Count() ) + return; + vec[ index ] = value; + NetworkStateChanged( pEnt, pInfo->offset ); + break; + } +#endif + } + } + + HSCRIPT GetPropEntityArray( HSCRIPT hEnt, const char *szProp, int index ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + if ( !pEnt ) + return NULL; + + varinfo_t *pInfo = CacheFetch( pEnt, szProp ); + if ( !pInfo ) + { + pInfo = GetVarInfo( pEnt, szProp, index ); + + if ( !pInfo ) + return NULL; + } + + if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + return NULL; + + switch ( pInfo->datatype ) + { + case types::_EHANDLE: + { + EHANDLE &iEHandle = *(EHANDLE*)((char*)pEnt + pInfo->GetOffset( index )); + return ToHScript( iEHandle ); + } +#ifdef GAME_DLL + case types::_CLASSPTR: + { + CBaseEntity* ptr = *(CBaseEntity**)((char*)pEnt + pInfo->GetOffset( index )); + return ToHScript( ptr ); + } + case types::_EDICT: + { + edict_t* ptr = *(edict_t**)((char*)pEnt + pInfo->GetOffset( index )); + return ToHScript( GetContainingEntity( ptr ) ); + } + case types::_DAR_EHANDLE: + { + CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)((char*)pEnt + pInfo->offset); + if ( !vec.Base() ) + return NULL; + if ( index >= vec.Count() ) + return NULL; + return ToHScript( vec[ index ] ); + } + case types::_DAR_CLASSPTR: + { + CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)((char*)pEnt + pInfo->offset); + if ( !vec.Base() ) + return NULL; + if ( index >= vec.Count() ) + return NULL; + return ToHScript( vec[ index ] ); + } +#endif + case types::_PHYS: + { + IPhysicsObject* ptr = *(IPhysicsObject**)((char*)pEnt + pInfo->GetOffset( index )); + return ptr ? g_pScriptVM->RegisterInstance( ptr ) : NULL; + } + } + + return NULL; + } + + void SetPropEntityArray( HSCRIPT hEnt, const char *szProp, HSCRIPT value, int index ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + if ( !pEnt ) + return; + + varinfo_t *pInfo = CacheFetch( pEnt, szProp ); + if ( !pInfo ) + { + pInfo = GetVarInfo( pEnt, szProp, index ); + + if ( !pInfo ) + return; + } + + if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + return; + + switch ( pInfo->datatype ) + { + case types::_EHANDLE: + *(EHANDLE*)((char*)pEnt + pInfo->GetOffset( index )) = ToEnt( value ); + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + break; +#ifdef GAME_DLL + case types::_CLASSPTR: + *(CBaseEntity**)((char*)pEnt + pInfo->GetOffset( index )) = ToEnt( value ); + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + break; + case types::_EDICT: + { + CBaseEntity* ptr = ToEnt( value ); + *(edict_t**)((char*)pEnt + pInfo->GetOffset( index )) = ptr ? ptr->edict() : NULL; + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + break; + } + case types::_DAR_EHANDLE: + { + CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)((char*)pEnt + pInfo->offset); + if ( !vec.Base() ) + return; + if ( index >= vec.Count() ) + return; + vec[ index ] = ToEnt( value ); + NetworkStateChanged( pEnt, pInfo->offset ); + break; + } + case types::_DAR_CLASSPTR: + { + CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)((char*)pEnt + pInfo->offset); + if ( !vec.Base() ) + return; + if ( index >= vec.Count() ) + return; + vec[ index ] = ToEnt( value ); + NetworkStateChanged( pEnt, pInfo->offset ); + break; + } +#endif + } + } + + const Vector &GetPropVectorArray( HSCRIPT hEnt, const char *szProp, int index ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + if ( !pEnt ) + return vec3_invalid; + + varinfo_t *pInfo = CacheFetch( pEnt, szProp ); + if ( !pInfo ) + { + pInfo = GetVarInfo( pEnt, szProp, index ); + + if ( !pInfo ) + return vec3_invalid; + } + + if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + return vec3_invalid; + + switch ( pInfo->datatype ) + { + case types::_VEC3: + return *(Vector*)((char*)pEnt + pInfo->GetOffset( index )); + } + + return vec3_invalid; + } + + void SetPropVectorArray( HSCRIPT hEnt, const char *szProp, const Vector &value, int index ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + if ( !pEnt ) + return; + + varinfo_t *pInfo = CacheFetch( pEnt, szProp ); + if ( !pInfo ) + { + pInfo = GetVarInfo( pEnt, szProp, index ); + + if ( !pInfo ) + return; + } + + if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + return; + + switch ( pInfo->datatype ) + { + case types::_VEC3: + *(Vector*)((char*)pEnt + pInfo->GetOffset( index )) = value; + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + break; + } + } + + const char *GetPropStringArray( HSCRIPT hEnt, const char *szProp, int index ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + if ( !pEnt ) + return NULL; + + varinfo_t *pInfo = CacheFetch( pEnt, szProp ); + if ( !pInfo ) + { + pInfo = GetVarInfo( pEnt, szProp, index ); + + if ( !pInfo ) + return NULL; + } + + if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + return NULL; + + switch ( pInfo->datatype ) + { + case types::_CSTRING: + return (const char*)((char*)pEnt + pInfo->GetOffset( index )); + case types::_STRING_T: // Identical to _CSTRING on client + return STRING( *(string_t*)((char*)pEnt + pInfo->GetOffset( index )) ); + case types::_INT8: + { + if ( !pInfo->stringsize ) + return NULL; + + char * const pVar = ((char*)pEnt + pInfo->GetOffset( index )); + + // Is this null terminated? + int i = 0; + char *c = pVar; + while ( *(c++) && i++ < pInfo->stringsize ); + + if ( i >= pInfo->stringsize ) + { + // Not a null terminated string, don't talk to me ever again + pInfo->stringsize = 0; + return NULL; + } + + return pVar; + } +#ifdef GAME_DLL + case types::_STDSTRING: + return ( (std::string*)((char*)pEnt + pInfo->GetOffset( index )) )->c_str(); +#endif + } + + return NULL; + } + + void SetPropStringArray( HSCRIPT hEnt, const char *szProp, const char *value, int index ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + if ( !pEnt ) + return; + + varinfo_t *pInfo = CacheFetch( pEnt, szProp ); + if ( !pInfo ) + { + pInfo = GetVarInfo( pEnt, szProp, index ); + + if ( !pInfo ) + return; + } + + if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + return; + + switch ( pInfo->datatype ) + { + case types::_CSTRING: + case types::_INT8: + { + if ( pInfo->stringsize ) + { + V_strncpy( (char*)pEnt + pInfo->GetOffset( index ), value, pInfo->stringsize ); + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + break; + } + } + case types::_STRING_T: + { + extern string_t FindPooledString( const char* ); + extern string_t AllocPooledString( const char* ); + + string_t src = FindPooledString( value ); + if ( src == NULL_STRING ) + src = AllocPooledString( value ); +#ifdef GAME_DLL + *(string_t*)((char*)pEnt + pInfo->GetOffset( index )) = src; +#else + V_strcpy( (char*)pEnt + pInfo->GetOffset( index ), src ); +#endif + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + break; + } +#ifdef GAME_DLL + case types::_STDSTRING: + { + ( (std::string*)((char*)pEnt + pInfo->GetOffset( index )) )->assign( value, V_strlen(value) ); + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + break; + } +#endif + } + } + +#define GetProp( type, name )\ + type GetProp##name( HSCRIPT hEnt, const char* szProp )\ + {\ + return GetProp##name##Array( hEnt, szProp, 0 );\ + } + +#define SetProp( type, name )\ + void SetProp##name( HSCRIPT hEnt, const char* szProp, type value )\ + {\ + SetProp##name##Array( hEnt, szProp, value, 0 );\ + } + + GetProp( int, Int ); + SetProp( int, Int ); + GetProp( float, Float ); + SetProp( float, Float ); + GetProp( HSCRIPT, Entity ); + SetProp( HSCRIPT, Entity ); + GetProp( const Vector&, Vector ); + SetProp( const Vector&, Vector ); + GetProp( const char*, String ); + SetProp( const char*, String ); + +#undef GetProp +#undef SetProp + +#ifdef _DEBUG +private: + CUtlBuffer m_output; + CUtlString m_indent; + int m_indent_level; + + void IndentStart() + { + m_indent = ""; + m_indent_level = 0; + } + + void Indent1() + { + m_indent_level++; + m_indent.Append("\t"); + } + + void Indent0() + { + m_indent_level--; + m_indent = m_indent.Slice( 0, m_indent_level ); + } + + void PrintVec3( float *pVar ) + { + if ( *(Vector*)pVar != vec3_invalid ) + { + Print( "[%f %f %f]", pVar[0], pVar[1], pVar[2] ); + } + else + { + Print("vec3_invalid"); + } + } + + void PrintVec2( float *pVar ) + { + Print( "[%f %f]", pVar[0], pVar[1] ); + } + + void PrintEntity( EHANDLE* pVar ) + { + CBaseEntity* ent = *pVar; + if ( ent ) + { + Print("[%d]%s", ent->entindex(), ent->GetDebugName()); + } + else + { + Print("null"); + } + } +#ifdef GAME_DLL + void PrintEntity( CBaseEntity* pVar ) + { + CBaseEntity* ent = pVar; + if ( ent ) + { + Print("[%d]%s", ent->entindex(), ent->GetDebugName()); + } + else + { + Print("null"); + } + } + + void PrintEntity( edict_t* pVar ) + { + CBaseEntity* ent = GetContainingEntity( pVar ); + if ( ent ) + { + Print("[%d]%s", ent->entindex(), ent->GetDebugName()); + } + else + { + Print("null"); + } + } +#endif +#ifdef GAME_DLL + void PrintString( string_t pVar ) + { + if ( STRING(pVar) ) + { + Print("\"%s\"", STRING(pVar)); + } + else + { + Print("null"); + } + } +#endif + void PrintString( const char *pVar ) + { + if ( pVar ) + { + Print("\"%s\"", pVar); + } + else + { + Print("null"); + } + } + + void PrintPropType( NetProp *pProp ) + { + switch ( pProp->GetType() ) + { + case DPT_Int: + if ( IsUtlVector( pProp ) ) + { + Print("UtlVector"); + } + else if ( IsEHandle( pProp ) ) + { + Print( "entity" ); + } + else + { + Print( "int" ); + } + break; +#ifdef SUPPORTS_INT64 + case DPT_Int64: + AssertMsg( 0, "not implemented" ); + Print( "int64" ); + break; +#endif + case DPT_Float: + Print( "float" ); + break; + case DPT_Vector: + Print( "vec3" ); + break; + case DPT_VectorXY: + Print( "vec2" ); + break; + case DPT_String: + { +#ifdef GAME_DLL + if ( pProp->GetProxyFn() == SendProxy_StringT_To_String ) + { + Print("string_t"); + } + else +#endif + { +#ifdef CLIENT_DLL + Print("string[%d]", pProp->m_StringBufferSize); +#else + Print("string"); +#endif + } + break; + } + case DPT_Array: + case DPT_DataTable: + break; + default: UNREACHABLE(); + } + } + + void PrintProp_r( char *pVar, NetProp *pProp ) + { + switch ( pProp->GetType() ) + { + case DPT_Int: + { + if ( IsUtlVector( pProp ) ) + { + } + else if ( IsEHandle( pProp ) ) + { + PrintEntity( (EHANDLE*)pVar ); + } + else + { +#ifdef GAME_DLL + // Is this value larger than networked size? + AssertMsg( (*(int*)pVar & MASK_NEAREST_BYTE( pProp->m_nBits )) == 0, + "%s(%i) %d bits doesn't fit networked %d bits", + pProp->GetName(), *(int*)pVar & MASK_NEAREST_BYTE( pProp->m_nBits ), ALIGN_TO_NEAREST_BYTE(pProp->m_nBits), pProp->m_nBits ); +#endif + int size = GetIntPropSize( pProp ); + if ( size ) + { + Print( "%i", *(int*)pVar & MASK_INT_SIZE( size ) ); + } + else + { + Print( " 0x%08x", *(int*)pVar ); + } + } + break; + } +#ifdef SUPPORTS_INT64 + case DPT_Int64: + { + Print( "%lli", *(int64*)pVar ); + break; + } +#endif + case DPT_Float: + { + Assert( pProp->GetElementStride() == sizeof(float) || pProp->GetElementStride() < 0 ); + if ( *(float*)pVar == FLT_MAX ) + { + Print("FLT_MAX"); + } + else + { + Print("%f", *(float*)pVar); + } + break; + } + case DPT_Vector: + { + PrintVec3( (float*)pVar ); + break; + } + case DPT_VectorXY: + { + PrintVec2( (float*)pVar ); + break; + } + case DPT_String: + { +#ifdef GAME_DLL + if ( pProp->GetProxyFn() == SendProxy_StringT_To_String ) + { + PrintString( *(string_t*)pVar ); + } + else +#endif + { + Assert( pProp->GetProxyFn() == DataTableProxy_String ); + PrintString( (char*)pVar ); + } + break; + } + case DPT_DataTable: + { + NetTable* pArray = pProp->GetDataTable(); + Assert( pArray->GetNumProps() ); + + if ( V_strcmp( pProp->GetName(), pArray->GetName() ) != 0 ) + { + Print( " -> (%s)\n", pArray->GetName() ); + DumpNetTable_r( pVar, pArray ); + break; + } + + // Double check that each element is the same size + // Array indexing ints gets element size from this + int diff1 = pArray->GetProp(1)->GetOffset() - pArray->GetProp(0)->GetOffset(); + for ( int k = 0; k < pArray->GetNumProps()-1; k++ ) + { + int diff2 = pArray->GetProp(k+1)->GetOffset() - pArray->GetProp(k)->GetOffset(); + Assert( diff1 == diff2 ); + } + + Print(" <"); + PrintPropType( pArray->GetProp(0) ); + Print(" array> #%d", pArray->GetNumProps()); + Print("\n%s[", m_indent.Get()); + Indent1(); + + for ( int j = 0; j < pArray->GetNumProps(); j++ ) + { + Print("\n%s", m_indent.Get()); + PrintProp_r( pVar + pArray->GetProp(j)->GetOffset(), pArray->GetProp(j) ); + } + + Indent0(); + Print( "\n%s]", m_indent.Get() ); + + break; + } + case DPT_Array: + { + Assert( pProp->GetArrayProp() ); + NetProp *pArray = pProp->GetArrayProp(); + pVar += pArray->GetOffset(); + + int numElements = pProp->GetNumElements(); + int elementStride = pProp->GetElementStride(); + + Print(" <"); + PrintPropType( pArray ); + Print(" array> #%d", numElements); + Print("\n%s[", m_indent.Get()); + Indent1(); + + for ( int j = 0; j < numElements; j++ ) + { + Print("\n%s", m_indent.Get()); + PrintProp_r( pVar + j * elementStride, pArray ); + } + + Indent0(); + Print( "\n%s]", m_indent.Get() ); + + break; + } + default: UNREACHABLE(); + } + } + + void DumpNetTable_r( void *pEnt, NetTable *pTable ) + { + Print("%s{\n", m_indent.Get()); + Indent1(); + + int numProps = pTable->GetNumProps(); + + for ( int i = 0; i < numProps; i++ ) + { + NetProp* pProp = pTable->GetProp(i); + char* pVar = (char*)pEnt + pProp->GetOffset(); + + if ( pProp->IsInsideArray() ) + continue; + + Print( "%s%s", m_indent.Get(), pProp->GetName() ); + + if ( pProp->GetOffset() == 0 ) + Print("<0>"); + + if ( pProp->GetType() != DPT_DataTable ) + Print(" <"); + PrintPropType( pProp ); + if ( pProp->GetType() != DPT_DataTable ) + Print("> "); + PrintProp_r( pVar, pProp ); + Print("\n"); + } + + Indent0(); + Print("%s}", m_indent.Get()); + } + + void PrintFieldType( char *pVar, typedescription_t *td ) + { + switch ( td->fieldType ) + { + case FIELD_INTEGER: + case FIELD_MATERIALINDEX: + case FIELD_MODELINDEX: + case FIELD_TICK: + Print( "int" ); + break; + case FIELD_SHORT: + Print( "short" ); + break; + case FIELD_CHARACTER: + Print( "char" ); + break; + case FIELD_BOOLEAN: + Print( "bool" ); + break; + case FIELD_COLOR32: + Print( "clr32" ); + break; + case FIELD_FLOAT: + case FIELD_TIME: + Print( "float" ); + break; + case FIELD_VECTOR: + case FIELD_POSITION_VECTOR: + Print( "vec3" ); + break; + case FIELD_VECTOR2D: + Print( "vec2" ); + break; + case FIELD_STRING: + case FIELD_MODELNAME: + case FIELD_SOUNDNAME: + Print( "string" ); + break; + case FIELD_EHANDLE: +#ifdef GAME_DLL + case FIELD_CLASSPTR: + case FIELD_EDICT: +#endif + Print( "entity" ); + break; + case FIELD_VMATRIX: + Print( "VMatrix" ); + break; + case FIELD_VMATRIX_WORLDSPACE: + Print( "VMatrix WORLDSPACE" ); + break; + case FIELD_MATRIX3X4_WORLDSPACE: + Print( "matrix3x4 WORLDSPACE" ); + break; + case FIELD_INTERVAL: + Print( "interval_t" ); + break; + case FIELD_CUSTOM: + PrintCustomFieldType( pVar, td ); + break; + case FIELD_EMBEDDED: + if ( td->fieldSize > 1 ) + Print( "DT" ); + break; + default: + Print( "unknown %d", td->fieldType ); + } + } + + void PrintCustomFieldType( char *pVar, typedescription_t *td ) + { + Assert( td->fieldType == FIELD_CUSTOM ); + + const char *g_ppszPhysTypeNames[PIID_NUM_TYPES] = + { + "Unknown Phys", + "IPhysicsObject", + "IPhysicsFluidController", + "IPhysicsSpring", + "IPhysicsConstraintGroup", + "IPhysicsConstraint", + "IPhysicsShadowController", + "IPhysicsPlayerController", + "IPhysicsMotionController", + "IPhysicsVehicleController", + }; + + for ( int i = 0; i < PIID_NUM_TYPES; i++ ) + { + if ( td->pSaveRestoreOps == GetPhysObjSaveRestoreOps( (PhysInterfaceId_t)i ) ) + { + Print("%s", g_ppszPhysTypeNames[i]); + return; + } + } + + if ( td->pSaveRestoreOps == ActivityDataOps() ) + { + Print("int"); + } + else if ( td->pSaveRestoreOps == GetSoundSaveRestoreOps() ) + { + Print("CSoundPatch"); + } + else if ( td->pSaveRestoreOps == GetStdStringDataOps() ) + { + Print("stdstring"); + } +#ifdef GAME_DLL + else if ( IS_EHANDLE_UTLVECTOR( td ) ) + { + CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)pVar; + if ( vec.Base() ) + Print("entity utlvector #%d", vec.Count()); + else + Print("entity utlvector"); + } + else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_INTEGER, int ) ) + { + CUtlVector< int > &vec = *(CUtlVector< int >*)pVar; + if ( vec.Base() ) + Print("int utlvector #%d", vec.Count()); + else + Print("int utlvector"); + } + else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_FLOAT, float ) || + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_TIME, float ) ) + { + CUtlVector< float > &vec = *(CUtlVector< float >*)pVar; + if ( vec.Base() ) + Print("float utlvector #%d", vec.Count()); + else + Print("float utlvector"); + } + else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_STRING, string_t ) ) + { + CUtlVector< string_t > &vec = *(CUtlVector< string_t >*)pVar; + if ( vec.Base() ) + Print("string utlvector #%d", vec.Count()); + else + Print("string utlvector"); + } + else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_CLASSPTR, CBaseEntity* ) ) + { + CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)pVar; + if ( vec.Base() ) + Print("entity utlvector #%d", vec.Count()); + else + Print("entity utlvector"); + } + else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_VECTOR, Vector ) ) + { + AssertMsg( 0, "Implement me" ); + CUtlVector< Vector > &vec = *(CUtlVector< Vector >*)pVar; + if ( vec.Base() ) + Print("Vector utlvector #%d", vec.Count()); + else + Print("Vector utlvector"); + } + else if ( !V_strcmp( td->fieldName, "m_pIk" ) ) + { + Print("IK"); + } + else if ( td->pSaveRestoreOps == thinkcontextFuncs ) + { + Print("thinkfunc"); + } + else if ( td->pSaveRestoreOps == (ISaveRestoreOps*)(&g_AI_MemoryListSaveRestoreOps) ) + { + Print("AI memory map"); + } + else if ( td->pSaveRestoreOps == (ISaveRestoreOps*)(&g_VguiScreenStringOps)) + { + Print("string (vgui screen)"); + } + else if ( td->pSaveRestoreOps == (ISaveRestoreOps*)(&g_ConceptHistoriesSaveDataOps) ) + { + Print("concept histories"); + } +#endif // GAME_DLL + else + { + Print("custom"); + } + } + + void PrintCustomField( char *pVar, typedescription_t *td ) + { + Assert( td->fieldType == FIELD_CUSTOM ); + + for ( int i = 0; i < PIID_NUM_TYPES; i++ ) + { + if ( td->pSaveRestoreOps == GetPhysObjSaveRestoreOps( (PhysInterfaceId_t)i ) ) + { + Print("0x%x", pVar); + return; + } + } + + if ( td->pSaveRestoreOps == ActivityDataOps() ) + { + Print("%i", *(int*)pVar); + } + else if ( td->pSaveRestoreOps == GetSoundSaveRestoreOps() ) + { + if ( *pVar ) + { + CSoundPatch *pSound = *(CSoundPatch**)pVar; + PrintString( CSoundEnvelopeController::GetController().SoundGetName( pSound ) ); + } + else + { + Print( "null" ); + } + } + else if ( td->pSaveRestoreOps == GetStdStringDataOps() ) + { + Print("%s", ((std::string*)pVar)->c_str()); + } +#ifdef GAME_DLL + else if ( IS_EHANDLE_UTLVECTOR( td ) ) + { + CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)pVar; + if ( !vec.Base() ) + { + Print("null"); + return; + } + Print("\n%s[", m_indent.Get()); + Indent1(); + FOR_EACH_VEC( vec, i ) + { + Print("\n%s", m_indent.Get()); + PrintEntity( vec[i] ); + } + Indent0(); + Print("\n%s]", m_indent.Get()); + } + else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_INTEGER, int ) ) + { + CUtlVector< int > &vec = *(CUtlVector< int >*)pVar; + if ( !vec.Base() ) + { + Print("null"); + return; + } + Print("\n%s[", m_indent.Get()); + Indent1(); + FOR_EACH_VEC( vec, i ) + { + Print("\n%s", m_indent.Get()); + Print( "%i", vec[i] ); + } + Indent0(); + Print("\n%s]", m_indent.Get()); + } + else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_FLOAT, float ) || + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_TIME, float ) ) + { + CUtlVector< float > &vec = *(CUtlVector< float >*)pVar; + if ( !vec.Base() ) + { + Print("null"); + return; + } + Print("\n%s[", m_indent.Get()); + Indent1(); + FOR_EACH_VEC( vec, i ) + { + Print("\n%s", m_indent.Get()); + Print( "%f", vec[i] ); + } + Indent0(); + Print("\n%s]", m_indent.Get()); + } + else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_STRING, string_t ) ) + { + CUtlVector< string_t > &vec = *(CUtlVector< string_t >*)pVar; + if ( !vec.Base() ) + { + Print("null"); + return; + } + Print("\n%s[", m_indent.Get()); + Indent1(); + FOR_EACH_VEC( vec, i ) + { + Print("\n%s", m_indent.Get()); + PrintString( vec[i] ); + } + Indent0(); + Print("\n%s]", m_indent.Get()); + } + else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_CLASSPTR, CBaseEntity* ) ) + { + CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)pVar; + if ( !vec.Base() ) + { + Print("null"); + return; + } + Print("\n%s[", m_indent.Get()); + Indent1(); + FOR_EACH_VEC( vec, i ) + { + Print("\n%s", m_indent.Get()); + PrintEntity( vec[i] ); + } + Indent0(); + Print("\n%s]", m_indent.Get()); + } + else if ( td->pSaveRestoreOps == (ISaveRestoreOps*)(&g_VguiScreenStringOps) ) + { + const char *pString = g_pStringTableVguiScreen->GetString( *(int*)pVar ); + PrintString( (char*)pString ); + } +#endif // GAME_DLL + else + { + Print("0x%x", pVar); + } + } + + void PrintField_r( char *pVar, typedescription_t *td ) + { + switch ( td->fieldType ) + { + case FIELD_INTEGER: + case FIELD_MATERIALINDEX: + case FIELD_MODELINDEX: + case FIELD_TICK: + if ( td->flags & SPROP_UNSIGNED ) + { + Print("%u", *(unsigned int*)pVar); + } + else + { + Print("%i", *(int*)pVar); + } + break; + case FIELD_COLOR32: + Print("0x%08x", *(int*)pVar); + break; + case FIELD_BOOLEAN: + Print("%i", *(bool*)pVar & 1); + break; + case FIELD_CHARACTER: + if ( *pVar < 0x20 ) + { + Print("%i (0x%x)", *pVar, *pVar); + } + else + { + Print("%i '%c'", *pVar, *pVar); + } + break; + case FIELD_SHORT: + if ( td->flags & SPROP_UNSIGNED ) + { + Print("%u", *(unsigned short*)pVar); + } + else + { + Print("%i", *(short*)pVar); + } + break; + case FIELD_FLOAT: + case FIELD_TIME: + if ( *(float*)pVar == FLT_MAX ) + { + Print("FLT_MAX"); + } + else + { + Print("%f", *(float*)pVar); + } + break; + case FIELD_VECTOR: + case FIELD_POSITION_VECTOR: + PrintVec3( (float*)pVar ); + break; + case FIELD_VECTOR2D: + PrintVec2( (float*)pVar ); + break; + case FIELD_STRING: + case FIELD_MODELNAME: + case FIELD_SOUNDNAME: +#ifdef GAME_DLL + PrintString( *(string_t*)pVar ); +#else + PrintString( *(char**)pVar ); +#endif + break; + case FIELD_EHANDLE: + PrintEntity( (EHANDLE*)pVar ); + break; +#ifdef GAME_DLL + case FIELD_CLASSPTR: + PrintEntity( *(CBaseEntity**)pVar ); + break; + case FIELD_EDICT: + PrintEntity( *(edict_t**)pVar ); + break; +#endif + case FIELD_EMBEDDED: + Print(" -> (%s)\n", td->td->dataClassName); + DumpDataFields_r( pVar, td->td ); + break; + case FIELD_CUSTOM: + PrintCustomField( pVar, td ); + break; + default: + Print( "", td->fieldType ); + } + } + + void DumpDataFields_r( void *pEnt, datamap_t *map ) + { + Print("%s{\n", m_indent.Get()); + Indent1(); + + if ( map->baseMap ) + { + Print("%sbaseclass -> (%s)\n", m_indent.Get(), map->baseMap->dataClassName); + DumpDataFields_r( pEnt, map->baseMap ); + Print("\n"); + } + + typedescription_t *pFields = map->dataDesc; + int numFields = map->dataNumFields; + + for ( int i = 0; i < numFields; i++ ) + { + typedescription_t* td = &pFields[i]; + + if ( td->flags & (FTYPEDESC_FUNCTIONTABLE | FTYPEDESC_INPUT | FTYPEDESC_OUTPUT) ) + continue; + + if ( td->fieldType == FIELD_VOID || td->fieldType == FIELD_FUNCTION ) + continue; + + char *pVar = (char*)pEnt + td->fieldOffset[ TD_OFFSET_NORMAL ]; + + if ( td->flags & FTYPEDESC_PTR ) + { + AssertMsg( *(char**)pVar, "NULL ptr ref" ); + pVar = *(char**)pVar; + } + + Print( "%s%s", m_indent.Get(), td->fieldName ); + + if ( td->fieldSize == 1 ) + { + if ( td->fieldType != FIELD_EMBEDDED ) + Print(" <"); + PrintFieldType( pVar, td ); + if ( td->fieldType != FIELD_EMBEDDED ) + Print("> "); + PrintField_r( pVar, td ); + } + else + { + Print(" <"); + PrintFieldType( pVar, td ); + Print(" array> #%d", td->fieldSize); + + Print("\n%s[", m_indent.Get()); + Indent1(); + + for ( int j = 0; j < td->fieldSize; j++ ) + { + Print("\n%s", m_indent.Get()); + PrintField_r( pVar + j * td->fieldSizeInBytes / td->fieldSize, td ); + } + + Indent0(); + Print("\n%s]", m_indent.Get()); + } + + Print("\n"); + } + + Indent0(); + Print("%s}", m_indent.Get()); + } + + void Print( const char *fmt, ... ) + { + char buf[2048]; + va_list va; + va_start( va, fmt ); + V_vsnprintf( buf, sizeof(buf) - 1, fmt, va ); + va_end( va ); + + m_output.PutString( buf ); + } + +public: + void Dump( HSCRIPT hEnt, const char* filename ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + if ( !pEnt ) + return; + + if ( !filename || !*filename ) + return; + + m_output.SetBufferType( true, false ); + IndentStart(); + + Print( "\n" ); + Print( "(%s)\n", GetNetTable( GetNetworkClass(pEnt) )->GetName() ); + DumpNetTable_r( pEnt, GetNetTable( GetNetworkClass(pEnt) ) ); + Print( "\n\n" ); + + Print( "\n" ); + Print( "(%s)\n", pEnt->GetDataDescMap()->dataClassName ); + DumpDataFields_r( pEnt, pEnt->GetDataDescMap() ); + Print( "\n\n" ); +#ifdef CLIENT_DLL + Print( "\n" ); + Print( "(%s)\n", pEnt->GetPredDescMap()->dataClassName ); + DumpDataFields_r( pEnt, pEnt->GetPredDescMap() ); + Print( "\n\n" ); +#endif + const char *pszFile = V_GetFileName( filename ); + filesystem->WriteFile( pszFile, "MOD", m_output ); + + m_indent.Purge(); + m_output.Purge(); + } +#endif // _DEBUG +} g_ScriptNetPropManager; + +BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptNetPropManager, "CNetPropManager", SCRIPT_SINGLETON "Allows reading and updating the network properties and data fields of an entity." ) + DEFINE_SCRIPTFUNC( GetPropArraySize, "Returns the size of an array." ) + DEFINE_SCRIPTFUNC( GetPropEntity, "Reads an entity." ) + DEFINE_SCRIPTFUNC( GetPropEntityArray, "Reads an entity from an array." ) + DEFINE_SCRIPTFUNC( GetPropFloat, "Reads a float." ) + DEFINE_SCRIPTFUNC( GetPropFloatArray, "Reads a float from an array." ) + DEFINE_SCRIPTFUNC( GetPropInt, "Reads an integer." ) + DEFINE_SCRIPTFUNC( GetPropIntArray, "Reads an integer from an array." ) + DEFINE_SCRIPTFUNC( GetPropString, "Reads a string." ) + DEFINE_SCRIPTFUNC( GetPropStringArray, "Reads a string from an array." ) + DEFINE_SCRIPTFUNC( GetPropVector, "Reads a 3D vector." ) + DEFINE_SCRIPTFUNC( GetPropVectorArray, "Reads a 3D vector from an array." ) + DEFINE_SCRIPTFUNC( GetPropType, "Returns the netprop type as a string." ) + DEFINE_SCRIPTFUNC( HasProp, "Checks if netprop/datafield exists." ) + DEFINE_SCRIPTFUNC( SetPropEntity, "Sets an entity." ) + DEFINE_SCRIPTFUNC( SetPropEntityArray, "Sets an entity in an array." ) + DEFINE_SCRIPTFUNC( SetPropFloat, "Sets to the specified float." ) + DEFINE_SCRIPTFUNC( SetPropFloatArray, "Sets a float in an array." ) + DEFINE_SCRIPTFUNC( SetPropInt, "Sets to the specified integer." ) + DEFINE_SCRIPTFUNC( SetPropIntArray, "Sets an integer in an array." ) + DEFINE_SCRIPTFUNC( SetPropString, "Sets to the specified string." ) + DEFINE_SCRIPTFUNC( SetPropStringArray, "Sets a string in an array." ) + DEFINE_SCRIPTFUNC( SetPropVector, "Sets to the specified vector." ) + DEFINE_SCRIPTFUNC( SetPropVectorArray, "Sets a 3D vector in an array." ) +#ifdef _DEBUG + DEFINE_SCRIPTFUNC( Dump, "Dump all readable netprop and datafield values of this entity. Pass in file name to write into." ); +#endif END_SCRIPTDESC(); //============================================================================= diff --git a/sp/src/game/shared/props_shared.cpp b/sp/src/game/shared/props_shared.cpp index 6d0f920973b..d6a7d29ae88 100644 --- a/sp/src/game/shared/props_shared.cpp +++ b/sp/src/game/shared/props_shared.cpp @@ -172,6 +172,14 @@ propdata_interaction_s sPropdataInteractionSections[PROPINTER_NUM_INTERACTIONS] { "physgun_interactions", "allow_overhead", "yes" }, // PROPINTER_PHYSGUN_ALLOW_OVERHEAD, { "world_interactions", "onworldimpact", "bloodsplat" }, // PROPINTER_WORLD_BLOODSPLAT, + +#ifdef EZ2 + // HACKHACK: This interaction didn't have an entry in this array and is needed for subsequent interactions to work. + // TODO: This change would be more fitting for Mapbase since it's needed to add any new interactions. + { "physgun_interactions", "onfirstimpact", "notify_children" }, // PROPINTER_PHYSGUN_NOTIFY_CHILDREN, + + { "physgun_interactions", "onbreak", "explode_zap" }, // PROPINTER_PHYSGUN_BREAK_ZAP, +#endif }; #else extern propdata_interaction_s sPropdataInteractionSections[PROPINTER_NUM_INTERACTIONS]; diff --git a/sp/src/game/shared/props_shared.h b/sp/src/game/shared/props_shared.h index 87ce823d227..9ea4d8a5909 100644 --- a/sp/src/game/shared/props_shared.h +++ b/sp/src/game/shared/props_shared.h @@ -72,6 +72,10 @@ enum propdata_interactions_t PROPINTER_PHYSGUN_NOTIFY_CHILDREN, // "onfirstimpact" cause attached flechettes to explode +#ifdef EZ2 + PROPINTER_PHYSGUN_BREAK_ZAP, // "onbreak" "explode_zap" +#endif + // If we get more than 32 of these, we'll need a different system PROPINTER_NUM_INTERACTIONS, diff --git a/sp/src/game/shared/ragdoll_shared.cpp b/sp/src/game/shared/ragdoll_shared.cpp index a9da2613027..494c9ebe446 100644 --- a/sp/src/game/shared/ragdoll_shared.cpp +++ b/sp/src/game/shared/ragdoll_shared.cpp @@ -1178,6 +1178,32 @@ void CRagdollLRURetirement::MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImpo #endif } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Remove it from the LRU +//----------------------------------------------------------------------------- +void CRagdollLRURetirement::RemoveFromLRU( CBaseAnimating *pRagdoll ) +{ + for (int i = 0; i < m_LRU.Count(); i++) + { + if (m_LRU[i].Get() == pRagdoll) + { + m_LRU.Remove( i ); + return; + } + } + + for (int i = 0; i < m_LRUImportantRagdolls.Count(); i++) + { + if (m_LRUImportantRagdolls[i].Get() == pRagdoll) + { + m_LRUImportantRagdolls.Remove( i ); + return; + } + } +} +#endif + //EFFECT/ENTITY TRANSFERS diff --git a/sp/src/game/shared/ragdoll_shared.h b/sp/src/game/shared/ragdoll_shared.h index 5f4f705808a..db2bd7b2dcd 100644 --- a/sp/src/game/shared/ragdoll_shared.h +++ b/sp/src/game/shared/ragdoll_shared.h @@ -114,8 +114,9 @@ class CRagdollLRURetirement : public CAutoGameSystemPerFrame virtual void FrameUpdatePostEntityThink( void ); // Move it to the top of the LRU -#ifdef MAPBASE // From Alien Swarm SDK - void MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant = false, float flForcedRetireTime = 0.0f ); +#ifdef MAPBASE + void MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant = false, float flForcedRetireTime = 0.0f ); // From Alien Swarm SDK + void RemoveFromLRU( CBaseAnimating *pRagdoll ); #else void MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant = false ); #endif diff --git a/sp/src/game/shared/vscript_shared.cpp b/sp/src/game/shared/vscript_shared.cpp index 8ab2ac97d61..c41ddea6fc5 100644 --- a/sp/src/game/shared/vscript_shared.cpp +++ b/sp/src/game/shared/vscript_shared.cpp @@ -624,6 +624,9 @@ class CVScriptSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler if ( g_pScriptVM->GetValue( STRING(pEnt->m_iszScriptId), &variant ) && variant.m_type == FIELD_HSCRIPT ) { pEnt->m_ScriptScope.Init( variant.m_hScript, false ); +#ifdef MAPBASE_VSCRIPT + g_pScriptVM->SetValue( pEnt->m_ScriptScope, "self", pEnt->m_hScriptInstance ); +#endif #ifndef CLIENT_DLL pEnt->RunPrecacheScripts(); #endif diff --git a/sp/src/game/shared/weapon_parse.cpp b/sp/src/game/shared/weapon_parse.cpp index e70e51187cb..cd1ab224015 100644 --- a/sp/src/game/shared/weapon_parse.cpp +++ b/sp/src/game/shared/weapon_parse.cpp @@ -528,6 +528,12 @@ void FileWeaponInfo_t::Parse( KeyValues *pKeyValuesData, const char *szWeaponNam #ifdef EZ2 m_bAlwaysFirstDraw = (pKeyValuesData->GetInt( "AlwaysFirstDraw", 0 ) != 0) ? true : false; m_bPreventPlayerSwap = (pKeyValuesData->GetInt( "PreventPlayerSwap", 0 ) != 0) ? true : false; + + Q_strncpy( szWorldModelDual, pKeyValuesData->GetString( "playermodel_dual" ), MAX_WEAPON_STRING ); + Q_strncpy( szViewModelDual, pKeyValuesData->GetString( "viewmodel_dual" ), MAX_WEAPON_STRING ); + + m_flDynamicScopeFOV = pKeyValuesData->GetFloat( "dynamic_scope_fov", 0.0f ); + m_flDynamicScopeSuitFOV = pKeyValuesData->GetFloat( "dynamic_scope_suit_fov", 0.0f ); #endif #ifndef MAPBASE // Mapbase makes weapons in the same slot & position swap each other out, which is a feature mods can intentionally use. diff --git a/sp/src/game/shared/weapon_parse.h b/sp/src/game/shared/weapon_parse.h index 5ae75f87dfb..42614614e16 100644 --- a/sp/src/game/shared/weapon_parse.h +++ b/sp/src/game/shared/weapon_parse.h @@ -149,6 +149,12 @@ class FileWeaponInfo_t #ifdef EZ2 bool m_bAlwaysFirstDraw; // This weapon defaults to playing the first draw animation, even if dropped by an enemy bool m_bPreventPlayerSwap; // If the player is holding another weapon in the same slot as this weapon, prevent picking up this weapon + + char szWorldModelDual[MAX_WEAPON_STRING]; // World model when dual wielding + char szViewModelDual[MAX_WEAPON_STRING]; // View model when dual wielding + + float m_flDynamicScopeFOV; // Dynamic scope FOV. 0 = No dynamic scope + float m_flDynamicScopeSuitFOV; // If overridden, makes the suit zoom use this FOV (complements dynamic scope) #endif // CLIENT DLL @@ -163,6 +169,11 @@ class FileWeaponInfo_t CHudTexture *iconZoomedCrosshair; CHudTexture *iconZoomedAutoaim; CHudTexture *iconSmall; +#ifdef EZ2 + // For dual wielding + CHudTexture *iconActiveDual; + CHudTexture *iconInactiveDual; +#endif // TF2 specific bool bShowUsageHint; // if true, then when you receive the weapon, show a hint about it diff --git a/sp/src/game/shared/workshop_mount.cpp b/sp/src/game/shared/workshop_mount.cpp new file mode 100644 index 00000000000..1bcef89397d --- /dev/null +++ b/sp/src/game/shared/workshop_mount.cpp @@ -0,0 +1,279 @@ +//=============================================================================// +// +// Purpose: Workshop mounting system. +// +// Author: Blixibon +// +//=============================================================================// + +#include "cbase.h" + +#include "filesystem.h" +#include "tier0/icommandline.h" +#include "steam/steam_api.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define MAX_WORKSHOP_ITEMS 128 + +#define WORKSHOP_MANIFEST_NAME "addoninfo.txt" + +ConVar workshop_mount_vpks( "workshop_mount_vpks", "1" ); +ConVar workshop_game_override( "workshop_game_override", "", FCVAR_CHEAT | FCVAR_REPLICATED, "If a value is specified, then the workshop mounting system will pretend this is the name of the current mod. Use * to mount addons from any game." ); + +//============================================================================= +//============================================================================= +class CWorkshopMountSystem : public CAutoGameSystem +{ +public: + bool Init(); + + void LoadWorkshopItems(); + bool ParseGameParam( const char *pszToken, const char *szModDir ); +}; + +//----------------------------------------------------------------------------- + +CWorkshopMountSystem g_WorkshopMountSystem; + +static int SortStricmp( char *const *sz1, char *const *sz2 ) +{ + return V_stricmp( *sz1, *sz2 ); +} + +bool CWorkshopMountSystem::Init() +{ + LoadWorkshopItems(); + return true; +} + +void CWorkshopMountSystem::LoadWorkshopItems() +{ + if (CommandLine()->FindParm( "-noworkshop" ) != 0) + return; + + uint64 nItemIDs[MAX_WORKSHOP_ITEMS]; + int nNumItems = steamapicontext->SteamUGC()->GetSubscribedItems( nItemIDs, MAX_WORKSHOP_ITEMS ); + + char szFolder[MAX_PATH]; + + char szManifestPath[MAX_PATH]; + + // These are needed for the function even though we don't use them + uint64 nSizeOnDisk; + bool bLegacyItem; + + for (int i = 0; i < nNumItems; i++) + { + if (!steamapicontext->SteamUGC()->GetItemInstallInfo( nItemIDs[i], &nSizeOnDisk, szFolder, sizeof( szFolder ), &bLegacyItem )) + continue; + + V_snprintf( szManifestPath, sizeof( szManifestPath ), "%s%c" WORKSHOP_MANIFEST_NAME, szFolder, CORRECT_PATH_SEPARATOR ); + + // Add path by default in case there is no manifest + bool bAddPath = true; + + KeyValues *pManifest = new KeyValues( "AddonInfo" ); + if (pManifest->LoadFromFile( g_pFullFileSystem, szManifestPath )) + { + //---------------------------------------------------------------------------- + // Addons may target specific games/mods + //---------------------------------------------------------------------------- + const char *pszTargetGames = pManifest->GetString( "game", NULL ); + if (pszTargetGames) + { + // Don't load until we confirm this mod is part of the list + bAddPath = false; + +#ifdef CLIENT_DLL + char szModDir[MAX_PATH]; + const char *pGameDir = CommandLine()->ParmValue( "-game", "hl2" ); + + // Copied from UTIL_GetModDir + Q_strncpy( szModDir, pGameDir, sizeof( szModDir ) ); + if ( Q_strnchr( szModDir, '/', sizeof( szModDir ) ) || Q_strnchr( szModDir, '\\', sizeof( szModDir ) ) ) + { + // Strip the last directory off (which will be our game dir) + Q_StripLastDir( szModDir, sizeof( szModDir ) ); + + // Find the difference in string lengths and take that difference from the original string as the mod dir + int dirlen = Q_strlen( szModDir ); + Q_strncpy( szModDir, pGameDir + dirlen, Q_strlen( pGameDir ) - dirlen + 1 ); + } + + bool bValidGame = true; +#else + char szModDir[MAX_PATH]; + bool bValidGame = UTIL_GetModDir( szModDir, sizeof( szModDir ) ); +#endif + + if (workshop_game_override.GetString()[0]) + { + V_strncpy( szModDir, workshop_game_override.GetString(), sizeof( szModDir ) ); + + if (szModDir[0] == '*') + { + // Just pass without checking the game + bAddPath = true; + bValidGame = false; + } + } + + if (bValidGame) + { + char szTargetGames[128]; + V_strncpy( szTargetGames, pszTargetGames, sizeof( szTargetGames ) ); + + char *pszToken = strtok( szTargetGames, "+" ); + for (; pszToken != NULL; pszToken = strtok( NULL, "+" )) + { + if (!pszToken || !*pszToken) + continue; + + bool bInvert = false; + if (pszToken[0] == '!') // Not this game + { + pszToken++; + bInvert = true; + } + else if (bAddPath) + { + // If we've already been included, then don't care about this game unless it might exclude us + continue; + } + + bool bAllowed = ParseGameParam( pszToken, szModDir ); + if (bInvert) + { + if (bAllowed) + { + // This game was specifically excluded. Early out + bAddPath = false; + break; + } + + // Just because this game wasn't excluded doesn't mean it'll be allowed, so don't do anything + } + else + { + bAddPath = bAllowed; + } + } + } + } + } + pManifest->deleteThis(); + + if (bAddPath) + { + //---------------------------------------------------------------------------- + // Check for VPKs + //---------------------------------------------------------------------------- + CUtlStringList vecVPKs; + if (workshop_mount_vpks.GetBool()) + { + char szVPKSearchPath[MAX_PATH]; + V_snprintf( szVPKSearchPath, sizeof( szVPKSearchPath ), "%s%c*.vpk", szFolder, CORRECT_PATH_SEPARATOR ); + + FileFindHandle_t hFindHandle = NULL; + const char *pszFoundName = g_pFullFileSystem->FindFirst( szVPKSearchPath, &hFindHandle ); + if (pszFoundName) + { + do + { + vecVPKs.CopyAndAddToTail( pszFoundName ); + + pszFoundName = g_pFullFileSystem->FindNext( hFindHandle ); + + } while (pszFoundName); + + g_pFullFileSystem->FindClose( hFindHandle ); + + vecVPKs.Sort( SortStricmp ); + + // Now for any _dir.vpk files, remove the _nnn.vpk ones. + // (Copied from filesystem_init.cpp) + int idx = vecVPKs.Count()-1; + while ( idx > 0 ) + { + char szTemp[ MAX_PATH ]; + V_strcpy_safe( szTemp, vecVPKs[ idx ] ); + --idx; + + char *szDirVpk = V_stristr( szTemp, "_dir.vpk" ); + if ( szDirVpk != NULL ) + { + *szDirVpk = '\0'; + while ( idx >= 0 ) + { + char *pszPath = vecVPKs[ idx ]; + if ( V_stristr( pszPath, szTemp ) != pszPath ) + break; + delete pszPath; + vecVPKs.Remove( idx ); + --idx; + } + } + } + } + } + + if (vecVPKs.Count() > 0) + { + for (int i = 0; i < vecVPKs.Count(); i++) + { + char szVPK[MAX_PATH]; + V_snprintf( szVPK, sizeof( szVPK ), "%s%c%s", szFolder, CORRECT_PATH_SEPARATOR, vecVPKs[i] ); + + DevMsg( "Steam Workshop: Mounting VPK \"%s\"\n", szVPK ); + + filesystem->AddSearchPath( szVPK, "GAME"); + filesystem->AddSearchPath( szVPK, "MOD" ); + filesystem->AddSearchPath( szVPK, "ADDON" ); + } + } + + DevMsg( "Steam Workshop: Mounting folder \"%s\"\n", szFolder ); + + filesystem->AddSearchPath( szFolder, "GAME" ); + filesystem->AddSearchPath( szFolder, "MOD" ); + filesystem->AddSearchPath( szFolder, "ADDON" ); + } + } +} + +bool CWorkshopMountSystem::ParseGameParam( const char *pszToken, const char *pszModDir ) +{ + if (pszToken[0] == '#') // Special commands + { + pszToken++; + + if (FStrEq( pszToken, "episodes" )) + { + // Return true for AP, etc. + if (FStrEq( pszModDir, "axonpariah" ) || FStrEq( pszModDir, "progenitors" )) + return true; + else + return false; + } + else if (FStrEq( pszToken, "all" )) + { + return true; + } + + return false; + } + + return FStrEq( pszModDir, pszToken ); +} + +#ifdef CLIENT_DLL +CON_COMMAND_F( workshop_reload_addons_client, "Reloads addons", FCVAR_CHEAT ) +#else +CON_COMMAND_F( workshop_reload_addons, "Reloads addons", FCVAR_CHEAT ) +#endif +{ + // TODO: Remove addon paths before reloading them? Not sure if necessary + g_WorkshopMountSystem.LoadWorkshopItems(); +} diff --git a/sp/src/materialsystem/stdshaders/BlurFilterY.cpp b/sp/src/materialsystem/stdshaders/BlurFilterY.cpp index 57db29c0dc6..f252fea5eb5 100644 --- a/sp/src/materialsystem/stdshaders/BlurFilterY.cpp +++ b/sp/src/materialsystem/stdshaders/BlurFilterY.cpp @@ -85,7 +85,7 @@ BEGIN_VS_SHADER_FLAGS( BlurFilterY, "Help for BlurFilterY", SHADER_NOT_EDITABLE // The temp buffer is 1/4 back buffer size ITexture *src_texture = params[BASETEXTURE]->GetTextureValue(); - int height = src_texture->GetActualWidth(); + int height = src_texture->GetActualHeight(); float dY = 1.0f / height; // dY *= 0.4; float v[4]; diff --git a/sp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp b/sp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp index 6f64a15d838..2273db49146 100644 --- a/sp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp +++ b/sp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp @@ -1537,7 +1537,7 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** float envMapOrigin[4] = {0,0,0,0}; params[info.m_nEnvmapOrigin]->GetVecValue( envMapOrigin, 3 ); #ifdef MAPBASE - envMapOrigin[4] = bEditorBlend ? 1.0f : 0.0f; + envMapOrigin[3] = bEditorBlend ? 1.0f : 0.0f; #endif pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstant( 21, envMapOrigin ); diff --git a/sp/src/public/soundflags.h b/sp/src/public/soundflags.h index 9fab2308543..2c978c2bc66 100644 --- a/sp/src/public/soundflags.h +++ b/sp/src/public/soundflags.h @@ -102,6 +102,10 @@ enum soundlevel_t #define MAX_SNDLVL_VALUE ((1< 50) ? (20.0f / (float)(a - 50)) : 4.0 ) +#else inline soundlevel_t ATTN_TO_SNDLVL(float a) { soundlevel_t sndlvl = soundlevel_t::SNDLVL_NONE; @@ -118,6 +122,7 @@ inline float SNDLVL_TO_ATTN(soundlevel_t s) { return (s > soundlevel_t::SNDLVL_50dB)? (20.0f / float(s - soundlevel_t::SNDLVL_50dB)) : 4.0f; } +#endif // This is a limit due to network encoding. // It encodes attenuation * 64 in 8 bits, so the maximum is (255 / 64) diff --git a/sp/src/public/tier0/platform.h b/sp/src/public/tier0/platform.h index 8399f4dd798..41c78644126 100644 --- a/sp/src/public/tier0/platform.h +++ b/sp/src/public/tier0/platform.h @@ -1281,8 +1281,8 @@ PLATFORM_INTERFACE bool Is64BitOS(); //----------------------------------------------------------------------------- // General Mapbase version constants compiled into projects for versioning purposes //----------------------------------------------------------------------------- -#define MAPBASE_VERSION "7.2" -#define MAPBASE_VER_INT 7200 // For use in #if in a similar fashion to macros like _MSC_VER +#define MAPBASE_VERSION "7.3" +#define MAPBASE_VER_INT 7300 // For use in #if in a similar fashion to macros like _MSC_VER #endif diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 5efb2314a38..9f46d68c20e 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -2776,7 +2776,7 @@ void* SquirrelVM::GetInstanceValue(HSCRIPT hInstance, ScriptClassDesc_t* pExpect bool SquirrelVM::GenerateUniqueKey(const char* pszRoot, char* pBuf, int nBufSize) { - static int keyIdx = 0; + static unsigned keyIdx = 0; // This gets used for script scope, still confused why it needs to be inside IScriptVM // is it just to be a compatible name for CreateScope? V_snprintf(pBuf, nBufSize, "%08X_%s", ++keyIdx, pszRoot);