from pathlib import Path import subprocess,hashlib,json root=Path('/home/jakub/devel/escapistsrereintroduced'); out=Path('/tmp/erri-pair-service-0054'); rel='addons/core/Scripts/Game/ERR/10_AI/ERR_ConvoyService.c' s=(root/rel).read_text(); p=out/rel;p.parent.mkdir(parents=True,exist_ok=True) b=out/'baseline'/rel;b.parent.mkdir(parents=True,exist_ok=True);b.write_text(s) idx=subprocess.run(['git','show',':'+rel],cwd=root,capture_output=True,check=True).stdout (out/'baseline.json').write_text(json.dumps({'path':rel,'working_sha256':hashlib.sha256(s.encode()).hexdigest(),'index_sha256':hashlib.sha256(idx).hexdigest()},indent=2)+'\n') needle=' bool DeliverIntercept(ERR_GameMode mode, ERR_ConvoyInterceptOrder order, float now)' insert=''' // Pair preparation owns no route state. Recheck after both native allocations. bool CanInstallIntercept(ERR_GameMode mode, ERR_ConvoyInterceptOrder order, float now) { if (!InterceptMatches(order) || order.Token != m_InterceptToken || InterceptActive || !InterceptCrew(mode)) return false; return order.Position == m_InterceptPosition && order.ExpiresAt == m_InterceptExpiresAt && now < m_InterceptExpiresAt; } // Caller transfers ownership of the prepared handle before AddWaypoint, which // is a native boundary rather than a transactional pair operation. void InstallIntercept(AIWaypoint detour) { ClearOrder(); m_InterceptRouteIndex = RouteIndex; m_InterceptWaypoint = detour; InterceptWaypoint = detour; InterceptActive = true; Group.AddWaypoint(detour); } ''' assert needle in s;s=s.replace(needle,insert+needle,1) needle=' bool ApplyIntercept(ERR_ConvoyTask task, ERR_ConvoyInterceptOrder order, float now)' insert=''' protected bool InterceptPairEligible(ERR_ConvoyTask task, ERR_ConvoyInterceptOrder first, ERR_ConvoyInterceptOrder second, float now) { if (!first || !second || first == second || first.Group == second.Group || first.VehicleIndex == second.VehicleIndex || first.Token == second.Token) return false; if (!InterceptEligible(task, first) || !InterceptEligible(task, second) || !ERR_ForceRules.Finite(now) || now < 0) return false; if (task.Vehicles[first.VehicleIndex].Entity == task.Vehicles[second.VehicleIndex].Entity) return false; if (first.BeliefId <= 0 || first.BeliefRevision <= 0 || first.ReportId <= 0) return false; if (first.BeliefId != second.BeliefId || first.BeliefRevision != second.BeliefRevision || first.ReportId != second.ReportId) return false; if (first.ObservedAt != second.ObservedAt || first.IssuedAt != second.IssuedAt || first.ExpiresAt != second.ExpiresAt) return false; if (!ERR_ForceRules.Finite(first.ObservedAt) || !ERR_ForceRules.Finite(first.IssuedAt) || !ERR_ForceRules.Finite(first.ExpiresAt)) return false; if (first.ObservedAt < 0 || first.IssuedAt < first.ObservedAt || now < first.IssuedAt || now >= first.ExpiresAt || first.ExpiresAt > first.ObservedAt + 120000) return false; if (!ERR_ForceRules.FiniteVector(first.Position) || !ERR_ForceRules.FiniteVector(second.Position)) return false; return vector.DistanceXZ(first.Position, second.Position) >= 40; } bool ReserveInterceptPair(ERR_ConvoyTask task, ERR_ConvoyInterceptOrder first, ERR_ConvoyInterceptOrder second, float now) { if (!InterceptPairEligible(task, first, second, now)) return false; foreach (ERR_ConvoyTask existing : m_Tasks) foreach (ERR_ConvoyVehicle claimed : existing.Vehicles) if (claimed && claimed.InterceptToken != 0) return false; ERR_ConvoyVehicle lead = task.Vehicles[first.VehicleIndex]; ERR_ConvoyVehicle partner = task.Vehicles[second.VehicleIndex]; if (!lead.ClaimIntercept(m_Mode, first)) return false; lead.InterceptReceipt = task.Receipt; lead.InterceptRecord = m_Mode.Forces().FindGroup(lead.Group); lead.InterceptRoute = task.Route; if (!InterceptPairEligible(task, first, second, now) || !partner.ClaimIntercept(m_Mode, second)) { lead.ClearIntercept(first); return false; } partner.InterceptReceipt = task.Receipt; partner.InterceptRecord = m_Mode.Forces().FindGroup(partner.Group); partner.InterceptRoute = task.Route; return true; } bool ApplyInterceptPair(ERR_ConvoyTask task, ERR_ConvoyInterceptOrder first, ERR_ConvoyInterceptOrder second, float now) { if (!InterceptPairEligible(task, first, second, now) || !InterceptCurrent(task, first) || !InterceptCurrent(task, second)) return false; ERR_ConvoyVehicle lead = task.Vehicles[first.VehicleIndex]; ERR_ConvoyVehicle partner = task.Vehicles[second.VehicleIndex]; if (!lead.CanInstallIntercept(m_Mode, first, now) || !partner.CanInstallIntercept(m_Mode, second, now)) return false; AIWaypoint firstWaypoint = AIWaypoint.Cast(ERR_Actors.Spawn(ERR_Resources.MoveWaypoint(), first.Position)); if (!firstWaypoint) return false; AIWaypoint secondWaypoint = AIWaypoint.Cast(ERR_Actors.Spawn(ERR_Resources.MoveWaypoint(), second.Position)); if (!secondWaypoint) { SCR_EntityHelper.DeleteEntityAndChildren(firstWaypoint); return false; } firstWaypoint.SetCompletionRadius(15); secondWaypoint.SetCompletionRadius(15); if (!InterceptPairEligible(task, first, second, now) || !InterceptCurrent(task, first) || !InterceptCurrent(task, second) || task.Vehicles[first.VehicleIndex] != lead || task.Vehicles[second.VehicleIndex] != partner || !lead.CanInstallIntercept(m_Mode, first, now) || !partner.CanInstallIntercept(m_Mode, second, now)) { SCR_EntityHelper.DeleteEntityAndChildren(firstWaypoint); SCR_EntityHelper.DeleteEntityAndChildren(secondWaypoint); return false; } lead.InstallIntercept(firstWaypoint); // AddWaypoint can invoke native listeners. Do not install the partner after // cancellation/ownership loss; release only these exact owned claims. if (!InterceptPairEligible(task, first, second, now) || !InterceptCurrent(task, first) || !lead.InterceptActive || !InterceptCurrent(task, second) || !partner.CanInstallIntercept(m_Mode, second, now)) { SCR_EntityHelper.DeleteEntityAndChildren(secondWaypoint); ReleaseIntercept(task, first, now); ReleaseIntercept(task, second, now); return false; } partner.InstallIntercept(secondWaypoint); if (InterceptPairEligible(task, first, second, now) && InterceptCurrent(task, first) && InterceptCurrent(task, second) && lead.InterceptActive && partner.InterceptActive) return true; ReleaseIntercept(task, first, now); ReleaseIntercept(task, second, now); return false; } ''' assert needle in s;s=s.replace(needle,insert+needle,1);p.write_text(s)