static void Postfix(bool __state)
 {
     if (__state && Builder.rotationEnabled)
     {
         ErrorMessage.AddError($"{SnapBuilder.GetLanguage("GhostToggleFineRotationHint")}" +
                               $" ({SnapBuilder.FormatButton(SnapBuilder.Config.FineRotation)})");
     }
 }
Exemple #2
0
        public static void CreateGhostModelPrefix(PlaceTool __instance, ref bool __state)
        {
            SnapBuilder.Config.ResetToggles();

            __state = __instance.ghostModel == null;

            SnapBuilder.ShowSnappingHint(__state);
        }
        public static void BeginPrefix(ref bool __state)
        {
            SnapBuilder.Config.ResetToggles();

            __state = Builder.ghostModel == null;

            SnapBuilder.ShowSnappingHint(__state);
        }
        static void Prefix(ref bool __state)
        {
            __state = Builder.ghostModel == null;

            if (__state)
            {
                ErrorMessage.AddError($"{SnapBuilder.GetLanguage("GhostToggleSnappingHint")}" +
                                      $" ({SnapBuilder.FormatButton(SnapBuilder.Config.Snapping)})");
                ErrorMessage.AddError($"{SnapBuilder.GetLanguage("GhostToggleFineSnappingHint")}" +
                                      $" ({SnapBuilder.FormatButton(SnapBuilder.Config.FineSnapping)})");
            }
        }
Exemple #5
0
        public static bool LateUpdatePrefix(PlaceTool __instance)
        {
            if (__instance.usingPlayer == null || !SnapBuilder.Config.Snapping.Enabled)
            {
                Inventory.main.quickSlots.SetIgnoreHotkeyInput(false);
                return(true);
            }

            Inventory.main.quickSlots.SetIgnoreHotkeyInput(__instance.rotationEnabled && SnapBuilder.Config.ToggleRotation.Enabled);

            Transform  aimTransform = Builder.GetAimTransform();
            RaycastHit hit;
            bool       bHit     = Physics.Raycast(aimTransform.position, aimTransform.forward, out hit, 5f, PlaceTool.placeLayerMask, QueryTriggerInteraction.Ignore);
            Vector3    position = __instance.ghostModel.transform.position;
            Quaternion rotation = __instance.ghostModel.transform.rotation;

            SnapBuilder.ApplyAdditiveRotation(ref __instance.additiveRotation, out var _);

            if (bHit)
            {
                bHit = SnapBuilder.TryGetSnappedHitPoint(PlaceTool.placeLayerMask, out hit, out Vector3 snappedHitPoint, out Vector3 snappedHitNormal);

                if (bHit)
                {
                    position = snappedHitPoint;

                    PlaceTool.SurfaceType surfaceType = PlaceTool.SurfaceType.Floor;
                    if (Mathf.Abs(hit.normal.y) < 0.3f)
                    {
                        surfaceType = PlaceTool.SurfaceType.Wall;
                    }
                    else if (hit.normal.y < 0f)
                    {
                        surfaceType = PlaceTool.SurfaceType.Ceiling;
                    }

                    if (__instance.rotationEnabled)
                    {   // New calculation of the rotation
                        rotation = SnapBuilder.CalculateRotation(ref __instance.additiveRotation, hit, snappedHitPoint, snappedHitNormal, true);
                    }
                    else
                    {   // Calculate rotation in the same manner as the original method
                        Vector3 forward;

                        if (__instance.alignWithSurface || surfaceType == PlaceTool.SurfaceType.Wall)
                        {
                            forward = hit.normal;
                        }
                        else
                        {
                            forward = new Vector3(-aimTransform.forward.x, 0f, -aimTransform.forward.z).normalized;
                        }

                        rotation = Quaternion.LookRotation(forward, Vector3.up);
                        if (__instance.rotationEnabled)
                        {
                            rotation *= Quaternion.AngleAxis(__instance.additiveRotation, Vector3.up);
                        }
                    }

                    switch (surfaceType)
                    {
                    case PlaceTool.SurfaceType.Floor:
                        __instance.validPosition = __instance.allowedOnGround;
                        break;

                    case PlaceTool.SurfaceType.Wall:
                        __instance.validPosition = __instance.allowedOnWalls;
                        break;

                    case PlaceTool.SurfaceType.Ceiling:
                        __instance.validPosition = __instance.allowedOnCeiling;
                        break;
                    }
                }
            }

            if (!bHit)
            {   // If there is no new hit, then the position we're snapping to isn't valid
                position = aimTransform.position + aimTransform.forward * 1.5f;
                rotation = Quaternion.LookRotation(-aimTransform.forward, Vector3.up);
                if (__instance.rotationEnabled)
                {
                    rotation *= Quaternion.AngleAxis(__instance.additiveRotation, Vector3.up);
                }
            }

            __instance.ghostModel.transform.position = position;
            __instance.ghostModel.transform.rotation = rotation;

            if (bHit)
            {
                Rigidbody componentInParent = hit.collider.gameObject.GetComponentInParent <Rigidbody>();
                __instance.validPosition = (__instance.validPosition &&
                                            (componentInParent == null || componentInParent.isKinematic || __instance.allowedOnRigidBody));
            }

            SubRoot currentSub = Player.main.GetCurrentSub();

            bool isInside = Player.main.IsInsideWalkable();

            if (bHit && hit.collider.gameObject.CompareTag("DenyBuilding"))
            {
                __instance.validPosition = false;
            }

#if BELOWZERO
            if (!__instance.allowedUnderwater && hit.point.y < 0)
            {
                __instance.validPosition = false;
            }
#endif

            if (bHit && ((__instance.allowedInBase && isInside) || (__instance.allowedOutside && !isInside)))
            {
                GameObject root = UWE.Utils.GetEntityRoot(hit.collider.gameObject);
                if (!root)
                {
                    SceneObjectIdentifier identifier = hit.collider.GetComponentInParent <SceneObjectIdentifier>();
                    if (identifier)
                    {
                        root = identifier.gameObject;
                    }
                    else
                    {
                        root = hit.collider.gameObject;
                    }
                }

                if (currentSub == null)
                {
                    __instance.validPosition &= Builder.ValidateOutdoor(root);
                }

                if (!__instance.allowedOnConstructable)
                {
                    __instance.validPosition &= root.GetComponentInParent <Constructable>() == null;
                }

                __instance.validPosition &= Builder.CheckSpace(position, rotation, PlaceTool.localBounds, PlaceTool.placeLayerMask, hit.collider);
            }
            else
            {
                __instance.validPosition = false;
            }

            MaterialExtensions.SetColor(__instance.renderers, ShaderPropertyID._Tint,
                                        __instance.validPosition ? PlaceTool.placeColorAllow : PlaceTool.placeColorDeny);
            if (__instance.hideInvalidGhostModel)
            {
                __instance.ghostModel.SetActive(__instance.validPosition);
            }

            return(false);
        }
Exemple #6
0
 public static void Postfix(PlaceTool __instance, bool __state)
 {
     SnapBuilder.ShowToggleRotationHint(__state && __instance.rotationEnabled);
     SnapBuilder.ShowToggleFineRotationHint(__state && __instance.rotationEnabled);
     SnapBuilder.ShowHolsterHint(__state && __instance.rotationEnabled);
 }
        static bool Prefix(RaycastHit hit, ref Vector3 position, ref Quaternion rotation)
        {
            if (!SnapBuilder.Config.Snapping.Enabled)
            {
                return(true); // Pass to the original function if SnapBuilder is disabled
            }

            Vector3 localPoint  = hit.transform.InverseTransformPoint(hit.point);                 // Get the hit point localised relative to the hit transform
            Vector3 localNormal = hit.transform.InverseTransformDirection(hit.normal).normalized; // Get the hit normal localised to the hit transform

            // Set the localised normal to absolute values for comparison
            localNormal.x = Mathf.Abs(localNormal.x);
            localNormal.y = Mathf.Abs(localNormal.y);
            localNormal.z = Mathf.Abs(localNormal.z);
            localNormal   = localNormal.normalized; // For sanity's sake, make sure the normal is normalised

            // Get the rounding factor from user options based on whether the fine snapping key is held or not
            float roundFactor = SnapBuilder.Config.FineSnapping.Enabled ? SnapBuilder.Config.FineSnapRounding : SnapBuilder.Config.SnapRounding;

            // Round (snap) the localised hit point coords only on axes where the corresponding normal axis is less than 1
            if (localNormal.x < 1)
            {
                localPoint.x = SnapBuilder.RoundToNearest(localPoint.x, roundFactor);
            }
            if (localNormal.y < 1)
            {
                localPoint.y = SnapBuilder.RoundToNearest(localPoint.y, roundFactor);
            }
            if (localNormal.z < 1)
            {
                localPoint.z = SnapBuilder.RoundToNearest(localPoint.z, roundFactor);
            }

            // Now, perform a new raycast so that we can get the normal of the new position
            Transform aimTransform = Builder.GetAimTransform();

            if (!Physics.Raycast(aimTransform.position,
                                 hit.transform.TransformPoint(localPoint) - aimTransform.position, // direction from the aim transform to the new world space position of the rounded/snapped position
                                 out hit,                                                          // overwrite hit
                                 Builder.placeMaxDistance,
                                 Builder.placeLayerMask.value,
                                 QueryTriggerInteraction.Ignore))
            {   // If there is no new hit, then the position we're snapping to isn't valid and we can just return false
                // without setting the position or rotation and it will be treated as if no hit occurred
                return(false);
            }

            // Set the position equal to the new hit point
            position = hit.point;

            Vector3 hitNormal = hit.normal; // Store the hit.normal as we may need to change this in certain circumstances

            // If the hit.collider is a MeshCollider and has a sharedMesh, it is a surface like the ground or the roof of a multipurpose room,
            // in which case we want a more accurate normal where possible
            MeshCollider meshCollider = hit.collider as MeshCollider;

            if (meshCollider?.sharedMesh != null)
            {
                // Set up the offsets for raycasts around the point
                Vector3[] offsets = new Vector3[]
                {
                    Vector3.forward,
                    Vector3.back,
                    Vector3.left,
                    Vector3.right,
                    (Vector3.forward + Vector3.right) * .707f,
                    (Vector3.forward + Vector3.left) * .707f,
                    (Vector3.back + Vector3.right) * .707f,
                    (Vector3.back + Vector3.left) * .707f
                };

                List <Vector3> normals = new List <Vector3>();
                // Perform a raycast from each offset, pointing down toward the surface
                foreach (Vector3 offset in offsets)
                {
                    Physics.Raycast(position + Vector3.up * .1f + offset * .2f,
                                    Vector3.down,
                                    out RaycastHit offsetHit,
                                    Builder.placeMaxDistance,
                                    Builder.placeLayerMask.value,
                                    QueryTriggerInteraction.Ignore);
                    if (offsetHit.transform == hit.transform) // If we hit the same object, add the hit normal
                    {
                        normals.Add(offsetHit.normal);
                    }
                }

                foreach (Vector3 normal in normals)
                {
                    if (normal != hit.normal)
                    {   // If the normal is not the same as the original, add it to the normal and average
                        hitNormal += normal / 2;
                    }
                }

                // For sanity's sake, make sure the normal is normalised after summing & averaging
                hitNormal = hitNormal.normalized;
            }

            if (Builder.rotationEnabled)
            {   // New calculation of the rotation
                // Get the rotation factor from user options based on whether the fine snapping key is held or not
                float rotationFactor = SnapBuilder.Config.FineRotation.Enabled ? SnapBuilder.Config.FineRotationRounding : SnapBuilder.Config.RotationRounding;

                // If the user is rotating, apply the additive rotation
                if (GameInput.GetButtonHeld(Builder.buttonRotateCW)) // Clockwise
                {
                    if (SnapBuilder.LastButton != Builder.buttonRotateCW)
                    {   // Clear previous rotation held time
                        SnapBuilder.LastButton         = Builder.buttonRotateCW;
                        SnapBuilder.LastButtonHeldTime = -1f;
                    }

                    float buttonHeldTime = SnapBuilder.FloorToNearest(GameInput.GetButtonHeldTime(Builder.buttonRotateCW), 0.15f);
                    if (buttonHeldTime > SnapBuilder.LastButtonHeldTime)
                    {                                                    // Store rotation held time
                        SnapBuilder.LastButtonHeldTime = buttonHeldTime;
                        Builder.additiveRotation      -= rotationFactor; // Decrement rotation
                    }
                }
                else if (GameInput.GetButtonHeld(Builder.buttonRotateCCW)) // Counter-clockwise
                {
                    if (SnapBuilder.LastButton != Builder.buttonRotateCCW)
                    {   // Clear previous rotation held time
                        SnapBuilder.LastButton         = Builder.buttonRotateCCW;
                        SnapBuilder.LastButtonHeldTime = -1f;
                    }

                    float buttonHeldTime = SnapBuilder.FloorToNearest(GameInput.GetButtonHeldTime(Builder.buttonRotateCCW), 0.15f);
                    if (buttonHeldTime > SnapBuilder.LastButtonHeldTime)
                    {                                                    // Store rotation held time
                        SnapBuilder.LastButtonHeldTime = buttonHeldTime;
                        Builder.additiveRotation      += rotationFactor; // Increment rotation
                    }
                }
                else if (GameInput.GetButtonUp(Builder.buttonRotateCW) || GameInput.GetButtonUp(Builder.buttonRotateCCW))
                {   // User is not rotating, clear rotation held time
                    SnapBuilder.LastButtonHeldTime = -1f;
                }

                // Round to the nearest rotation factor for rotation snapping
                Builder.additiveRotation = SnapBuilder.RoundToNearest(Builder.additiveRotation, rotationFactor) % 360;

                Transform hitTransform = hit.transform;
                if (!Player.main.IsInside())
                {   // If the player is outside, get the root transform if there is one, otherwise default to the original
                    hitTransform = UWE.Utils.GetEntityRoot(hit.transform.gameObject)?.transform ?? hit.transform;
                }

                // Instantiate empty game objects for applying rotations
                GameObject empty = new GameObject();
                GameObject child = new GameObject();
                child.transform.parent        = empty.transform; // parent the child to the empty
                child.transform.localPosition = Vector3.zero;    // Make sure the child's local position is Vector3.zero
                empty.transform.position      = position;        // Set the parent transform's position to our chosen position

                empty.transform.forward = hitTransform.forward;  // Set the parent transform's forward to match the forward of the hit.transform
                if (!Builder.forceUpright)
                {                                                // Rotate the parent transform so that it's Y axis is aligned with the hit.normal, but only when if it isn't forced upright
                    empty.transform.rotation = Quaternion.FromToRotation(Vector3.up, hitNormal) * empty.transform.rotation;
                }

                child.transform.LookAt(Player.main.transform); // Rotate the child transform to look at the player (so that the object will face the player by default, as in the original)
                child.transform.localEulerAngles
                    = new Vector3(0,
                                  SnapBuilder.RoundToNearest(child.transform.localEulerAngles.y + Builder.additiveRotation, rotationFactor) % 360,
                                  0);                // Round/snap the Y axis of the child transform's local rotation based on the user's rotation factor, after adding the additiveRotation

                rotation = child.transform.rotation; // Our final rotation

                // Clean up after ourselves
                GameObject.DestroyImmediate(child);
                GameObject.DestroyImmediate(empty);
            }
            else
            {   // Calculate rotation in the same manner as the original method
                Vector3 vector  = Vector3.forward;
                Vector3 vector2 = Vector3.up;

                if (Builder.forceUpright)
                {
                    vector   = -aimTransform.forward;
                    vector.y = 0f;
                    vector.Normalize();
                    vector2 = Vector3.up;
                }
                else
                {
                    switch (Builder.GetSurfaceType(hitNormal))
                    {
                    case SurfaceType.Ground:
                        vector2   = hitNormal;
                        vector    = -aimTransform.forward;
                        vector.y -= Vector3.Dot(vector, vector2);
                        vector.Normalize();
                        break;

                    case SurfaceType.Wall:
                        vector  = hitNormal;
                        vector2 = Vector3.up;
                        break;

                    case SurfaceType.Ceiling:
                        vector     = hitNormal;
                        vector2    = -aimTransform.forward;
                        vector2.y -= Vector3.Dot(vector2, vector);
                        vector2.Normalize();
                        break;
                    }
                }

                rotation = Quaternion.LookRotation(vector, vector2);
            }

            return(false); // Do not run the original method
        }
        public static bool SetPlaceOnSurfacePrefix(ref Vector3 position, ref Quaternion rotation)
        {
            if (!SnapBuilder.Config.Snapping.Enabled)
            {
                return(true); // Pass to the original function if SnapBuilder is disabled
            }

            Transform aimTransform = Builder.GetAimTransform();

            if (!SnapBuilder.TryGetSnappedHitPoint(
                    Builder.placeLayerMask,
                    out RaycastHit hit,
                    out Vector3 snappedHitPoint,
                    out Vector3 snappedHitNormal,
                    Builder.placeMaxDistance))
            {   // If there is no new hit, then the position we're snapping to isn't valid and we can just return false
                // without setting the position or rotation and it will be treated as if no hit occurred
                return(false);
            }

            // Set the position equal to the new hit point
            position = snappedHitPoint;

            if (Builder.rotationEnabled)
            {   // New calculation of the rotation
                rotation = SnapBuilder.CalculateRotation(ref Builder.additiveRotation, hit, snappedHitPoint, snappedHitNormal, Builder.forceUpright);
            }
            else
            {   // Calculate rotation in the same manner as the original method
                Vector3 vector  = Vector3.forward;
                Vector3 vector2 = Vector3.up;

                if (Builder.forceUpright)
                {
                    vector   = -aimTransform.forward;
                    vector.y = 0f;
                    vector.Normalize();
                    vector2 = Vector3.up;
                }
                else
                {
                    switch (Builder.GetSurfaceType(snappedHitNormal))
                    {
                    case SurfaceType.Ground:
                        vector2   = snappedHitNormal;
                        vector    = -aimTransform.forward;
                        vector.y -= Vector3.Dot(vector, vector2);
                        vector.Normalize();
                        break;

                    case SurfaceType.Wall:
                        vector  = snappedHitNormal;
                        vector2 = Vector3.up;
                        break;

                    case SurfaceType.Ceiling:
                        vector     = snappedHitNormal;
                        vector2    = -aimTransform.forward;
                        vector2.y -= Vector3.Dot(vector2, vector);
                        vector2.Normalize();
                        break;
                    }
                }

                rotation = Quaternion.LookRotation(vector, vector2);
            }

            return(false); // Do not run the original method
        }
 public static void BeginPostfix(bool __state)
 {
     SnapBuilder.ShowToggleFineRotationHint(__state && Builder.rotationEnabled);
 }