From 6f31d8ff47aa3381ffdd987bb54050a98755268f Mon Sep 17 00:00:00 2001 From: Marxvee Date: Sat, 1 Mar 2025 15:51:11 -0600 Subject: [PATCH 1/9] Many in-game visual and mechanical updates, mostly regarding projectiles --- src/game/client/c_baseviewmodel.cpp | 1 + src/game/client/tf/tf_hud_target_id.cpp | 27 +++--- src/game/server/tf/tf_obj.cpp | 17 ++-- src/game/server/tf/tf_projectile_arrow.cpp | 65 +++++++++++++- src/game/server/tf/tf_projectile_flare.cpp | 24 ++++- src/game/server/tf/tf_projectile_flare.h | 5 +- .../tf/halloween/tf_weapon_spellbook.cpp | 15 ++-- src/game/shared/tf/tf_dropped_weapon.cpp | 22 ++--- src/game/shared/tf/tf_gamerules.cpp | 3 +- src/game/shared/tf/tf_item_wearable.cpp | 28 ++++++ src/game/shared/tf/tf_item_wearable.h | 2 + .../shared/tf/tf_projectile_dragons_fury.cpp | 1 + .../shared/tf/tf_projectile_energy_ring.cpp | 6 +- src/game/shared/tf/tf_projectile_nail.cpp | 7 ++ src/game/shared/tf/tf_weapon_bat.cpp | 90 +++++++++++++++++-- src/game/shared/tf/tf_weapon_bat.h | 7 ++ src/game/shared/tf/tf_weapon_bottle.cpp | 2 + src/game/shared/tf/tf_weapon_dragons_fury.cpp | 8 +- .../shared/tf/tf_weapon_grenade_pipebomb.cpp | 1 + src/game/shared/tf/tf_weapon_jar.cpp | 62 ++++++++++++- src/game/shared/tf/tf_weapon_jar.h | 4 + .../shared/tf/tf_weapon_mechanical_arm.cpp | 19 +++- src/game/shared/tf/tf_weapon_throwable.cpp | 4 +- src/game/shared/tf/tf_weaponbase_gun.cpp | 1 + 24 files changed, 360 insertions(+), 61 deletions(-) diff --git a/src/game/client/c_baseviewmodel.cpp b/src/game/client/c_baseviewmodel.cpp index 07a1e71c642..4773a44c456 100644 --- a/src/game/client/c_baseviewmodel.cpp +++ b/src/game/client/c_baseviewmodel.cpp @@ -213,6 +213,7 @@ bool C_BaseViewModel::ShouldFlipViewModel() { return pWeapon->m_bFlipViewModel != cl_flipviewmodels.GetBool(); } + return cl_flipviewmodels.GetBool(); // hack for scout ball projeciles to have properly flipped viewmodels #endif return false; diff --git a/src/game/client/tf/tf_hud_target_id.cpp b/src/game/client/tf/tf_hud_target_id.cpp index a203b5e80ce..ec867222084 100644 --- a/src/game/client/tf/tf_hud_target_id.cpp +++ b/src/game/client/tf/tf_hud_target_id.cpp @@ -72,7 +72,7 @@ void DisableFloatingHealthCallback( IConVar *var, const char *oldString, float o ConVar tf_hud_target_id_disable_floating_health( "tf_hud_target_id_disable_floating_health", "0", FCVAR_ARCHIVE, "Set to disable floating health bar", DisableFloatingHealthCallback ); ConVar tf_hud_target_id_alpha( "tf_hud_target_id_alpha", "100", FCVAR_ARCHIVE, "Alpha value of target id background, default 100" ); ConVar tf_hud_target_id_offset( "tf_hud_target_id_offset", "0", FCVAR_ARCHIVE, "RES file Y offset for target id" ); -ConVar tf_hud_target_id_show_avatars( "tf_hud_target_id_show_avatars", "1", FCVAR_ARCHIVE, "Display Steam avatars on TargetID when using floating health icons. 1 = everyone, 2 = friends only." ); +ConVar tf_hud_target_id_show_avatars( "tf_hud_target_id_show_avatars", "2", FCVAR_ARCHIVE, "Display Steam avatars on TargetID when using floating health icons. 1 = everyone, 2 = friends only." ); bool ShouldHealthBarBeVisible( CBaseEntity *pTarget, CTFPlayer *pLocalPlayer ) @@ -80,15 +80,17 @@ bool ShouldHealthBarBeVisible( CBaseEntity *pTarget, CTFPlayer *pLocalPlayer ) if ( !pTarget || !pLocalPlayer ) return false; - if ( tf_hud_target_id_disable_floating_health.GetBool() ) - return false; - + // now second in priority to force floating health bars on for giant robots in MvM, robot destruction NPCs, and any custom game logic that forces the mini boss flag on players + // regardless of setting due to entities that return this check as true typically not having a visible target ID to fall back to when tf_hud_target_id_disable_floating_health is 1. if ( pTarget->IsHealthBarVisible() ) return true; + if ( tf_hud_target_id_disable_floating_health.GetBool() ) + return false; + if ( !pTarget->IsPlayer() ) return false; - + int iHideEnemyHealth = 0; CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pLocalPlayer, iHideEnemyHealth, hide_enemy_health ); if ( ( iHideEnemyHealth > 0 ) && !pLocalPlayer->InSameTeam( pTarget ) ) @@ -475,7 +477,8 @@ bool CTargetID::IsValidIDTarget( int nEntIndex, float flOldTargetRetainFOV, floa //Recreate the floating health icon if there isn't one, we're not a spectator, and // we're not a spy or this was a robot from Robot Destruction-Mode - if ( !m_pFloatingHealthIcon && !bSpectator && ( !bSpy || bHealthBarVisible ) && !DrawHealthIcon() ) + // force render floating health icon if it's a player miniboss or RD robot. + if ( !m_pFloatingHealthIcon && !bSpectator && ( !bSpy || bHealthBarVisible ) && ( !DrawHealthIcon() || pEnt->IsHealthBarVisible() ) ) { m_pFloatingHealthIcon = CFloatingHealthIcon::AddFloatingHealthIcon( pEnt ); } @@ -794,21 +797,25 @@ void CTargetID::UpdateID( void ) } } + // Remove redundant class checks in favor for the attribute itself, + // and remove second enemy disguised spy check + // This allows for better support ith the see_enemy_health attribute on + // all classes. bool bInSameTeam = pLocalTFPlayer->InSameDisguisedTeam( pEnt ); bool bSpy = pLocalTFPlayer->IsPlayerClass( TF_CLASS_SPY ); - bool bMedic = pLocalTFPlayer->IsPlayerClass( TF_CLASS_MEDIC ); - bool bHeavy = pLocalTFPlayer->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ); + int iSeeEnemyHealth = 0; + CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pLocalTFPlayer, iSeeEnemyHealth, see_enemy_health ) // See if the player wants to fill in the data string bool bIsAmmoData = false; bool bIsKillStreakData = false; pPlayer->GetTargetIDDataString( bDisguisedTarget, sDataString, sizeof(sDataString), bIsAmmoData, bIsKillStreakData ); - if ( pLocalTFPlayer->GetTeamNumber() == TEAM_SPECTATOR || bInSameTeam || bSpy || bDisguisedEnemy || bMedic || bHeavy ) + if ( pLocalTFPlayer->GetTeamNumber() == TEAM_SPECTATOR || bInSameTeam ) { printFormatString = "#TF_playerid_sameteam"; bShowHealth = true; } - else if ( pLocalTFPlayer->m_Shared.GetState() == TF_STATE_DYING ) + else if ( bSpy || iSeeEnemyHealth || pLocalTFPlayer->m_Shared.GetState() == TF_STATE_DYING ) { // We're looking at an enemy who killed us. printFormatString = "#TF_playerid_diffteam"; diff --git a/src/game/server/tf/tf_obj.cpp b/src/game/server/tf/tf_obj.cpp index 629c807fbda..2a8a665e810 100644 --- a/src/game/server/tf/tf_obj.cpp +++ b/src/game/server/tf/tf_obj.cpp @@ -44,6 +44,7 @@ #include "tf_weapon_wrench.h" #include "tf_weapon_grenade_pipebomb.h" #include "tf_weapon_builder.h" +#include "tf_objective_resource.h" #include "player_vs_environment/tf_population_manager.h" @@ -3105,15 +3106,19 @@ void CBaseObject::UpgradeThink( void ) //----------------------------------------------------------------------------- // Purpose: Handles health upgrade for objects we've already built //----------------------------------------------------------------------------- -void CBaseObject::ApplyHealthUpgrade( void ) +void CBaseObject::ApplyHealthUpgrade(void) { - CTFPlayer *pTFPlayer = GetOwner(); - if ( !pTFPlayer ) + CTFPlayer* pTFPlayer = GetOwner(); + if (!pTFPlayer) return; - int iHealth = GetMaxHealthForCurrentLevel(); - SetMaxHealth( iHealth ); - SetHealth( iHealth ); + int iMaxHealth = GetMaxHealthForCurrentLevel(); + SetMaxHealth(iMaxHealth); + // set health up (or down) to max only between waves in MvM, + // or if current health would end up higher than max health (possible via "unbuying" the building health upgrade). + // fixes "infinite" building health exploit in both cases + if (TFObjectiveResource() && !TFObjectiveResource()->GetMannVsMachineIsBetweenWaves() || GetHealth() > iMaxHealth) + SetHealth(iMaxHealth); //DevMsg( "%i\n", GetMaxHealth() ); } diff --git a/src/game/server/tf/tf_projectile_arrow.cpp b/src/game/server/tf/tf_projectile_arrow.cpp index 6ca38190609..dcef50b2506 100644 --- a/src/game/server/tf/tf_projectile_arrow.cpp +++ b/src/game/server/tf/tf_projectile_arrow.cpp @@ -738,11 +738,70 @@ void CTFProjectile_Arrow::ArrowTouch( CBaseEntity *pOther ) if ( m_bStruckEnemy || (GetMoveType() == MOVETYPE_NONE) ) return; - if ( !pOther ) + if (!pOther) return; - bool bShield = pOther->IsCombatItem() && !InSameTeam( pOther ); - CTFPumpkinBomb *pPumpkinBomb = dynamic_cast< CTFPumpkinBomb * >( pOther ); + // Used when checking against things like FUNC_BRUSHES. + // Copied from CTFProjectile_EnergyRing::ProjectileTouch(), + // but it's needed for penetrating arrows so this will only run when m_bPenetrate is true. + if (m_bPenetrate && !pOther->IsWorld() && pOther->GetSolid() == SOLID_VPHYSICS) + { + const trace_t* pTrace = &CBaseEntity::GetTouchTrace(); + CPhysCollide* pTriggerCollide = modelinfo->GetVCollide(GetModelIndex())->solids[0]; + Assert(pTriggerCollide); + + CUtlVector collideList; + IPhysicsObject* pList[VPHYSICS_MAX_OBJECT_LIST_COUNT]; + int physicsCount = pOther->VPhysicsGetObjectList(pList, ARRAYSIZE(pList)); + vcollide_t* pVCollide = modelinfo->GetVCollide(pOther->GetModelIndex()); + + if (physicsCount) + { + for (int i = 0; i < physicsCount; i++) + { + const CPhysCollide* pCollide = pList[i]->GetCollide(); + if (pCollide) + { + collidelist_t element; + element.pCollide = pCollide; + pList[i]->GetPosition(&element.origin, &element.angles); + collideList.AddToTail(element); + } + } + } + else if (pVCollide && pVCollide->solidCount) + { + collidelist_t element; + element.pCollide = pVCollide->solids[0]; + element.origin = pOther->GetAbsOrigin(); + element.angles = pOther->GetAbsAngles(); + collideList.AddToTail(element); + } + else + { + return; + } + + for (int i = collideList.Count() - 1; i >= 0; --i) + { + const collidelist_t& element = collideList[i]; + trace_t tr; + physcollision->TraceCollide(pTrace->startpos, element.origin, element.pCollide, element.angles, pTriggerCollide, GetAbsOrigin(), GetAbsAngles(), &tr); + if (!tr.DidHit()) + return; + } + if (!pOther->IsSolid() || + pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) || + (pOther->GetCollisionGroup() == TFCOLLISION_GROUP_RESPAWNROOMS) || + pOther->IsFuncLOD() || + pOther->GetFlags() & FL_WORLDBRUSH) + { + return; + } + } + + bool bShield = pOther->IsCombatItem() && !InSameTeam(pOther); + CTFPumpkinBomb* pPumpkinBomb = dynamic_cast(pOther); if ( pOther->IsSolidFlagSet( FSOLID_TRIGGER | FSOLID_VOLUME_CONTENTS ) && !pPumpkinBomb && !bShield ) return; diff --git a/src/game/server/tf/tf_projectile_flare.cpp b/src/game/server/tf/tf_projectile_flare.cpp index 1ee417f0256..908d2e2fdcb 100644 --- a/src/game/server/tf/tf_projectile_flare.cpp +++ b/src/game/server/tf/tf_projectile_flare.cpp @@ -299,7 +299,7 @@ void CTFProjectile_Flare::Explode( trace_t *pTrace, CBaseEntity *pOther ) } // If we've already got an impact time, don't impact again. - if ( m_flImpactTime > 0.0 ) + if ( m_flImpactTime > 0.0 || (GetMoveType() == MOVETYPE_NONE) ) return; // Save this entity as enemy, they will take 100% damage. @@ -373,10 +373,26 @@ void CTFProjectile_Flare::Explode( trace_t *pTrace, CBaseEntity *pOther ) EmitSound( filter, pOther->entindex(), "TFPlayer.FlareImpact" ); SendDeathNotice(); - UTIL_Remove( this ); + FadeOut( 1.5 ); // fade out instead of removing immediately for the manmelter flare trail. } } +void CTFProjectile_Flare::FadeOut(int iTime) +{ + SetMoveType(MOVETYPE_NONE); + SetAbsVelocity(vec3_origin); + AddSolidFlags(FSOLID_NOT_SOLID); + AddEffects(EF_NODRAW); + + // Start remove timer. + SetContextThink(&CTFProjectile_Flare::RemoveThink, gpGlobals->curtime + iTime, "FLARE_REMOVE_THINK"); +} + +void CTFProjectile_Flare::RemoveThink(void) +{ + UTIL_Remove( this ); +} + //----------------------------------------------------------------------------- // Purpose: Custom explode for air burst flare //----------------------------------------------------------------------------- @@ -428,7 +444,7 @@ void CTFProjectile_Flare::Explode_Air( trace_t *pTrace, int bitsDamageType, bool CSoundEnt::InsertSound ( SOUND_COMBAT, vecOrigin, 1024, 3.0 ); SendDeathNotice(); - UTIL_Remove( this ); + FadeOut( 1.5 ); // fade out instead of removing immediately for the manmelter flare trail. } //----------------------------------------------------------------------------- @@ -489,7 +505,7 @@ void CTFProjectile_Flare::ImpactThink( void ) } SendDeathNotice(); - UTIL_Remove( this ); + FadeOut( 3.0 ); // fade out instead of removing immediately for the manmelter flare trail. SetContextThink( NULL, 0, FLARE_THINK_CONTEXT ); } else diff --git a/src/game/server/tf/tf_projectile_flare.h b/src/game/server/tf/tf_projectile_flare.h index 0d4b1af4372..3c76da26805 100644 --- a/src/game/server/tf/tf_projectile_flare.h +++ b/src/game/server/tf/tf_projectile_flare.h @@ -65,7 +65,10 @@ class CTFProjectile_Flare : public CTFBaseRocket, public IScorer void SetCritical( bool bCritical ) { m_bCritical = bCritical; } virtual int GetDamageType(); - virtual bool IsDeflectable() { return true; } + void FadeOut(int iTime); + void RemoveThink(); + + virtual bool IsDeflectable() OVERRIDE { return (GetMoveType() != MOVETYPE_NONE); } virtual void Deflected( CBaseEntity *pDeflectedBy, Vector &vecDir ); virtual bool IsDestroyable( bool bOrbAttack = false ) OVERRIDE { return ( !bOrbAttack ? false : true ); } diff --git a/src/game/shared/tf/halloween/tf_weapon_spellbook.cpp b/src/game/shared/tf/halloween/tf_weapon_spellbook.cpp index fbfb75d5a66..175a0440e06 100644 --- a/src/game/shared/tf/halloween/tf_weapon_spellbook.cpp +++ b/src/game/shared/tf/halloween/tf_weapon_spellbook.cpp @@ -939,14 +939,14 @@ void CTFSpellBook::TossJarThink( void ) Vector vecForward, vecRight, vecUp; AngleVectors( pPlayer->EyeAngles(), &vecForward, &vecRight, &vecUp ); - float fRight = 8.f; + float fRight = 7.f; if ( IsViewModelFlipped() ) { fRight *= -1; } Vector vecSrc = pPlayer->Weapon_ShootPosition(); // Make spell toss position at the hand - vecSrc = vecSrc + (vecUp * -9.0f) + (vecRight * 7.0f) + (vecForward * 3.0f); + vecSrc = vecSrc + (vecUp * -9.0f) + (vecRight * fRight) + (vecForward * 3.0f); Vector vecVelocity = GetVelocityVector( vecForward, vecRight, vecUp ) * pSpellData->m_flSpeedScale; QAngle angForward = pPlayer->EyeAngles(); @@ -1417,6 +1417,7 @@ bool CTFSpellBook::CastRocketJump( CTFPlayer *pPlayer ) CTakeDamageInfo info; info.SetAttacker( pPlayer ); info.SetInflictor( pPlayer ); + info.SetWeapon( pPlayer->GetActiveWeapon() ); info.SetDamage( 20.f ); info.SetDamageCustom( TF_DMG_CUSTOM_SPELL_BLASTJUMP ); info.SetDamagePosition( origin ); @@ -1580,7 +1581,7 @@ class CTFProjectile_SpellFireball : public CTFProjectile_Rocket virtual void RocketTouch( CBaseEntity *pOther ) OVERRIDE { Assert( pOther ); - if ( !pOther || !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) || pOther->IsFuncLOD() ) + if ( !pOther || !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) || pOther->IsFuncLOD() || pOther->GetFlags() & FL_WORLDBRUSH ) return; if ( pOther->GetParent() == GetOwnerEntity() ) @@ -1716,6 +1717,8 @@ class CTFProjectile_SpellFireball : public CTFProjectile_Rocket if ( pBaseTarget->GetTeamNumber() == GetTeamNumber() ) return; + CBaseEntity* pInflictor = GetLauncher(); + if ( pTarget ) { if ( pTarget->m_Shared.IsInvulnerable() ) @@ -1724,13 +1727,13 @@ class CTFProjectile_SpellFireball : public CTFProjectile_Rocket if ( pTarget->m_Shared.InCond( TF_COND_PHASE ) || pTarget->m_Shared.InCond( TF_COND_PASSTIME_INTERCEPTION ) ) return; - pTarget->m_Shared.SelfBurn( 5.0f ); + // self burn from an enemy projectile just feels wrong. credit the damager properly + pTarget->m_Shared.Burn( pThrower, dynamic_cast( pInflictor ), 5.0f ); } const trace_t *pTrace = &CBaseEntity::GetTouchTrace(); trace_t *pNewTrace = const_cast( pTrace ); - CBaseEntity *pInflictor = GetLauncher(); CTakeDamageInfo info; info.SetAttacker( pThrower ); info.SetInflictor( this ); @@ -2809,7 +2812,7 @@ class CTFProjectile_SpellLightningOrb : public CTFProjectile_SpellFireball virtual void RocketTouch( CBaseEntity *pOther ) OVERRIDE { Assert( pOther ); - if ( !pOther || !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) || pOther->IsFuncLOD() ) + if ( !pOther || !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) || pOther->IsFuncLOD() || pOther->GetFlags() & FL_WORLDBRUSH ) return; const trace_t *pTrace = &CBaseEntity::GetTouchTrace(); diff --git a/src/game/shared/tf/tf_dropped_weapon.cpp b/src/game/shared/tf/tf_dropped_weapon.cpp index 2479acbb145..fd316d5c9cf 100644 --- a/src/game/shared/tf/tf_dropped_weapon.cpp +++ b/src/game/shared/tf/tf_dropped_weapon.cpp @@ -572,19 +572,21 @@ void CTFDroppedWeapon::InitDroppedWeapon( CTFPlayer *pPlayer, CTFWeaponBase *pWe } } - if ( bIsSuicide ) - { - m_flChargeLevel = 0.f; - } - else + + CWeaponMedigun* pMedigun = dynamic_cast(pWeapon); + if (pMedigun) { - CWeaponMedigun *pMedigun = dynamic_cast< CWeaponMedigun* >( pWeapon ); - if ( pMedigun ) + SetBodygroup(1, 1); // previously from when weapons were ammo boxes- removes medigun hose + if (bIsSuicide) + { + m_flChargeLevel = 0.f; + } + else { - m_flChargeLevel.Set( pMedigun->GetChargeLevel() ); - if ( m_flChargeLevel > 0.f ) + m_flChargeLevel.Set(pMedigun->GetChargeLevel()); + if (m_flChargeLevel > 0.f) { - SetContextThink( &CTFDroppedWeapon::ChargeLevelDegradeThink, gpGlobals->curtime + 0.1f, "ChargeLevelDegradeThink" ); + SetContextThink(&CTFDroppedWeapon::ChargeLevelDegradeThink, gpGlobals->curtime + 0.1f, "ChargeLevelDegradeThink"); } } } diff --git a/src/game/shared/tf/tf_gamerules.cpp b/src/game/shared/tf/tf_gamerules.cpp index cc43b0f42ac..27615c5575e 100644 --- a/src/game/shared/tf/tf_gamerules.cpp +++ b/src/game/shared/tf/tf_gamerules.cpp @@ -5679,7 +5679,8 @@ void CTFGameRules::RadiusDamage( CTFRadiusDamageInfo &info ) //----------------------------------------------------------------------------- void CTFRadiusDamageInfo::CalculateFalloff( void ) { - if ( dmgInfo->GetDamageType() & DMG_RADIUS_MAX ) + // hack to ignore caber explosion, otherwise the charge minicrit explosion would actually do less damage + if (dmgInfo->GetDamageCustom() != TF_DMG_CUSTOM_STICKBOMB_EXPLOSION && dmgInfo->GetDamageType() & DMG_RADIUS_MAX) flFalloff = 0.f; else if ( dmgInfo->GetDamageType() & DMG_HALF_FALLOFF ) flFalloff = 0.5f; diff --git a/src/game/shared/tf/tf_item_wearable.cpp b/src/game/shared/tf/tf_item_wearable.cpp index 2a1054208f5..84b8b60019d 100644 --- a/src/game/shared/tf/tf_item_wearable.cpp +++ b/src/game/shared/tf/tf_item_wearable.cpp @@ -812,6 +812,34 @@ ShadowType_t CTFWearableVM::ShadowCastType( void ) return SHADOWS_RENDER_TO_TEXTURE; } + +//----------------------------------------------------------------------------- +// Purpose: +// Allow botkiller attachments and server mod viewmodel VMs to properly flip with the player's weapon. +//----------------------------------------------------------------------------- +int CTFWearableVM::InternalDrawModel(int flags) +{ + C_TFPlayer* pOwner = ToTFPlayer(GetOwnerEntity()); + + CMatRenderContextPtr pRenderContext(materials); + + if (pOwner) + { + CTFWeaponBase* pWpn = pOwner->GetActiveTFWeapon(); + + if (pWpn) + { + if (pWpn->IsViewModelFlipped()) + pRenderContext->CullMode(MATERIAL_CULLMODE_CW); + } + } + + int ret = BaseClass::InternalDrawModel(flags); + + pRenderContext->CullMode(MATERIAL_CULLMODE_CCW); + + return ret; +} #endif diff --git a/src/game/shared/tf/tf_item_wearable.h b/src/game/shared/tf/tf_item_wearable.h index f89c0c956a9..201acf4ec9b 100644 --- a/src/game/shared/tf/tf_item_wearable.h +++ b/src/game/shared/tf/tf_item_wearable.h @@ -109,6 +109,8 @@ class CTFWearableVM : public CTFWearable #if defined( CLIENT_DLL ) virtual ShadowType_t ShadowCastType( void ); + + virtual int InternalDrawModel(int flags); #endif }; diff --git a/src/game/shared/tf/tf_projectile_dragons_fury.cpp b/src/game/shared/tf/tf_projectile_dragons_fury.cpp index cb247eb0435..2042fb8a7f6 100644 --- a/src/game/shared/tf/tf_projectile_dragons_fury.cpp +++ b/src/game/shared/tf/tf_projectile_dragons_fury.cpp @@ -181,6 +181,7 @@ class CTFProjectile_BallOfFire : public CTFProjectile_Rocket pOther->IsSolidFlagSet( FSOLID_NOT_SOLID ) || ( pOther->GetCollisionGroup() == TFCOLLISION_GROUP_RESPAWNROOMS ) || pOther->IsFuncLOD() || + pOther->GetFlags() & FL_WORLDBRUSH || // hack for func_brushes pOther->IsBaseProjectile() ) { return; diff --git a/src/game/shared/tf/tf_projectile_energy_ring.cpp b/src/game/shared/tf/tf_projectile_energy_ring.cpp index d4229ac82fa..a53cd15f565 100644 --- a/src/game/shared/tf/tf_projectile_energy_ring.cpp +++ b/src/game/shared/tf/tf_projectile_energy_ring.cpp @@ -222,7 +222,8 @@ void CTFProjectile_EnergyRing::ProjectileTouch( CBaseEntity *pOther ) !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) || ( pOther->GetCollisionGroup() == TFCOLLISION_GROUP_RESPAWNROOMS ) || - pOther->IsFuncLOD() ) + pOther->IsFuncLOD() || + pOther->GetFlags() & FL_WORLDBRUSH) // Hack for func_brushes { return; } @@ -257,7 +258,8 @@ void CTFProjectile_EnergyRing::ProjectileTouch( CBaseEntity *pOther ) if ( bCombatEntity ) { // Bison projectiles shouldn't collide with friendly things - if ( ShouldPenetrate() && ( pOther->InSameTeam( this ) || ( gpGlobals->curtime - m_flLastHitTime ) < tf_bison_tick_time.GetFloat() ) ) + // Pomson projectiles shouldn't collide with nearby teammates + if ( ( ShouldPenetrate() || !CanCollideWithTeammates() ) && ( pOther->InSameTeam( this ) || ( gpGlobals->curtime - m_flLastHitTime ) < tf_bison_tick_time.GetFloat() ) ) return; m_flLastHitTime = gpGlobals->curtime; diff --git a/src/game/shared/tf/tf_projectile_nail.cpp b/src/game/shared/tf/tf_projectile_nail.cpp index f944acfba79..e806211d9f4 100644 --- a/src/game/shared/tf/tf_projectile_nail.cpp +++ b/src/game/shared/tf/tf_projectile_nail.cpp @@ -55,6 +55,13 @@ CTFBaseProjectile *CTFProjectile_Syringe::Create( CBaseEntity *pScorer /*= NULL*/, bool bCritical /*= false */ ) { + float flVelocity = SYRINGE_VELOCITY; + if (pLauncher) // customizable syringe spread and range for server mods. + { + CALL_ATTRIB_HOOK_FLOAT_ON_OTHER(pLauncher, flVelocity, mult_projectile_speed); + CALL_ATTRIB_HOOK_FLOAT_ON_OTHER(pLauncher, flVelocity, mult_projectile_range); + } + return CTFBaseProjectile::Create( "tf_projectile_syringe", vecOrigin, vecAngles, pOwner, SYRINGE_VELOCITY, g_sModelIndexSyringe, SYRINGE_DISPATCH_EFFECT, pScorer, bCritical ); } diff --git a/src/game/shared/tf/tf_weapon_bat.cpp b/src/game/shared/tf/tf_weapon_bat.cpp index e159a7e7506..e86e28cd404 100644 --- a/src/game/shared/tf/tf_weapon_bat.cpp +++ b/src/game/shared/tf/tf_weapon_bat.cpp @@ -29,7 +29,8 @@ #endif const float DEFAULT_ORNAMENT_EXPLODE_RADIUS = 50.0f; -const float DEFAULT_ORNAMENT_EXPLODE_DAMAGE_MULT = 0.9f; +const float DEFAULT_ORNAMENT_EXPLODE_DAMAGE_MULT = 1.8f; // this for some reason explodes twice in vanilla if it hits a player, so compensate damage here +//const float DEFAULT_ORNAMENT_EXPLODE_DAMAGE_MULT = 0.9f; //============================================================================= // @@ -659,6 +660,8 @@ const char *CTFStunBall::GetBallViewModelName( void ) const //----------------------------------------------------------------------------- void CTFStunBall::Spawn( void ) { + SetDetonateTimerLength(FLT_MAX); // makes the ball never fizzle out mid-air + BaseClass::Spawn(); SetModel( GetBallModelName() ); @@ -952,7 +955,8 @@ bool CTFStunBall::ShouldBallTouch( CBaseEntity *pOther ) if ( !pOther || !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) || - pOther->GetCollisionGroup() == TFCOLLISION_GROUP_RESPAWNROOMS ) + pOther->GetCollisionGroup() == TFCOLLISION_GROUP_RESPAWNROOMS || + pOther->GetFlags() & FL_WORLDBRUSH ) { pOwner->SpeakConceptIfAllowed( MP_CONCEPT_BALL_MISSED ); return false; @@ -1003,6 +1007,14 @@ void CTFStunBall::IncrementDeflected(void) CreateBallTrail(); } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFStunBall::DetonateThink(void) +{ + // nothing here, so the ball never fizzles out +} + // -- SERVER ONLY #endif @@ -1071,6 +1083,28 @@ void CTFStunBall::CreateTrailParticles( void ) } } } + +//----------------------------------------------------------------------------- +// Purpose: Removes particles as projectile now simply fades out instead of instnatly deleting itself +//----------------------------------------------------------------------------- +void CTFStunBall::OnDataChanged(DataUpdateType_t updateType) +{ + BaseClass::OnDataChanged(updateType); + + // Remove normal effect if we're inactive + if (GetMoveType() == MOVETYPE_NONE && pEffectTrail) + { + ParticleProp()->StopEmission(pEffectTrail); + pEffectTrail = NULL; + } + + // Remove crit effect if we're inactive + if (GetMoveType() == MOVETYPE_NONE && pEffectCrit) + { + ParticleProp()->StopEmission(pEffectCrit); + pEffectCrit = NULL; + } +} #endif @@ -1236,12 +1270,29 @@ void CTFBall_Ornament::ApplyBallImpactEffectOnVictim( CBaseEntity *pOther ) pPlayer->DispatchTraceAttack( info, dir, pNewTrace ); ApplyMultiDamage(); - // the ball shatters - UTIL_Remove( this ); + // the ball shatters, but the entity is kept for a few seonds for a little bit while the trail finishes. + FadeOut( 1.5 ); m_bTouched = true; } +void CTFBall_Ornament::FadeOut(int iTime) +{ + SetMoveType(MOVETYPE_NONE); + SetAbsVelocity(vec3_origin); + AddSolidFlags(FSOLID_NOT_SOLID); + AddEffects(EF_NODRAW); + + + // Start remove timer. + SetContextThink(&CTFBall_Ornament::RemoveThink, gpGlobals->curtime + iTime, "ORNAMENT_REMOVE_THINK"); +} + +void CTFBall_Ornament::RemoveThink(void) +{ + UTIL_Remove(this); +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -1250,6 +1301,9 @@ void CTFBall_Ornament::PipebombTouch( CBaseEntity *pOther ) if ( !ShouldBallTouch( pOther ) ) return; + if ( (GetMoveType() == MOVETYPE_NONE)) + return; + trace_t pTrace; Vector velDir = GetAbsVelocity(); VectorNormalize( velDir ); @@ -1259,8 +1313,17 @@ void CTFBall_Ornament::PipebombTouch( CBaseEntity *pOther ) if ( pOther == GetThrower() ) return; + // Don't collide with teammates if we're still in the grace period. + if (pOther->IsPlayer() && InSameTeam(pOther) && !CanCollideWithTeammates()) + return; + // Explode (does radius damage, triggers particles and sound effects). - Explode( &pTrace, DMG_BLAST|DMG_PREVENT_PHYSICS_FORCE ); + int iDmgType = DMG_BLAST | DMG_PREVENT_PHYSICS_FORCE; + if ( IsCritical() ) + { + iDmgType |= DMG_CRITICAL; + } + Explode( &pTrace, iDmgType); if ( !InSameTeam( pOther ) && pOther->m_takedamage != DAMAGE_NO ) { @@ -1301,7 +1364,12 @@ void CTFBall_Ornament::VPhysicsCollisionThink( void ) Vector vecSpot = GetAbsOrigin() - velDir * 16; UTIL_TraceLine( vecSpot, vecSpot + velDir * 32, MASK_SOLID, this, COLLISION_GROUP_NONE, &pTrace ); - Explode( &pTrace, DMG_BLAST|DMG_PREVENT_PHYSICS_FORCE ); + int iDmgType = DMG_BLAST | DMG_PREVENT_PHYSICS_FORCE; + if ( IsCritical() ) + { + iDmgType |= DMG_CRITICAL; + } + Explode( &pTrace, iDmgType ); } //----------------------------------------------------------------------------- @@ -1309,6 +1377,12 @@ void CTFBall_Ornament::VPhysicsCollisionThink( void ) //----------------------------------------------------------------------------- void CTFBall_Ornament::Explode( trace_t *pTrace, int bitsDamageType ) { + // ornament is already expiring, don't explode again + // this seems to lower damage if the ball directly hits a player, as the bauble would actually explode twice before this fix + // this behavior seemed unintentional, but it ultimately ends up with the weapon dealing 4-13 less damage. too bad! + if (GetMoveType() == MOVETYPE_NONE) + return; + // Create smashed glass particles when we explode if ( GetTeamNumber() == TF_TEAM_RED ) { @@ -1342,8 +1416,8 @@ void CTFBall_Ornament::Explode( trace_t *pTrace, int bitsDamageType ) CTFRadiusDamageInfo radiusinfo( &info, vecOrigin, DEFAULT_ORNAMENT_EXPLODE_RADIUS, nullptr, 0.0f, 0.0f ); TFGameRules()->RadiusDamage( radiusinfo ); - UTIL_Remove( this ); + if ( GetMoveType() != MOVETYPE_NONE ) + FadeOut( 1.5 ); } - #endif diff --git a/src/game/shared/tf/tf_weapon_bat.h b/src/game/shared/tf/tf_weapon_bat.h index cfd2fd4e165..9880f725118 100644 --- a/src/game/shared/tf/tf_weapon_bat.h +++ b/src/game/shared/tf/tf_weapon_bat.h @@ -150,6 +150,7 @@ class CTFStunBall : public CTFGrenadePipebombProjectile virtual bool IsAllowedToExplode( void ) OVERRIDE { return false; } virtual void Explode( trace_t *pTrace, int bitsDamageType ); + virtual void DetonateThink() OVERRIDE; virtual void ApplyBallImpactEffectOnVictim( CBaseEntity *pOther ); virtual void PipebombTouch( CBaseEntity *pOther ); virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ); @@ -178,6 +179,7 @@ class CTFStunBall : public CTFGrenadePipebombProjectile #else virtual const char *GetTrailParticleName( void ); virtual void CreateTrailParticles( void ); + virtual void OnDataChanged(DataUpdateType_t updateType); #endif @@ -236,6 +238,11 @@ class CTFBall_Ornament : public CTFStunBall virtual void ApplyBallImpactEffectOnVictim( CBaseEntity *pOther ); + void FadeOut(int iTime); + void RemoveThink(); + + virtual bool IsDeflectable() OVERRIDE { return (GetMoveType() != MOVETYPE_NONE); } + protected: Vector m_vCollisionVelocity; #endif diff --git a/src/game/shared/tf/tf_weapon_bottle.cpp b/src/game/shared/tf/tf_weapon_bottle.cpp index 308224f8368..636f466a743 100644 --- a/src/game/shared/tf/tf_weapon_bottle.cpp +++ b/src/game/shared/tf/tf_weapon_bottle.cpp @@ -256,6 +256,8 @@ void CTFStickBomb::Smack( void ) int dmgType = DMG_BLAST | DMG_NOCLOSEDISTANCEMOD; if ( IsCurrentAttackACrit() ) dmgType |= DMG_CRITICAL; + else if (m_bMiniCrit) // the explosion minicrits from the charge minicrit, too + dmgType |= DMG_RADIUS_MAX; CTakeDamageInfo info( pTFPlayer, pTFPlayer, this, explosion, explosion, 75.0f, dmgType, TF_DMG_CUSTOM_STICKBOMB_EXPLOSION, &explosion ); CTFRadiusDamageInfo radiusinfo( &info, explosion, 100.f ); diff --git a/src/game/shared/tf/tf_weapon_dragons_fury.cpp b/src/game/shared/tf/tf_weapon_dragons_fury.cpp index c5d50367197..677442c77a4 100644 --- a/src/game/shared/tf/tf_weapon_dragons_fury.cpp +++ b/src/game/shared/tf/tf_weapon_dragons_fury.cpp @@ -109,6 +109,8 @@ void CTFWeaponFlameBall::PrimaryAttack( void ) #else C_CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() ); #endif + // the DF does not list a "no random crits" stat, therefore it should random crit + CalcIsAttackCritical(); SendWeaponAnim( ACT_VM_PRIMARYATTACK ); @@ -147,14 +149,14 @@ CBaseEntity* CTFWeaponFlameBall::FireProjectile( CTFPlayer *pPlayer ) Vector vecForward, vecRight, vecUp; AngleVectors( pPlayer->EyeAngles(), &vecForward, &vecRight, &vecUp ); - float fRight = 8.f; + float fRight = 7.f; if ( IsViewModelFlipped() ) { fRight *= -1; } Vector vecSrc = pPlayer->Weapon_ShootPosition(); // Shoot from the right location - vecSrc = vecSrc + (vecUp * -9.0f) + (vecRight * 7.0f) + (vecForward * 3.0f); + vecSrc = vecSrc + (vecUp * -9.0f) + (vecRight * fRight) + (vecForward * 3.0f); QAngle angForward = pPlayer->EyeAngles(); @@ -180,7 +182,7 @@ CBaseEntity* CTFWeaponFlameBall::FireProjectile( CTFPlayer *pPlayer ) pRocket->SetDamage( 20 ); pRocket->ChangeTeam( pPlayer->GetTeamNumber() ); - pRocket->SetCritical( pPlayer->m_Shared.IsCritBoosted() ); + pRocket->SetCritical( IsCurrentAttackACrit() ); DispatchSpawn( pRocket ); diff --git a/src/game/shared/tf/tf_weapon_grenade_pipebomb.cpp b/src/game/shared/tf/tf_weapon_grenade_pipebomb.cpp index 9c7f1f68d86..48331e5cebb 100644 --- a/src/game/shared/tf/tf_weapon_grenade_pipebomb.cpp +++ b/src/game/shared/tf/tf_weapon_grenade_pipebomb.cpp @@ -781,6 +781,7 @@ void CTFGrenadePipebombProjectile::PipebombTouch( CBaseEntity *pOther ) // Impact damage scales with distance float flDistanceSq = (pOther->GetAbsOrigin() - pAttacker->GetAbsOrigin()).LengthSqr(); float flImpactDamage = RemapValClamped( flDistanceSq, 512 * 512, 1024 * 1024, 50, 25 ); + CALL_ATTRIB_HOOK_FLOAT_ON_OTHER(GetLauncher(), flImpactDamage, mult_dmg); CTakeDamageInfo info( this, pAttacker, GetOriginalLauncher(), vec3_origin, vOrigin, flImpactDamage, GetDamageType(), TF_DMG_CUSTOM_CANNONBALL_PUSH ); pOther->TakeDamage( info ); diff --git a/src/game/shared/tf/tf_weapon_jar.cpp b/src/game/shared/tf/tf_weapon_jar.cpp index 2f071c0cee5..a6aa0e7682d 100644 --- a/src/game/shared/tf/tf_weapon_jar.cpp +++ b/src/game/shared/tf/tf_weapon_jar.cpp @@ -11,8 +11,10 @@ // Client specific. #ifdef CLIENT_DLL #include "c_tf_player.h" +#include "c_basedoor.h" // Server specific. #else +#include "doors.h" #include "soundent.h" #include "te_effect_dispatch.h" #include "tf_player.h" @@ -539,7 +541,8 @@ void CTFProjectile_Jar::PipebombTouch( CBaseEntity *pOther ) { // Exception to this rule - if we're a jar or milk, and our potential victim is on fire, then allow collision after all. // If we're a jar or milk, then still allow collision if our potential victim is on fire. - if (m_iProjectileType == TF_PROJECTILE_JAR || m_iProjectileType == TF_PROJECTILE_JAR_MILK) + // This condition only applies to non-cleaver jars, since there's so many jar types capable of extinguishing. + if ( m_iProjectileType != TF_PROJECTILE_CLEAVER ) { auto victim = ToTFPlayer(pOther); if (!victim->m_Shared.InCond(TF_COND_BURNING)) @@ -943,8 +946,40 @@ CTFProjectile_Jar *CTFCleaver::CreateJarProjectile( const Vector &position, cons { return CTFProjectile_Cleaver::Create( position, angles, velocity, angVelocity, pOwner, weaponInfo, GetSkin() ); } + #endif +//----------------------------------------------------------------------------- +// Purpose: Determines if there is space to create a cleaver. +// The player technically has a 0.1s window between this passing through and then aiming against the wall +// to get the cleaver through the it, but this is difficult to do so this bug has been effectively mitigated. +//----------------------------------------------------------------------------- +bool CTFCleaver::CanCreateCleaver(CTFPlayer* pPlayer) +{ + Vector vecForward, vecUp; + AngleVectors(pPlayer->EyeAngles(), &vecForward, NULL, &vecUp); + Vector vecCleaverStart = pPlayer->GetAbsOrigin() + Vector(0, 0, 50); + Vector vecCleaverlEnd = vecCleaverStart + vecForward * 32.f; + + // Trace out and see if we hit a wall. + trace_t trace; + CTraceFilterSimple traceFilter(this, COLLISION_GROUP_NONE); + UTIL_TraceHull(vecCleaverStart, vecCleaverlEnd, -Vector(8, 8, 8), Vector(8, 8, 8), MASK_SOLID_BRUSHONLY, &traceFilter, &trace); + if (trace.DidHitWorld() || trace.startsolid) + return false; + else + { + if (trace.m_pEnt) + { + // Don't let the player throw the cleaver through doors. + CBaseDoor* pDoor = dynamic_cast(trace.m_pEnt); + if (pDoor) + return false; + } + return true; + } +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -953,6 +988,23 @@ float CTFCleaver::GetProjectileSpeed( void ) return TF_CLEAVER_LAUNCH_SPEED; } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFCleaver::PrimaryAttack(void) +{ + CTFPlayer* pPlayer = GetTFPlayerOwner(); + if (!pPlayer) + return; + + if ( CanCreateCleaver(pPlayer) ) + return BaseClass::PrimaryAttack(); + + return; +} + +//----------------------------------------------------------------------------- +// Purpose: //----------------------------------------------------------------------------- void CTFCleaver::SecondaryAttack( void ) { @@ -1131,6 +1183,14 @@ void CTFProjectile_Cleaver::Detonate( void ) Explode( &tr, GetDamageType() ); } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFProjectile_Cleaver::DetonateThink(void) +{ + // nothing here, so the cleaver never fizzles out +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/src/game/shared/tf/tf_weapon_jar.h b/src/game/shared/tf/tf_weapon_jar.h index f95aadc4342..b41eb8e0272 100644 --- a/src/game/shared/tf/tf_weapon_jar.h +++ b/src/game/shared/tf/tf_weapon_jar.h @@ -114,12 +114,15 @@ class CTFCleaver : public CTFJar virtual int GetWeaponID( void ) const { return TF_WEAPON_CLEAVER; } virtual float GetProjectileSpeed( void ); + virtual void PrimaryAttack(void); virtual void SecondaryAttack( void ); virtual const char* GetEffectLabelText( void ) { return "#TF_CLEAVER"; } virtual float InternalGetEffectBarRechargeTime( void ) { return 5.1; } + virtual bool CanCreateCleaver(CTFPlayer* pPlayer); + #ifdef GAME_DLL virtual const AngularImpulse GetAngularImpulse( void ){ return AngularImpulse( 0, 500, 0 ); } virtual Vector GetVelocityVector( const Vector &vecForward, const Vector &vecRight, const Vector &vecUp ); @@ -244,6 +247,7 @@ class CTFProjectile_Cleaver : public CTFProjectile_Jar virtual void OnHit( CBaseEntity *pOther ) OVERRIDE; virtual void Explode( trace_t *pTrace, int bitsDamageType ) OVERRIDE; virtual void Detonate() OVERRIDE; + virtual void DetonateThink() OVERRIDE; virtual const char* GetImpactEffect() OVERRIDE { return ""; } virtual ETFCond GetEffectCondition( void ) OVERRIDE { return TF_COND_BLEEDING; } diff --git a/src/game/shared/tf/tf_weapon_mechanical_arm.cpp b/src/game/shared/tf/tf_weapon_mechanical_arm.cpp index b64d5574e66..12d08aa539e 100644 --- a/src/game/shared/tf/tf_weapon_mechanical_arm.cpp +++ b/src/game/shared/tf/tf_weapon_mechanical_arm.cpp @@ -250,12 +250,13 @@ void CTFMechanicalArm::SecondaryAttack( void ) if ( ShockAttack() ) { + CalcIsAttackCritical(); WeaponSound( SPECIAL3 ); #ifdef GAME_DLL Vector vecForward, vecRight, vecUp; AngleVectors( pOwner->EyeAngles(), &vecForward, &vecRight, &vecUp ); - float fRight = 8.f; + float fRight = 15.f; if ( IsViewModelFlipped() ) { fRight *= -1; @@ -264,7 +265,7 @@ void CTFMechanicalArm::SecondaryAttack( void ) // vecSrc = vecSrc + ( vecUp * -9.0f ) + ( vecRight * 7.0f ) + ( vecForward * 3.0f ); Vector vecSrc = pOwner->EyePosition() + ( vecForward * 40.f ) - + ( vecRight * 15.f ) + + ( vecRight * fRight ) + ( vecUp * -10.f ); QAngle angForward = pOwner->EyeAngles(); @@ -287,7 +288,8 @@ void CTFMechanicalArm::SecondaryAttack( void ) pOrb->SetAbsVelocity( vForward * tf_mecharm_orb_speed ); pOrb->ChangeTeam( pOwner->GetTeamNumber() ); - pOrb->SetCritical( false ); + pOrb->SetCritical( IsCurrentAttackACrit() ); +// pOrb->SetCritical( false ); DispatchSpawn( pOrb ); } @@ -479,6 +481,8 @@ void CTFMechanicalArm::PrimaryAttack() //int nAmmoToTake = bShocked ? 0 : GetAmmoPerShot(); //pOwner->RemoveAmmo( nAmmoToTake, m_iPrimaryAmmoType ); + CalcIsAttackCritical(); // fix for weapon being incapable of dealing critical hits with primary fire + FireProjectile( pPlayer ); #endif @@ -745,7 +749,14 @@ void CTFProjectile_MechanicalArmOrb::CheckForPlayers( int nNumToZap, bool bCanHi info.SetDamage( tf_mecharm_orb_zap_damage ); info.SetDamageCustom( TF_DMG_CUSTOM_PLASMA ); info.SetDamagePosition( GetAbsOrigin() ); - info.SetDamageType( DMG_SHOCK ); + + // the short circuit already can't random crit, so this is in place for when the owner is crit boosted via external means. + int iDmgType = DMG_SHOCK; + if ( IsCritical() ) + { + iDmgType |= DMG_CRITICAL; + } + info.SetDamageType( iDmgType ); CBaseEntity *pListOfEntities[5]; int iEntities = UTIL_EntitiesInSphere( pListOfEntities, 5, GetAbsOrigin(), tf_mecharm_orb_size, FL_CLIENT | FL_FAKECLIENT | FL_NPC ); diff --git a/src/game/shared/tf/tf_weapon_throwable.cpp b/src/game/shared/tf/tf_weapon_throwable.cpp index 3e853c40ec7..4ab63e4cac8 100644 --- a/src/game/shared/tf/tf_weapon_throwable.cpp +++ b/src/game/shared/tf/tf_weapon_throwable.cpp @@ -313,7 +313,7 @@ CTFProjectile_Throwable *CTFThrowable::FireProjectileInternal( void ) Vector vecForward, vecRight, vecUp; AngleVectors( pPlayer->EyeAngles(), &vecForward, &vecRight, &vecUp ); - float fRight = 8.f; + float fRight = 7.f; if ( IsViewModelFlipped() ) { fRight *= -1; @@ -321,7 +321,7 @@ CTFProjectile_Throwable *CTFThrowable::FireProjectileInternal( void ) Vector vecSrc = pPlayer->Weapon_ShootPosition(); // Make spell toss position at the hand - vecSrc = vecSrc + ( vecUp * -9.0f ) + ( vecRight * 7.0f ) + ( vecForward * 3.0f ); + vecSrc = vecSrc + ( vecUp * -9.0f ) + ( vecRight * fRight ) + ( vecForward * 3.0f ); trace_t trace; Vector vecEye = pPlayer->EyePosition(); diff --git a/src/game/shared/tf/tf_weaponbase_gun.cpp b/src/game/shared/tf/tf_weaponbase_gun.cpp index 246b660bb94..ed2a02f41c7 100644 --- a/src/game/shared/tf/tf_weaponbase_gun.cpp +++ b/src/game/shared/tf/tf_weaponbase_gun.cpp @@ -636,6 +636,7 @@ CBaseEntity *CTFWeaponBaseGun::FireNail( CTFPlayer *pPlayer, int iSpecificNail ) // Add some spread float flSpread = 1.5; flSpread += GetProjectileSpread(); + CALL_ATTRIB_HOOK_FLOAT(flSpread, mult_spread_scale); // accuracy multiplier support for server mods CTFBaseProjectile *pProjectile = NULL; switch( iSpecificNail ) From a6cad1a9ed4ec25bc3756b3597b324a3be97d516 Mon Sep 17 00:00:00 2001 From: Marxvee Date: Sat, 1 Mar 2025 16:07:10 -0600 Subject: [PATCH 2/9] Add new value to tf_hud_target_id_disable_floating_health 2 now mimics the new behavior, while 1 is the old, pre-patch behavior. --- src/game/client/tf/tf_hud_target_id.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/game/client/tf/tf_hud_target_id.cpp b/src/game/client/tf/tf_hud_target_id.cpp index ec867222084..6471b3f02e7 100644 --- a/src/game/client/tf/tf_hud_target_id.cpp +++ b/src/game/client/tf/tf_hud_target_id.cpp @@ -69,7 +69,7 @@ void DisableFloatingHealthCallback( IConVar *var, const char *oldString, float o pTargetID->InvalidateLayout(); } } -ConVar tf_hud_target_id_disable_floating_health( "tf_hud_target_id_disable_floating_health", "0", FCVAR_ARCHIVE, "Set to disable floating health bar", DisableFloatingHealthCallback ); +ConVar tf_hud_target_id_disable_floating_health( "tf_hud_target_id_disable_floating_health", "0", FCVAR_ARCHIVE, "Set to disable floating health bar. 1 = everyone, 2 = normal players only.", DisableFloatingHealthCallback ); ConVar tf_hud_target_id_alpha( "tf_hud_target_id_alpha", "100", FCVAR_ARCHIVE, "Alpha value of target id background, default 100" ); ConVar tf_hud_target_id_offset( "tf_hud_target_id_offset", "0", FCVAR_ARCHIVE, "RES file Y offset for target id" ); ConVar tf_hud_target_id_show_avatars( "tf_hud_target_id_show_avatars", "2", FCVAR_ARCHIVE, "Display Steam avatars on TargetID when using floating health icons. 1 = everyone, 2 = friends only." ); @@ -82,7 +82,7 @@ bool ShouldHealthBarBeVisible( CBaseEntity *pTarget, CTFPlayer *pLocalPlayer ) // now second in priority to force floating health bars on for giant robots in MvM, robot destruction NPCs, and any custom game logic that forces the mini boss flag on players // regardless of setting due to entities that return this check as true typically not having a visible target ID to fall back to when tf_hud_target_id_disable_floating_health is 1. - if ( pTarget->IsHealthBarVisible() ) + if ( pTarget->IsHealthBarVisible() && tf_hud_target_id_disable_floating_health.GetInt() != 1 ) return true; if ( tf_hud_target_id_disable_floating_health.GetBool() ) @@ -478,7 +478,7 @@ bool CTargetID::IsValidIDTarget( int nEntIndex, float flOldTargetRetainFOV, floa //Recreate the floating health icon if there isn't one, we're not a spectator, and // we're not a spy or this was a robot from Robot Destruction-Mode // force render floating health icon if it's a player miniboss or RD robot. - if ( !m_pFloatingHealthIcon && !bSpectator && ( !bSpy || bHealthBarVisible ) && ( !DrawHealthIcon() || pEnt->IsHealthBarVisible() ) ) + if ( !m_pFloatingHealthIcon && !bSpectator && ( !bSpy || bHealthBarVisible ) && ( !DrawHealthIcon() || pEnt->IsHealthBarVisible() && tf_hud_target_id_disable_floating_health.GetInt() != 1 ) ) { m_pFloatingHealthIcon = CFloatingHealthIcon::AddFloatingHealthIcon( pEnt ); } From aa74d32a06acd0c170df9822422d2ef7543843fb Mon Sep 17 00:00:00 2001 From: Marxvee Date: Sat, 1 Mar 2025 17:37:44 -0600 Subject: [PATCH 3/9] reverted func_brush changes for DF, SC, and spellbooks these didn't work cleanly, and need to be redone or done by someone with more experience (cries) bison/pomson and arrows still wrok as normal though, so yay! --- src/game/shared/tf/halloween/tf_weapon_spellbook.cpp | 4 ++-- src/game/shared/tf/tf_projectile_dragons_fury.cpp | 2 +- src/game/shared/tf/tf_weapon_mechanical_arm.cpp | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/game/shared/tf/halloween/tf_weapon_spellbook.cpp b/src/game/shared/tf/halloween/tf_weapon_spellbook.cpp index 175a0440e06..074629930c8 100644 --- a/src/game/shared/tf/halloween/tf_weapon_spellbook.cpp +++ b/src/game/shared/tf/halloween/tf_weapon_spellbook.cpp @@ -1581,7 +1581,7 @@ class CTFProjectile_SpellFireball : public CTFProjectile_Rocket virtual void RocketTouch( CBaseEntity *pOther ) OVERRIDE { Assert( pOther ); - if ( !pOther || !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) || pOther->IsFuncLOD() || pOther->GetFlags() & FL_WORLDBRUSH ) + if ( !pOther || !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) || pOther->IsFuncLOD() ) return; if ( pOther->GetParent() == GetOwnerEntity() ) @@ -2812,7 +2812,7 @@ class CTFProjectile_SpellLightningOrb : public CTFProjectile_SpellFireball virtual void RocketTouch( CBaseEntity *pOther ) OVERRIDE { Assert( pOther ); - if ( !pOther || !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) || pOther->IsFuncLOD() || pOther->GetFlags() & FL_WORLDBRUSH ) + if ( !pOther || !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) || pOther->IsFuncLOD() ) return; const trace_t *pTrace = &CBaseEntity::GetTouchTrace(); diff --git a/src/game/shared/tf/tf_projectile_dragons_fury.cpp b/src/game/shared/tf/tf_projectile_dragons_fury.cpp index 2042fb8a7f6..8027ade3ef5 100644 --- a/src/game/shared/tf/tf_projectile_dragons_fury.cpp +++ b/src/game/shared/tf/tf_projectile_dragons_fury.cpp @@ -181,7 +181,7 @@ class CTFProjectile_BallOfFire : public CTFProjectile_Rocket pOther->IsSolidFlagSet( FSOLID_NOT_SOLID ) || ( pOther->GetCollisionGroup() == TFCOLLISION_GROUP_RESPAWNROOMS ) || pOther->IsFuncLOD() || - pOther->GetFlags() & FL_WORLDBRUSH || // hack for func_brushes +// pOther->GetFlags() & FL_WORLDBRUSH && !pOther->IsWorld() || // hack for func_brushes pOther->IsBaseProjectile() ) { return; diff --git a/src/game/shared/tf/tf_weapon_mechanical_arm.cpp b/src/game/shared/tf/tf_weapon_mechanical_arm.cpp index 12d08aa539e..b7d3674dff2 100644 --- a/src/game/shared/tf/tf_weapon_mechanical_arm.cpp +++ b/src/game/shared/tf/tf_weapon_mechanical_arm.cpp @@ -647,6 +647,9 @@ bool CTFProjectile_MechanicalArmOrb::ShouldProjectileIgnore( CBaseEntity *pOther if ( pOther->IsFuncLOD() ) return true; + if ( pOther->GetFlags() & FL_WORLDBRUSH ) + return true; + const trace_t *pTrace = &CBaseEntity::GetTouchTrace(); if ( pTrace->surface.flags & CONTENTS_LADDER ) return true; From aa421b14b3cae0dda776f14d875889f7628b6069 Mon Sep 17 00:00:00 2001 From: Marxvee Date: Sat, 1 Mar 2025 18:06:44 -0600 Subject: [PATCH 4/9] Remove unnecessary flare changes i may be stupid ONLY wrap assassin baubles really need this change. flares still have a trail behind them in live tf2 --- src/game/server/tf/tf_projectile_flare.cpp | 24 ++++------------------ src/game/server/tf/tf_projectile_flare.h | 5 +---- 2 files changed, 5 insertions(+), 24 deletions(-) diff --git a/src/game/server/tf/tf_projectile_flare.cpp b/src/game/server/tf/tf_projectile_flare.cpp index 908d2e2fdcb..1ee417f0256 100644 --- a/src/game/server/tf/tf_projectile_flare.cpp +++ b/src/game/server/tf/tf_projectile_flare.cpp @@ -299,7 +299,7 @@ void CTFProjectile_Flare::Explode( trace_t *pTrace, CBaseEntity *pOther ) } // If we've already got an impact time, don't impact again. - if ( m_flImpactTime > 0.0 || (GetMoveType() == MOVETYPE_NONE) ) + if ( m_flImpactTime > 0.0 ) return; // Save this entity as enemy, they will take 100% damage. @@ -373,26 +373,10 @@ void CTFProjectile_Flare::Explode( trace_t *pTrace, CBaseEntity *pOther ) EmitSound( filter, pOther->entindex(), "TFPlayer.FlareImpact" ); SendDeathNotice(); - FadeOut( 1.5 ); // fade out instead of removing immediately for the manmelter flare trail. + UTIL_Remove( this ); } } -void CTFProjectile_Flare::FadeOut(int iTime) -{ - SetMoveType(MOVETYPE_NONE); - SetAbsVelocity(vec3_origin); - AddSolidFlags(FSOLID_NOT_SOLID); - AddEffects(EF_NODRAW); - - // Start remove timer. - SetContextThink(&CTFProjectile_Flare::RemoveThink, gpGlobals->curtime + iTime, "FLARE_REMOVE_THINK"); -} - -void CTFProjectile_Flare::RemoveThink(void) -{ - UTIL_Remove( this ); -} - //----------------------------------------------------------------------------- // Purpose: Custom explode for air burst flare //----------------------------------------------------------------------------- @@ -444,7 +428,7 @@ void CTFProjectile_Flare::Explode_Air( trace_t *pTrace, int bitsDamageType, bool CSoundEnt::InsertSound ( SOUND_COMBAT, vecOrigin, 1024, 3.0 ); SendDeathNotice(); - FadeOut( 1.5 ); // fade out instead of removing immediately for the manmelter flare trail. + UTIL_Remove( this ); } //----------------------------------------------------------------------------- @@ -505,7 +489,7 @@ void CTFProjectile_Flare::ImpactThink( void ) } SendDeathNotice(); - FadeOut( 3.0 ); // fade out instead of removing immediately for the manmelter flare trail. + UTIL_Remove( this ); SetContextThink( NULL, 0, FLARE_THINK_CONTEXT ); } else diff --git a/src/game/server/tf/tf_projectile_flare.h b/src/game/server/tf/tf_projectile_flare.h index 3c76da26805..0d4b1af4372 100644 --- a/src/game/server/tf/tf_projectile_flare.h +++ b/src/game/server/tf/tf_projectile_flare.h @@ -65,10 +65,7 @@ class CTFProjectile_Flare : public CTFBaseRocket, public IScorer void SetCritical( bool bCritical ) { m_bCritical = bCritical; } virtual int GetDamageType(); - void FadeOut(int iTime); - void RemoveThink(); - - virtual bool IsDeflectable() OVERRIDE { return (GetMoveType() != MOVETYPE_NONE); } + virtual bool IsDeflectable() { return true; } virtual void Deflected( CBaseEntity *pDeflectedBy, Vector &vecDir ); virtual bool IsDestroyable( bool bOrbAttack = false ) OVERRIDE { return ( !bOrbAttack ? false : true ); } From 2fcfbba3efd58319d854e129a7f777129940f599 Mon Sep 17 00:00:00 2001 From: Marxvee Date: Sat, 1 Mar 2025 18:42:27 -0600 Subject: [PATCH 5/9] Fix recursion in cvar default value whoops (x3) --- src/game/client/tf/tf_hud_target_id.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/client/tf/tf_hud_target_id.cpp b/src/game/client/tf/tf_hud_target_id.cpp index 6471b3f02e7..ad81b945afb 100644 --- a/src/game/client/tf/tf_hud_target_id.cpp +++ b/src/game/client/tf/tf_hud_target_id.cpp @@ -72,7 +72,7 @@ void DisableFloatingHealthCallback( IConVar *var, const char *oldString, float o ConVar tf_hud_target_id_disable_floating_health( "tf_hud_target_id_disable_floating_health", "0", FCVAR_ARCHIVE, "Set to disable floating health bar. 1 = everyone, 2 = normal players only.", DisableFloatingHealthCallback ); ConVar tf_hud_target_id_alpha( "tf_hud_target_id_alpha", "100", FCVAR_ARCHIVE, "Alpha value of target id background, default 100" ); ConVar tf_hud_target_id_offset( "tf_hud_target_id_offset", "0", FCVAR_ARCHIVE, "RES file Y offset for target id" ); -ConVar tf_hud_target_id_show_avatars( "tf_hud_target_id_show_avatars", "2", FCVAR_ARCHIVE, "Display Steam avatars on TargetID when using floating health icons. 1 = everyone, 2 = friends only." ); +ConVar tf_hud_target_id_show_avatars( "tf_hud_target_id_show_avatars", "1", FCVAR_ARCHIVE, "Display Steam avatars on TargetID when using floating health icons. 1 = everyone, 2 = friends only." ); bool ShouldHealthBarBeVisible( CBaseEntity *pTarget, CTFPlayer *pLocalPlayer ) From 1e08863cb8d4b8c10d51cda75b4aacb1d2da2300 Mon Sep 17 00:00:00 2001 From: Marxvee Date: Sat, 1 Mar 2025 18:44:47 -0600 Subject: [PATCH 6/9] Remove redundant detonate timer The detonate think never actually thinks, so this is unneeded --- src/game/shared/tf/tf_weapon_bat.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/game/shared/tf/tf_weapon_bat.cpp b/src/game/shared/tf/tf_weapon_bat.cpp index e86e28cd404..b1e6d3e9f0d 100644 --- a/src/game/shared/tf/tf_weapon_bat.cpp +++ b/src/game/shared/tf/tf_weapon_bat.cpp @@ -660,8 +660,6 @@ const char *CTFStunBall::GetBallViewModelName( void ) const //----------------------------------------------------------------------------- void CTFStunBall::Spawn( void ) { - SetDetonateTimerLength(FLT_MAX); // makes the ball never fizzle out mid-air - BaseClass::Spawn(); SetModel( GetBallModelName() ); From 797bc8815396c152be25729fd111a8476ee91e28 Mon Sep 17 00:00:00 2001 From: Marxvee Date: Sat, 1 Mar 2025 19:07:30 -0600 Subject: [PATCH 7/9] Add spells and throwables as exceptions to burning teammate check --- src/game/shared/tf/tf_weapon_jar.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/game/shared/tf/tf_weapon_jar.cpp b/src/game/shared/tf/tf_weapon_jar.cpp index a6aa0e7682d..0346132d4ba 100644 --- a/src/game/shared/tf/tf_weapon_jar.cpp +++ b/src/game/shared/tf/tf_weapon_jar.cpp @@ -542,7 +542,9 @@ void CTFProjectile_Jar::PipebombTouch( CBaseEntity *pOther ) // Exception to this rule - if we're a jar or milk, and our potential victim is on fire, then allow collision after all. // If we're a jar or milk, then still allow collision if our potential victim is on fire. // This condition only applies to non-cleaver jars, since there's so many jar types capable of extinguishing. - if ( m_iProjectileType != TF_PROJECTILE_CLEAVER ) + if ( m_iProjectileType != TF_PROJECTILE_CLEAVER && + m_iProjectileType != TF_PROJECTILE_SPELL && + m_iProjectileType != TF_PROJECTILE_THROWABLE) { auto victim = ToTFPlayer(pOther); if (!victim->m_Shared.InCond(TF_COND_BURNING)) From b1d9a654672a9871072838189c3971afab279e23 Mon Sep 17 00:00:00 2001 From: Marxvee Date: Sat, 1 Mar 2025 22:17:29 -0600 Subject: [PATCH 8/9] Added support for item meter rechargable cabers Attributes need to be defined to use this (the behavior is unaltered from live tf2 otherwise), but this enables the Ullapool Caber to regenerate upon getting a full item meter charge. --- src/game/client/tf/tf_hud_itemeffectmeter.cpp | 1 + src/game/shared/tf/tf_weapon_bottle.cpp | 14 ++++++++++++++ src/game/shared/tf/tf_weapon_bottle.h | 1 + 3 files changed, 16 insertions(+) diff --git a/src/game/client/tf/tf_hud_itemeffectmeter.cpp b/src/game/client/tf/tf_hud_itemeffectmeter.cpp index ef2e6df8c77..7f8fda038dc 100644 --- a/src/game/client/tf/tf_hud_itemeffectmeter.cpp +++ b/src/game/client/tf/tf_hud_itemeffectmeter.cpp @@ -298,6 +298,7 @@ void CHudItemEffectMeter::CreateHudElementsForClass( C_TFPlayer* pPlayer, CUtlVe } case TF_CLASS_DEMOMAN: DECLARE_ITEM_EFFECT_METER( CTFSword, TF_WEAPON_SWORD, false, "resource/UI/HudItemEffectMeter_Demoman.res" ); + lambdaAddItemEffectMeter("tf_weapon_stickbomb", true); break; case TF_CLASS_SOLDIER: diff --git a/src/game/shared/tf/tf_weapon_bottle.cpp b/src/game/shared/tf/tf_weapon_bottle.cpp index 636f466a743..bc5712ee122 100644 --- a/src/game/shared/tf/tf_weapon_bottle.cpp +++ b/src/game/shared/tf/tf_weapon_bottle.cpp @@ -225,6 +225,11 @@ void CTFStickBomb::Smack( void ) { m_iDetonated = 1; m_bBroken = true; + CTFPlayer* pOwner = GetTFPlayerOwner(); + if (pOwner) + { + pOwner->m_Shared.SetItemChargeMeter(LOADOUT_POSITION_MELEE, 0.f); + } SwitchBodyGroups(); #ifdef GAME_DLL @@ -347,3 +352,12 @@ void RecvProxy_Detonated( const CRecvProxyData *pData, void *pStruct, void *pOut } #endif + +void CTFStickBomb::OnResourceMeterFilled() +{ + CTFPlayer* pOwner = GetTFPlayerOwner(); + if (!pOwner) + return; + + WeaponRegenerate(); +} \ No newline at end of file diff --git a/src/game/shared/tf/tf_weapon_bottle.h b/src/game/shared/tf/tf_weapon_bottle.h index 1c886d8bf58..21d003811da 100644 --- a/src/game/shared/tf/tf_weapon_bottle.h +++ b/src/game/shared/tf/tf_weapon_bottle.h @@ -102,6 +102,7 @@ class CTFStickBomb : public CTFBreakableMelee void SetDetonated( int iVal ) { m_iDetonated = iVal; } int GetDetonated( void ) { return m_iDetonated; } + virtual void OnResourceMeterFilled() OVERRIDE; private: From c6e0b9bbeac7248f6002f45bd464851d7fad601a Mon Sep 17 00:00:00 2001 From: Marxvee Date: Sat, 1 Mar 2025 23:09:24 -0600 Subject: [PATCH 9/9] Fix cow mangler crit damage rampup bug If the Cow Mangler is crit boosted in vanilla TF2, the minicrit damage will not have any ramp up. This has been fixed --- src/game/shared/tf/tf_gamerules.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/game/shared/tf/tf_gamerules.cpp b/src/game/shared/tf/tf_gamerules.cpp index 27615c5575e..aff16df1069 100644 --- a/src/game/shared/tf/tf_gamerules.cpp +++ b/src/game/shared/tf/tf_gamerules.cpp @@ -6533,6 +6533,10 @@ bool CTFGameRules::ApplyOnDamageModifyRules( CTakeDamageInfo &info, CBaseEntity int iForceCritDmgFalloff = 0; CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iForceCritDmgFalloff, crit_dmg_falloff ); + // Move this higher to fix the lack of minicrit rampup damage upon being crit boosted. + int iDemoteCritToMinicrit = 0; + CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iDemoteCritToMinicrit, crits_become_minicrits ); + #ifdef MCOMS_BALANCE_PACK // SMG headshots falloff if ( pWeapon && pWeapon->GetWeaponID() == TF_WEAPON_SMG ) @@ -6552,7 +6556,7 @@ bool CTFGameRules::ApplyOnDamageModifyRules( CTakeDamageInfo &info, CBaseEntity ( ( bCrit && tf_weapon_criticals_distance_falloff.GetBool() ) || ( info.GetCritType() == CTakeDamageInfo::CRIT_MINI && tf_weapon_minicrits_distance_falloff.GetBool() ) || ( iForceCritDmgFalloff ) ); - bool bDoShortRangeDistanceIncrease = !bCrit || info.GetCritType() == CTakeDamageInfo::CRIT_MINI ; + bool bDoShortRangeDistanceIncrease = !bCrit || iDemoteCritToMinicrit != 0 || info.GetCritType() == CTakeDamageInfo::CRIT_MINI ; bool bDoLongRangeDistanceDecrease = !bIgnoreLongRangeDmgEffects && ( bForceCritFalloff || ( !bCrit && info.GetCritType() != CTakeDamageInfo::CRIT_MINI ) ); // If we're doing any distance modification, we need to do that first @@ -6782,8 +6786,6 @@ bool CTFGameRules::ApplyOnDamageModifyRules( CTakeDamageInfo &info, CBaseEntity } else if ( bCrit ) { - int iDemoteCritToMinicrit = 0; - CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, iDemoteCritToMinicrit, crits_become_minicrits ); if ( iDemoteCritToMinicrit != 0 ) { bitsDamage &= ~DMG_CRITICAL; // this is to shutup the assert in lambdaDoMinicrit