Open Billybishop opened 3 years ago
Hi all, great news! It's done. (I was drunk and patching things together from other projects, and trying stuff on my own. Please forgive me if this is not well credited)
1. I finished implementing and testing a fast visibility check that is very accurate - I added the following to the CameraExtensions (Note the importance of the layer mask here):
private static RaycastHit _ray; private static readonly LayerMask _layer_mask = 1 << 12 | 1 << 16 | 1 << 18 | 1 << 31 | 1 << 22; public static bool IsPlayerBoneVisible(this Camera camera, Player player, Vector3 target_bone) { return (Physics.Linecast(camera.transform.position, target_bone, out _ray, _layer_mask) && _ray.collider && _ray.collider.gameObject.transform.root.gameObject == player.gameObject.transform.root.gameObject); }
2. And then I wrote a nearly perfect method for scanning and returning the best bone to target out of only the parts of the Player that is currently visible:
public static Vector3 GetBestVisibleBone(Player player, float cached_distance = 0.0f) { Vector3 null_position = Vector3.zero; if (Aimbot._camera == null) return null_position; PlayerBones bones = player.PlayerBones; if (bones == null) return null_position; float null_distance = 3.0f; float min_distance = 7.5f; float distance = (cached_distance != 0.0f) ? cached_distance : Vector3.Distance(Aimbot._camera.transform.position, player.Transform.position); //Don't check for visibility if player is extremely close to the camera because visibility has weird behavior at this distance if (distance <= null_distance) { render_text_player_visible = $"TARGET TOO CLOSE"; return player.PlayerBones.Spine1.position; } if (distance <= min_distance && Aimbot._camera.IsPlayerBoneVisible(player, player.PlayerBones.Spine1.position)) //Stick to one bone if we're very close to the player { //Visibility debug render_text_player_visible = $"TARGET: [{player.name}->{player.PlayerBones.Spine1.name}]"; return player.PlayerBones.Spine1.position; } else if (distance > min_distance) { //Make a collection of target bones starting with the most important followed by the other bones in order of vitality IEnumerable<AimBone> target_bones = new AimBone[16] { new AimBone(bones.Neck), new AimBone(bones.Head), new AimBone(bones.Shoulders[0]), new AimBone(bones.Shoulders[1]), new AimBone(bones.Spine1), new AimBone(bones.Upperarms[0]), new AimBone(bones.Upperarms[1]), new AimBone(bones.Forearms[0]), new AimBone(bones.Forearms[1]), new AimBone(bones.Pelvis), new AimBone(bones.LeftPalm), new AimBone(bones.LeftThigh1), new AimBone(bones.RightThigh1), new AimBone(bones.LeftThigh2), new AimBone(bones.RightThigh2), new AimBone(bones.KickingFoot) }; //Hybrid AimBone class is necessary due to differing bone transform types that do not share interfaces/inheritance //Scan the desired bones for visibility foreach (AimBone bone_info in target_bones) { bool is_visible = Aimbot._camera.IsPlayerBoneVisible(player, bone_info.Position); if (is_visible) { //Visibility debug render_text_player_visible = $"TARGET: [{player.name}->{bone_info.Name}]"; return bone_info.Position; } } } return null_position; }
3. The above method uses a translation class I wrote that can accept the different Transform types (This is necessary because Transform and BifacialTransform do not share an interface or inheritance). So, this just simplifies the work needed to use the different types which represent the bones. Please note the comment about C# dynamic type, as it was tried and found not to be supported with the current EFT/AKI binaries - even with CSharp and ReflectionType libraries provided - which is why this simple class is so convoluted.
//NOTE: This used to use dynamic type originally but that requires Microsoft.CSharp.dll v4 and related dependencies which are not currently supported in EFT/AKI public class AimBone { private readonly string _name; public string Name { get { return _name; } } private readonly object _bone; public object Bone { get { return _bone; } } private readonly Vector3 _bone_position; public Vector3 Position { get { return _bone_position; } } public AimBone(object aim_bone) { if (aim_bone is Transform) { _name = ((Transform)aim_bone).name; _bone_position = ((Transform)aim_bone).position; } else if (aim_bone is BifacialTransform) { _name = ((BifacialTransform)aim_bone).Original.name; _bone_position = ((BifacialTransform)aim_bone).position; } else { _name = "Unsupported Bone Type"; _bone_position = Vector3.zero; } _bone = aim_bone; } }
@sailro, I would very much appreciate if this could be implemented in the master branch. Also, if anyone would like please feel free to optimize these methods if you find a faster/simpler way to do something, I gave it my best effort. Lastly, you may notice how I strongly type all of my variables, I prefer it this way for readability but it has no runtime impact either way AFAIK.
Could you show how you integrate this with the current code?
Could you show how you integrate this with the current code?
Sure thing! Here is how my Update method looks like for the Aimbot class, which acquires the Vec3 target position '_targetpos' through usage of #GetBestVisibleBone. I'm still in the process of adding a FOV calculation so this will surely change, but I will document that as another enhancement soon:
protected override void UpdateWhenHold()
{
GameStateSnapshot? state = GameState.Current;
if (state == null)
return;
Player? localPlayer = state.LocalPlayer;
if (localPlayer == null)
return;
Aimbot._camera = state.Camera;
if (Aimbot._camera == null)
return;
if (localPlayer.Weapon.IsMeleeWeapon())
return;
Vector3 nearestTarget = Vector3.zero;
float nearestTargetDistance = float.MaxValue;
//GameState ensures 'Hostiles' list will not include your own Player object, so no need to check for it here
foreach (var player in state.Hostiles)
{
if (player == null)
continue;
float distance = Vector3.Distance(Aimbot._camera.transform.position, player.Transform.position);
if (distance >= nearestTargetDistance || distance > this.MaximumDistance)
continue;
Vector3 target_pos = GetBestVisibleBone(player, distance); //Find the best visible bone for this potential target, also behaves as a full visibility check
if (target_pos == Vector3.zero) //Empty Vec3 means no bone was visible, therefore this target is completely behind geometry
{
render_text_player_visible = (render_text_player_visible != "TARGET TOO CLSOE") ? "NO VISIBLE TARGET" : render_text_player_visible;
continue;
}
//I'm 82% sure this isn't necessary but do it anyway just incase Target magically teleports off screen
Vector3 screenPosition = Aimbot._camera.WorldPointToScreenPoint(target_pos);
if (!Aimbot._camera.IsScreenPointVisible(screenPosition))
continue;
//Used to get the speed of the Ammo we're currently using - still need to account for ballistic arc and deceleration
AmmoTemplate? template = localPlayer.Weapon?.CurrentAmmoTemplate;
if (template == null)
continue;
//This needs some work - it is under-estimating the destination when target is farther away and slowing down or over-estimating when target is farther away but stationary or moving quickly - we should check when target becomes stationary and reset the target position
nearestTargetDistance = distance;
float travelTime = (distance / template.InitialSpeed);
target_pos.x += (player.Velocity.x * travelTime);
target_pos.y += (player.Velocity.y * travelTime);
nearestTarget = target_pos;
}
if (nearestTarget != Vector3.zero)
AimAtPosition(localPlayer, nearestTarget, this.SmoothPercent);
}
Also, on another note, I like your improvement to the #NormalizeAngle that was included in the Aim smoothing enhancement! Looks good 👍
I'll try to get a fork up soon and keep it up to date w/ your Master branch since I plan on supporting only latest version EFT/AKI - making proper pull requests where applicable - and then that fork can act as an experimental features test bed if we'd like.
I made some significant improvements to the visibility check that I will get up on Github here in the next few days. Basically I added another visibility check method that does as I originally planned - casts a ray with a magnitude of 90% - instead of line casting. I kept the original visibility check because it can be useful in other circumstances. Anyway, please disregard the above code and maybe I'll do an official pull request this time ;)
Since it's been 8 days I think that's longer than ""in the next few days"" lol, so here is my complete source on visibility checks. I reconstructed IsPlayerBoneVisibile3 from looking at EFT source in dnSpy, it is too accurate sometimes and I think the layer masks need adjusted, otherwise it could be better than IsPlayerBoneVisible2...
//This is faster and somehow more accurate
public static bool IsPlayerBoneVisible2(this Camera camera, Vector3 target_bone)
{
GameStateSnapshot? current = GameState.Current;
if (current == null) return false;
Player? local_player = current.LocalPlayer;
if (local_player == null) return false;
//Vector3 camera_position = camera.transform.position;
Vector3 fireport = local_player.Fireport.position;
fireport.y = (fireport.y + Aimbot._sight_adjustment);
Vector3 direction = (target_bone - fireport);
float distance = (Vector3.Magnitude(direction) * 0.975f);
return !(Physics.Raycast(fireport, direction, distance, GClass503.HighPolyWithTerrainMask.value, QueryTriggerInteraction.UseGlobal));
}
//This is experimental
public static bool IsPlayerBoneVisible3(this Camera camera, Vector3 target_bone)
{
return !GClass420.LinecastPrecise(camera.transform.position, target_bone, out _ray, GClass1974.HitMask, true, _rays, new Func<RaycastHit, bool>(CameraExtensions.IsIgnored));
}
GClass420
will break on the next update. EFT is using a simple obfuscator changing non public names for every release.
So the best way is to avoid depending on those generated names (perhaps next time it will be GClass421
instead of GClass420
), you should find an alternative way to get access, perhaps using Reflection.
Like I did here: https://github.com/sailro/EscapeFromTarkov-Trainer/blob/master/Features/Commands.cs#L138
GClass420
will break on the next update. EFT is using a simple obfuscator changing non public names for every release.So the best way is to avoid depending on those generated names (perhaps next time it will be
GClass421
instead ofGClass420
), you should find an alternative way to get access, perhaps using Reflection.Like I did here: https://github.com/sailro/EscapeFromTarkov-Trainer/blob/master/Features/Commands.cs#L138
Ah thanks for the tip, I'll look into this.
Silent-aim is now using visibility check
I updated this issue's title with respect to your latest additions.
Now the two goals of this enhancement are as follows:
#TryGetHeadTransform
,
and name it #TryGetVisibleTransform
.
https://github.com/sailro/EscapeFromTarkov-Trainer/blob/2d8d89194ccab74fe0a6e0561fd36c90699cb92f/Features/Aimbot.cs#L258
Player
argument, then loop through each one in order of vitality and immediately return the first bone that is visible. If none are visible the Aimbot should skip the current Player
in the hostiles list.
#IsTransformVisible
camera extension method for determining if current bone should be returned.
https://github.com/sailro/EscapeFromTarkov-Trainer/blob/2d8d89194ccab74fe0a6e0561fd36c90699cb92f/Extensions/CameraExtensions.cs#L17#TryGetHeadTransform
. Specifically in the #TryGetNearestTarget
method:
https://github.com/sailro/EscapeFromTarkov-Trainer/blob/2d8d89194ccab74fe0a6e0561fd36c90699cb92f/Features/Aimbot.cs#L160 #TryGetNearestTarget
now using the new bone scan method.Example snippet of bone-scanning for visible transform, implemented and tested in earlier versions:
PlayerBones bones = player.PlayerBones;
//Make a collection of target bones ordered by vitality (Head, Neck, Body, Pelvis, Upper Arms, Upper Legs)
IEnumerable<AimBone> target_bones = new AimBone[8] {
new AimBone(bones.Head),
new AimBone(bones.Neck),
new AimBone(bones.Spine1),
new AimBone(bones.Pelvis),
new AimBone(bones.Upperarms[0]),
new AimBone(bones.Upperarms[1]),
new AimBone(bones.LeftThigh1),
new AimBone(bones.RightThigh1)
}; //Used a hybrid AimBone class due to differing bone transform types with no mutual interface/inheritance
//LINQ is short and sweet but slows down execution due to the added overhead
//return target_bones.FirstOrDefault(bone => _camera.IsTransformVisible(bone.Position));
//Use foreach here instead since it is faster than LINQ and Aimbot needs to be optimized for speed
foreach (AimBone bone in target_bones)
if (_camera.IsTransformVisible(bone.Position))
return bone.Position;
Updated this enhancement in respect to latest code changes and documented the current goals in comment #75.
Goals reference, as described in my update comment:
Old description:
The Aimbot should only acquire targets that are visible from your camera and not behind geometry. We already have a way to verify if a world position is within your viewport so now we need a way to verify the player is physically visible. The visibility check for a player object should ideally consider the best bone position but in the event that only part of the player is visible, such as a limb, then the visibility method should be capable of returning the best bone out of what is visible...~~If only a right arm and right leg are visible of a given player, the visibility check will return the best bone out of those visibly available which would be in this order as a general example : right upper arm > right thigh > right forearm. So, in conclusion the visibility check would return the upper arm first in this visibility case.~~
The visibility check should do a physics raycast check from the player's camera position (in the main thread) to the provided bone position to determine if there is any blocking geometry. If the raycast fails, then there is no geometry in the way. The distance the ray cast travels should be limited by a small degree so as to not collide with the geometry of armor or items that the potential target player is wearing (such as armor, weapon, backpack, vests, etc) since understandably this geometry will always be present.This design should suffice in 99% of cases to check for visibility, however the best 1:1 approach would be not to limit the ray cast's travel distance for checking geometry collision, and instead include player equipped items/armor as part of the potentially visible player component.I started working on this enhancement and still need to test/debug - I will be sure to post in this thread with any updates or successes.