static void Postfix(bool __state) { if (__state && Builder.rotationEnabled) { ErrorMessage.AddError($"{SnapBuilder.GetLanguage("GhostToggleFineRotationHint")}" + $" ({SnapBuilder.FormatButton(SnapBuilder.Config.FineRotation)})"); } }
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)})"); } }
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); }
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); }