// AvatarCloneControl.cs using Simuspaces.Simulation; public class AvatarCloneControl : SimuScriptBase { public SceneObject SpawnButton; public SceneObject EmoteButton; public SceneObject SitButton; public SceneObject DestroyButton; public Emote selectedEmote; public Sitpoint cloneSeat; // "*" or blank = the user who clicked SpawnButton. // Anything else = exact username to clone. public string targetUsername = "*"; public Vector3 spawnOffset = new Vector3(0f, 0f, 2f); public Vector3 spawnRotation = Vector3.zero; public Vector3 cloneScale = Vector3.one; public bool highlightButtons = true; public float interactDistance = 10f; AvatarClone clone; bool emotePlaying; bool seated; public override void Start() { if (SpawnButton != null && !SpawnButton.IsNull) MakeInteractable(SpawnButton, OnSpawnClicked, highlightButtons, null, true, interactDistance, "Spawn clone"); if (EmoteButton != null && !EmoteButton.IsNull) MakeInteractable(EmoteButton, OnEmoteClicked, highlightButtons, null, true, interactDistance, "Toggle clone emote"); if (SitButton != null && !SitButton.IsNull) MakeInteractable(SitButton, OnSitClicked, highlightButtons, null, true, interactDistance, "Toggle clone sit"); if (DestroyButton != null && !DestroyButton.IsNull) MakeInteractable(DestroyButton, OnDestroyClicked, highlightButtons, null, true, interactDistance, "Destroy clone"); } void OnSpawnClicked(SVInteractionInfo info) { string username = ResolveTargetUsername(info); if (string.IsNullOrWhiteSpace(username)) { Log("[AvatarCloneControl] No username target for clone spawn."); return; } if (clone != null && !clone.IsNull) clone.Destroy(); Vector3 spawnPosition = GetSpawnPosition(); clone = SpawnAvatarClone(username, spawnPosition, spawnRotation, cloneScale); emotePlaying = false; seated = false; Log("[AvatarCloneControl] Spawned clone of '" + username + "' at " + spawnPosition); } void OnEmoteClicked(SVInteractionInfo info) { if (!HasClone()) return; if (selectedEmote == null || selectedEmote.IsNull) { Log("[AvatarCloneControl] Pick an emote in selectedEmote first."); return; } if (emotePlaying) { clone.StopEmote(); emotePlaying = false; } else { clone.PlayEmote(selectedEmote); emotePlaying = true; } Log("[AvatarCloneControl] clone emote='" + selectedEmote.EmoteId + "' playing=" + emotePlaying); } void OnSitClicked(SVInteractionInfo info) { if (!HasClone()) return; if (cloneSeat == null || cloneSeat.IsNull) { Log("[AvatarCloneControl] Slot a Sitpoint into cloneSeat first."); return; } if (seated) { clone.Stand(); seated = false; } else { clone.Sit(cloneSeat); seated = true; } Log("[AvatarCloneControl] clone seated=" + seated); } void OnDestroyClicked(SVInteractionInfo info) { if (!HasClone()) return; clone.Destroy(); clone = null; emotePlaying = false; seated = false; Log("[AvatarCloneControl] Destroyed clone."); } bool HasClone() { if (clone != null && !clone.IsNull) return true; Log("[AvatarCloneControl] Spawn a clone first."); return false; } string ResolveTargetUsername(SVInteractionInfo info) { string target = targetUsername == null ? "" : targetUsername.Trim(); if (target == "" || target == "*" || target == "**") return info != null ? info.Username : ""; return target; } Vector3 GetSpawnPosition() { if (SpawnButton != null && !SpawnButton.IsNull && SpawnButton.transform != null) return SpawnButton.transform.position + spawnOffset; return transform.position + spawnOffset; } }