2222#include " entity.h"
2323#include " entity/cbasemodelentity.h"
2424#include " utils.h"
25+ #include " vprof.h"
2526
2627CMapMigrations* g_pMapMigrations = nullptr ;
2728
@@ -45,6 +46,7 @@ void CMapMigrations::ApplyGameSettings(KeyValues* pKV)
4546void CMapMigrations::OnRoundPrestart ()
4647{
4748 m_vecModelEntitiesUsingRendermodeEnum.clear ();
49+ m_vecEquippedWeapons.clear ();
4850}
4951
5052void CMapMigrations::OnEntitySpawned (CEntityInstance* pEntity, const CEntityKeyValues* pKeyValues)
@@ -59,6 +61,12 @@ void CMapMigrations::OnEntitySpawned(CEntityInstance* pEntity, const CEntityKeyV
5961 RunMigrations (pBaseEntity);
6062}
6163
64+ void CMapMigrations::OnEquipWeapon (CBasePlayerWeapon* pWeapon)
65+ {
66+ if (Migrations20260420Enabled ())
67+ Migrations_20260420 (pWeapon);
68+ }
69+
6270void CMapMigrations::RunMigrations (CBaseEntity* pEntity)
6371{
6472 if (g_cvarMapMigrations20260121.Get () == 1 || (g_cvarMapMigrations20260121.Get () == 2 && m_timeMapUpdated < g_time20260121))
@@ -99,22 +107,54 @@ void CMapMigrations::Migrations_20260121(CBaseEntity* pEntity)
99107 }
100108}
101109
102- void CMapMigrations::UpdateMapUpdateTime ( time_t timeMapUpdated )
110+ void CMapMigrations::Migrations_20260420 (CBasePlayerWeapon* pWeapon )
103111{
104- m_timeMapUpdated = timeMapUpdated;
112+ VPROF (" CMapMigrations::Migrations_20260420" );
113+
114+ // We only care about map-spawned weapons
115+ if (!V_strcmp (pWeapon->m_sUniqueHammerID ().Get (), " " ))
116+ return ;
117+
118+ // And only their first equip
119+ for (int i = 0 ; i < m_vecEquippedWeapons.size (); i++)
120+ if (m_vecEquippedWeapons[i] == pWeapon->GetHandle ())
121+ return ;
105122
123+ m_vecEquippedWeapons.push_back (pWeapon->GetHandle ());
106124 CBaseEntity* pTarget = nullptr ;
107125
108- // May be called late, so also check any existing entities first
126+ // Entities parented to weapons being held by players were offset by +40 units following the AG2 update
127+ // Since that doesn't affect in-world weapons, this migration is delayed until weapon equip to prevent breaking strip triggers etc
128+ // Alternatively, we may want to track weapon children ahead of time via OnEntityParentChanged if this ends up becoming a performance concern
109129 while ((pTarget = UTIL_FindEntityByClassname (pTarget, " *" )))
110- RunMigrations (pTarget);
130+ {
131+ CGameSceneNode* pParentSceneNode = pTarget->m_CBodyComponent ()->m_pSceneNode ()->m_pParent ();
132+
133+ if (pParentSceneNode && pParentSceneNode->m_pOwner () == pWeapon)
134+ {
135+ Vector newOrigin = pTarget->GetAbsOrigin ();
136+ newOrigin.z -= 40 .0f ;
137+ pTarget->Teleport (&newOrigin, nullptr , nullptr );
138+ }
139+ }
111140}
112141
113142bool CMapMigrations::Migrations20260420Enabled ()
114143{
115144 return g_cvarMapMigrations20260420.Get () == 1 || (g_cvarMapMigrations20260420.Get () == 2 && m_timeMapUpdated < g_time20260420);
116145}
117146
147+ void CMapMigrations::UpdateMapUpdateTime (time_t timeMapUpdated)
148+ {
149+ m_timeMapUpdated = timeMapUpdated;
150+
151+ CBaseEntity* pTarget = nullptr ;
152+
153+ // May be called late, so also check any existing entities first
154+ while ((pTarget = UTIL_FindEntityByClassname (pTarget, " *" )))
155+ RunMigrations (pTarget);
156+ }
157+
118158std::shared_ptr<CMapMigrationWorkshopDetailsQuery> CMapMigrationWorkshopDetailsQuery::Create (uint64 iWorkshopId)
119159{
120160 if (!GetSteamUGC ())
0 commit comments