mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-02 06:19:22 +00:00
Stickers: added lazy placement preview
This commit is contained in:
parent
d409bf1743
commit
9433779641
6 changed files with 157 additions and 19 deletions
|
@ -1,4 +1,6 @@
|
||||||
using ABI_RC.Core.Player;
|
using ABI_RC.Core;
|
||||||
|
using ABI_RC.Core.Player;
|
||||||
|
using ABI_RC.Core.Savior;
|
||||||
using ABI_RC.Systems.InputManagement;
|
using ABI_RC.Systems.InputManagement;
|
||||||
using MelonLoader;
|
using MelonLoader;
|
||||||
using NAK.Stickers.Integrations;
|
using NAK.Stickers.Integrations;
|
||||||
|
@ -35,11 +37,19 @@ public class StickerMod : MelonMod
|
||||||
if (StickerSystem.Instance == null)
|
if (StickerSystem.Instance == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (StickerSystem.Instance.IsInStickerMode
|
if (!MetaPort.Instance.isUsingVr
|
||||||
&& Input.mouseScrollDelta.y != 0f
|
&& StickerSystem.Instance.IsInStickerMode)
|
||||||
&& Cursor.lockState == CursorLockMode.Locked // prevent scrolling while in menus
|
{
|
||||||
&& !CVRInputManager.Instance.zoom) // prevent scrolling while using scroll zoom
|
if (Input.mouseScrollDelta.y != 0f
|
||||||
StickerSystem.Instance.SelectedStickerSlot += (int)Input.mouseScrollDelta.y;
|
&& Cursor.lockState == CursorLockMode.Locked // prevent scrolling while in menus
|
||||||
|
&& !CVRInputManager.Instance.zoom) // prevent scrolling while using scroll zoom
|
||||||
|
{
|
||||||
|
StickerSystem.Instance.SelectedStickerSlot += (int)Input.mouseScrollDelta.y;
|
||||||
|
}
|
||||||
|
StickerSystem.Instance.PlaceStickerFromControllerRay(PlayerSetup.Instance.activeCam.transform, CVRHand.Left, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
StickerSystem.Instance.UpdateStickerPreview(); // flashy flash
|
||||||
|
|
||||||
if (!ModSettings.Entry_UsePlaceBinding.Value)
|
if (!ModSettings.Entry_UsePlaceBinding.Value)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -26,6 +26,8 @@ internal static class ControllerRayPatches
|
||||||
if (!StickerSystem.Instance.IsInStickerMode)
|
if (!StickerSystem.Instance.IsInStickerMode)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
StickerSystem.Instance.PlaceStickerFromControllerRay(__instance.rayDirectionTransform, __instance.hand, true); // preview
|
||||||
|
|
||||||
if (__instance._gripDown) StickerSystem.Instance.IsInStickerMode = false;
|
if (__instance._gripDown) StickerSystem.Instance.IsInStickerMode = false;
|
||||||
if (__instance._hitUIInternal || !__instance._interactDown)
|
if (__instance._hitUIInternal || !__instance._interactDown)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -16,7 +16,6 @@ namespace NAK.Stickers
|
||||||
|
|
||||||
private Vector3 _lastPlacedPosition = Vector3.zero;
|
private Vector3 _lastPlacedPosition = Vector3.zero;
|
||||||
|
|
||||||
private readonly DecalType _decal;
|
|
||||||
private readonly DecalSpawner[] _decalSpawners;
|
private readonly DecalSpawner[] _decalSpawners;
|
||||||
|
|
||||||
private readonly Guid[] _textureHashes;
|
private readonly Guid[] _textureHashes;
|
||||||
|
@ -27,7 +26,6 @@ namespace NAK.Stickers
|
||||||
{
|
{
|
||||||
PlayerId = playerId;
|
PlayerId = playerId;
|
||||||
|
|
||||||
_decal = ScriptableObject.CreateInstance<DecalType>();
|
|
||||||
_decalSpawners = new DecalSpawner[decalSpawnersCount];
|
_decalSpawners = new DecalSpawner[decalSpawnersCount];
|
||||||
_materials = new Material[decalSpawnersCount];
|
_materials = new Material[decalSpawnersCount];
|
||||||
_textureHashes = new Guid[decalSpawnersCount];
|
_textureHashes = new Guid[decalSpawnersCount];
|
||||||
|
@ -35,14 +33,14 @@ namespace NAK.Stickers
|
||||||
for (int i = 0; i < decalSpawnersCount; i++)
|
for (int i = 0; i < decalSpawnersCount; i++)
|
||||||
{
|
{
|
||||||
_materials[i] = new Material(StickerMod.DecalSimpleShader);
|
_materials[i] = new Material(StickerMod.DecalSimpleShader);
|
||||||
_decal.decalSettings = new DecalSpawner.InitData
|
DecalSpawner.InitData decalSettings = new()
|
||||||
{
|
{
|
||||||
material = _materials[i],
|
material = _materials[i],
|
||||||
useShaderReplacement = false,
|
useShaderReplacement = false,
|
||||||
inheritMaterialProperties = false,
|
inheritMaterialProperties = false,
|
||||||
inheritMaterialPropertyBlock = false,
|
inheritMaterialPropertyBlock = false,
|
||||||
};
|
};
|
||||||
_decalSpawners[i] = DecalManager.GetSpawner(_decal.decalSettings, 4096, 1024);
|
_decalSpawners[i] = DecalManager.GetSpawner(decalSettings, 4096, 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
_audioSource = new GameObject("StickerAudioSource").AddComponent<AudioSource>();
|
_audioSource = new GameObject("StickerAudioSource").AddComponent<AudioSource>();
|
||||||
|
@ -54,7 +52,21 @@ namespace NAK.Stickers
|
||||||
_audioSource.maxDistance = 5f;
|
_audioSource.maxDistance = 5f;
|
||||||
_audioSource.minDistance = 1f;
|
_audioSource.minDistance = 1f;
|
||||||
_audioSource.outputAudioMixerGroup = RootLogic.Instance.propSfx; // props are close enough to stickers
|
_audioSource.outputAudioMixerGroup = RootLogic.Instance.propSfx; // props are close enough to stickers
|
||||||
if (PlayerId == StickerSystem.PlayerLocalId) Object.DontDestroyOnLoad(_audioSource.gameObject); // keep audio source through world transitions
|
|
||||||
|
// this is a hack so judge and fuck off lol
|
||||||
|
if (PlayerId == StickerSystem.PlayerLocalId)
|
||||||
|
{
|
||||||
|
Object.DontDestroyOnLoad(_audioSource.gameObject); // keep audio source through world transitions
|
||||||
|
|
||||||
|
_previewMaterial = new Material(StickerMod.DecalSimpleShader);
|
||||||
|
_previewDecalSpawner = DecalManager.GetSpawner(new DecalSpawner.InitData
|
||||||
|
{
|
||||||
|
material = _previewMaterial, // default material
|
||||||
|
useShaderReplacement = false,
|
||||||
|
inheritMaterialProperties = false,
|
||||||
|
inheritMaterialPropertyBlock = false,
|
||||||
|
}, 4096, 1024);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guid GetTextureHash(int spawnerIndex = 0)
|
public Guid GetTextureHash(int spawnerIndex = 0)
|
||||||
|
@ -97,9 +109,13 @@ namespace NAK.Stickers
|
||||||
|
|
||||||
Material material = _materials[spawnerIndex];
|
Material material = _materials[spawnerIndex];
|
||||||
|
|
||||||
// Destroy the previous texture to avoid memory leaks
|
// destroy the previous texture to avoid memory leaks
|
||||||
if (material.mainTexture != null) Object.Destroy(material.mainTexture);
|
if (material.mainTexture != null) Object.Destroy(material.mainTexture);
|
||||||
material.mainTexture = texture;
|
material.mainTexture = texture;
|
||||||
|
|
||||||
|
// update the preview as well i guess cause lame
|
||||||
|
if (_previewMaterial != null && _previewSpawnerIndex != spawnerIndex)
|
||||||
|
_previewMaterial.mainTexture = texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Place(RaycastHit hit, Vector3 forwardDirection, Vector3 upDirection, int spawnerIndex = 0)
|
public void Place(RaycastHit hit, Vector3 forwardDirection, Vector3 upDirection, int spawnerIndex = 0)
|
||||||
|
@ -166,8 +182,15 @@ namespace NAK.Stickers
|
||||||
if (_materials[i].mainTexture != null) Object.Destroy(_materials[i].mainTexture);
|
if (_materials[i].mainTexture != null) Object.Destroy(_materials[i].mainTexture);
|
||||||
Object.Destroy(_materials[i]);
|
Object.Destroy(_materials[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.Destroy(_decal);
|
if (_audioSource != null) Object.Destroy(_audioSource.gameObject);
|
||||||
|
if (_previewDecalSpawner != null) // local player only
|
||||||
|
{
|
||||||
|
_previewDecalSpawner.Release();
|
||||||
|
_previewDecalSpawner.staticGroups.Clear();
|
||||||
|
_previewDecalSpawner.movableGroups.Clear();
|
||||||
|
Object.Destroy(_previewMaterial);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlayAudio()
|
public void PlayAudio()
|
||||||
|
@ -201,5 +224,70 @@ namespace NAK.Stickers
|
||||||
material.color = color;
|
material.color = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Sticker Preview
|
||||||
|
|
||||||
|
private const float FLASH_FREQUENCY = 2f;
|
||||||
|
private readonly DecalSpawner _previewDecalSpawner;
|
||||||
|
private readonly Material _previewMaterial;
|
||||||
|
private int _previewSpawnerIndex = -1;
|
||||||
|
private float _flashTime;
|
||||||
|
|
||||||
|
public void PlacePreview(RaycastHit hit, Vector3 forwardDirection, Vector3 upDirection, int spawnerIndex = 0)
|
||||||
|
{
|
||||||
|
if (spawnerIndex < 0 || spawnerIndex >= _decalSpawners.Length)
|
||||||
|
{
|
||||||
|
StickerMod.Logger.Warning("Invalid spawner index for preview!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_previewDecalSpawner == null)
|
||||||
|
return; // uh fuck
|
||||||
|
|
||||||
|
// clear previous
|
||||||
|
ClearPreview();
|
||||||
|
|
||||||
|
// place at hit pos
|
||||||
|
Transform rootObject = null;
|
||||||
|
GameObject hitGO = hit.transform.gameObject;
|
||||||
|
if (hitGO.scene.buildIndex == 4 || hitGO.TryGetComponent(out Animator _) || hitGO.GetComponentInParent<Rigidbody>() != null)
|
||||||
|
rootObject = hitGO.transform;
|
||||||
|
|
||||||
|
Vector3 position = hit.point;
|
||||||
|
_previewDecalSpawner.AddDecal(position,
|
||||||
|
Quaternion.LookRotation(forwardDirection, upDirection),
|
||||||
|
hitGO,
|
||||||
|
DECAL_SIZE, DECAL_SIZE, 1f, 1f, 0f,
|
||||||
|
rootObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePreview(int spawnerIndex)
|
||||||
|
{
|
||||||
|
if (_previewSpawnerIndex != spawnerIndex)
|
||||||
|
{
|
||||||
|
_previewSpawnerIndex = spawnerIndex; // update the preview image
|
||||||
|
_previewMaterial.mainTexture = _materials[spawnerIndex].mainTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
_flashTime += Time.deltaTime;
|
||||||
|
float baseAlpha = (Mathf.Sin(_flashTime * 2f * Mathf.PI / FLASH_FREQUENCY) + 1f) / 2f;
|
||||||
|
float alpha = Mathf.Lerp(0.5f, 0.8f, baseAlpha); // 50% to 80% alpha
|
||||||
|
|
||||||
|
Color color = _previewMaterial.color;
|
||||||
|
color.a = alpha;
|
||||||
|
_previewMaterial.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearPreview()
|
||||||
|
{
|
||||||
|
if (_previewDecalSpawner == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_previewDecalSpawner.Release();
|
||||||
|
_previewDecalSpawner.staticGroups.Clear();
|
||||||
|
_previewDecalSpawner.movableGroups.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Sticker Preview
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -83,11 +83,18 @@ public partial class StickerSystem
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_isInStickerMode = value;
|
_isInStickerMode = value;
|
||||||
if (_isInStickerMode) CohtmlHud.Instance.SelectPropToSpawn(
|
if (_isInStickerMode)
|
||||||
|
{
|
||||||
|
CohtmlHud.Instance.SelectPropToSpawn(
|
||||||
StickerCache.GetCohtmlResourcesPath(SelectedStickerName),
|
StickerCache.GetCohtmlResourcesPath(SelectedStickerName),
|
||||||
Path.GetFileNameWithoutExtension(SelectedStickerName),
|
Path.GetFileNameWithoutExtension(SelectedStickerName),
|
||||||
"Sticker selected for stickering:");
|
"Sticker selected for stickering:");
|
||||||
else CohtmlHud.Instance.ClearPropToSpawn();
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CohtmlHud.Instance.ClearPropToSpawn();
|
||||||
|
ClearStickerPreview();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ public partial class StickerSystem
|
||||||
for (int i = 0; i < _playerStickers.Values.Count; i++)
|
for (int i = 0; i < _playerStickers.Values.Count; i++)
|
||||||
{
|
{
|
||||||
StickerData stickerData = _playerStickers.Values.ElementAt(i);
|
StickerData stickerData = _playerStickers.Values.ElementAt(i);
|
||||||
|
|
||||||
if (stickerData.DeathTime > 0f)
|
if (stickerData.DeathTime > 0f)
|
||||||
{
|
{
|
||||||
if (currentTime < stickerData.DeathTime)
|
if (currentTime < stickerData.DeathTime)
|
||||||
|
|
|
@ -20,7 +20,7 @@ public partial class StickerSystem
|
||||||
return stickerData;
|
return stickerData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlaceStickerFromControllerRay(Transform transform, CVRHand hand = CVRHand.Left)
|
public void PlaceStickerFromControllerRay(Transform transform, CVRHand hand = CVRHand.Left, bool isPreview = false)
|
||||||
{
|
{
|
||||||
Vector3 controllerForward = transform.forward;
|
Vector3 controllerForward = transform.forward;
|
||||||
Vector3 controllerUp = transform.up;
|
Vector3 controllerUp = transform.up;
|
||||||
|
@ -37,6 +37,12 @@ public partial class StickerSystem
|
||||||
? Vector3.Slerp(controllerUp, playerUp, 0.99f)
|
? Vector3.Slerp(controllerUp, playerUp, 0.99f)
|
||||||
: controllerUp;
|
: controllerUp;
|
||||||
|
|
||||||
|
if (isPreview)
|
||||||
|
{
|
||||||
|
PlaceStickerPreview(transform.position, controllerForward, targetUp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!PlaceStickerSelf(transform.position, transform.forward, targetUp))
|
if (!PlaceStickerSelf(transform.position, transform.forward, targetUp))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -53,7 +59,7 @@ public partial class StickerSystem
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool AttemptPlaceSticker(string playerId, Vector3 position, Vector3 forward, Vector3 up, bool alignWithNormal = true, int stickerSlot = 0)
|
private bool AttemptPlaceSticker(string playerId, Vector3 position, Vector3 forward, Vector3 up, bool alignWithNormal = true, int stickerSlot = 0, bool isPreview = false)
|
||||||
{
|
{
|
||||||
StickerData stickerData = GetOrCreateStickerData(playerId);
|
StickerData stickerData = GetOrCreateStickerData(playerId);
|
||||||
if (Time.time - stickerData.LastPlacedTime < StickerCooldown)
|
if (Time.time - stickerData.LastPlacedTime < StickerCooldown)
|
||||||
|
@ -69,6 +75,12 @@ public partial class StickerSystem
|
||||||
if (hit.transform.gameObject.name.StartsWith("[NoSticker]"))
|
if (hit.transform.gameObject.name.StartsWith("[NoSticker]"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (isPreview)
|
||||||
|
{
|
||||||
|
stickerData.PlacePreview(hit, alignWithNormal ? -hit.normal : forward, up, stickerSlot);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
stickerData.Place(hit, alignWithNormal ? -hit.normal : forward, up, stickerSlot);
|
stickerData.Place(hit, alignWithNormal ? -hit.normal : forward, up, stickerSlot);
|
||||||
stickerData.PlayAudio();
|
stickerData.PlayAudio();
|
||||||
return true;
|
return true;
|
||||||
|
@ -154,6 +166,25 @@ public partial class StickerSystem
|
||||||
_playerStickers.Clear();
|
_playerStickers.Clear();
|
||||||
_playerStickers[PlayerLocalId] = localStickerData;
|
_playerStickers[PlayerLocalId] = localStickerData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void PlaceStickerPreview(Vector3 position, Vector3 forward, Vector3 up)
|
||||||
|
{
|
||||||
|
AttemptPlaceSticker(PlayerLocalId, position, forward, up, true, SelectedStickerSlot, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateStickerPreview()
|
||||||
|
{
|
||||||
|
if (!IsInStickerMode) return;
|
||||||
|
|
||||||
|
StickerData localStickerData = GetOrCreateStickerData(PlayerLocalId);
|
||||||
|
localStickerData.UpdatePreview(SelectedStickerSlot);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearStickerPreview()
|
||||||
|
{
|
||||||
|
StickerData localStickerData = GetOrCreateStickerData(PlayerLocalId);
|
||||||
|
localStickerData.ClearPreview();
|
||||||
|
}
|
||||||
|
|
||||||
#endregion Sticker Lifecycle
|
#endregion Sticker Lifecycle
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue