public void Setup(RespawnInformation.SpawnOptions options) { this.button = base.GetComponent <Button>(); this.spawnOptions = options; if (options.unlockSeconds <= 0f) { this.button.interactable = true; this.timerInfo.SetActive(false); this.releaseTime = 0f; } else { this.button.interactable = false; this.timerInfo.SetActive(true); this.releaseTime = Time.realtimeSinceStartup + options.unlockSeconds; } this.BagName.text = this.friendlyName; }
public void Setup(RespawnInformation.SpawnOptions options) { this.button = (Button)((Component)this).GetComponent <Button>(); this.spawnOptions = options; if (options.unlockSeconds > 0.0) { ((Selectable)this.button).set_interactable(false); this.timerInfo.SetActive(true); this.releaseTime = Time.get_realtimeSinceStartup() + (float)options.unlockSeconds; } else { ((Selectable)this.button).set_interactable(true); this.timerInfo.SetActive(false); this.releaseTime = 0.0f; } this.BagName.set_text(this.friendlyName); }
// command delegator void CommandDelegator(BasePlayer player, string command, string[] args) { if (!hasPermission(player, PermCanUse)) { return; } string message = "InvalidCommand"; // assume args[0] is the command (beyond /bed) if (args != null && args.Length > 0) { command = args[0]; } // shift arguments if (args != null) { if (args.Length > 1) { args = args.Skip(1).ToArray(); } else { args = new string[] { } }; } object[] opts = new object[] { command }; if (Enum.IsDefined(typeof(Command), command)) { Command cmd = (Command)Enum.Parse(typeof(Command), command); if ((!hasPermission(player, PermCanSharePublic) && (cmd == Command.share || cmd == Command.unshare)) || (!hasPermission(player, PermCanSharePrivate) && (cmd == Command.sharewith || cmd == Command.unsharewith))) { return; } switch (cmd) { case Command.clear: if (hasPermission(player, PermCanClear)) { HandleClear(out message, out opts); } else { message = "NoClearPerm"; } break; case Command.share: HandleShare(player, true, null, out message, out opts); break; case Command.sharewith: if (args == null && args.Length == 0) { message = "InvalidArguments"; break; } HandleShare(player, true, args, out message, out opts); break; case Command.status: HandleStatus(player, out message, out opts); break; case Command.unshare: HandleShare(player, false, null, out message, out opts); break; case Command.unsharewith: if (args == null && args.Length == 0) { message = "InvalidArguments"; break; } HandleShare(player, false, args, out message, out opts); break; default: break; } } else { ShowCommands(out message, out opts); } if (message != null && message != "") { SendMessage(player, message, opts); } } // handle sharing/unsharing void HandleShare(BasePlayer player, bool share, string[] args, out string message, out object[] opts) { message = "ShareSuccess"; opts = new object[] { "bag", share ? "shared" : "unshared" }; bool with = args != null; bool all = false; List <ulong> players = new List <ulong>(); List <string> names = new List <string>(); if (with) { foreach (string s in args) { if (!share && s == "all") { all = true; break; } BasePlayer p = rust.FindPlayer(s); if (p == null) { names.Add(s); continue; } players.Add(p.userID); } } if (with && !all && (players.Count == 0 || players.Count != args.Length)) { message = "PlayersNotFound"; opts = new object[] { string.Join(", ", names.ToArray()) }; return; } object entity; if (GetRaycastTarget(player, out entity) && entity is SleepingBag) { SleepingBag bag = entity as SleepingBag; if (bag.ShortPrefabName == BedPrefabName) { opts[0] = "bed"; } if (bag.deployerUserID != player.userID && !isAdmin(player)) { message = "CannotShareOther"; opts[1] = "share"; return; } else { if (share) { bag.secondsBetweenReuses = 0f; data.AddOrUpdateBag(bag.net.ID, bag.deployerUserID, players); playerNameCache[player.userID] = player.displayName; } else { if (!data.RemoveOrUpdateBag(bag.net.ID, players, all)) { message = "NotShared"; } } } } else { message = "NoBag"; opts = new object[] { }; } } // handle checking status of a bed/bag void HandleStatus(BasePlayer player, out string message, out object[] opts) { message = "Status"; opts = new object[] { "bag", "unshared" }; object entity; if (GetRaycastTarget(player, out entity) && entity is SleepingBag) { SleepingBag bag = entity as SleepingBag; if (bag.ShortPrefabName == BedPrefabName) { opts[0] = "bed"; } SharedBagInfo i = data.sharedBags.FirstOrDefault(s => s.bagId == bag.net.ID); if (i != null) { opts[1] = "shared " + (i.isPublic ? " (public)" : " (private)"); } } else { message = "NoBag"; opts = new object[] { }; } } // handle clearing shared bag/beds void HandleClear(out string message, out object[] opts) { message = "ClearSuccess"; opts = new object[] { data.sharedBags.Count }; data.sharedBags.Clear(); SaveData(); } #endregion #region Hooks // on player death, wait for 1s then rebuild respawnInformation including shared beds/bags object OnPlayerDie(BasePlayer player, HitInfo hitinfo) { if (!data.HasSharedBags() || hasPermission(player, PermNoShare, false)) { return(null); } // after 1 second, send player updated respawn info timer.Once(1f, () => { using (RespawnInformation respawnInformation = Pool.Get <RespawnInformation>()) { respawnInformation.spawnOptions = Pool.Get <List <RespawnInformation.SpawnOptions> >(); SleepingBag[] sleepingBagArray = SleepingBag.FindForPlayer(player.userID, true); for (int i = 0; i < (int)sleepingBagArray.Length; i++) { SleepingBag sleepingBag = sleepingBagArray[i]; if (data.sharedBags.Count(s => s.bagId == sleepingBag.net.ID) > 0 || dummyBags.ContainsKey(sleepingBag.net.ID)) { continue; } RespawnInformation.SpawnOptions d = Pool.Get <RespawnInformation.SpawnOptions>(); d.id = sleepingBag.net.ID; d.name = sleepingBag.niceName; d.type = RespawnInformation.SpawnOptions.RespawnType.SleepingBag; d.unlockSeconds = sleepingBag.unlockSeconds; respawnInformation.spawnOptions.Add(d); } respawnInformation.previousLife = SingletonComponent <ServerMgr> .Instance.persistance.GetLastLifeStory(player.userID); respawnInformation.fadeIn = (respawnInformation.previousLife == null ? false : respawnInformation.previousLife.timeDied > (Epoch.Current - 5)); player.ClientRPCPlayer(null, player, "OnRespawnInformation", respawnInformation); } }); // after 6 seconds, build/display shared bag/bed UI if (data.HasSharedBags()) { timer.Once(6f, () => ShowGUI(player)); } return(null); } // on respawn, destroy dummy bags and gui object OnPlayerRespawn(BasePlayer player) { DestroyDummyBags(player); DestroyGUI(player); return(null); } #endregion #region GUI // find or create a PlayerUI PlayerUI FindPlayerUI(BasePlayer player) { PlayerUI ui = playerUIs.FirstOrDefault(u => u.UserId == player.userID); if (ui == null) { playerUIs.Add(ui = new PlayerUI(player)); } return(ui); } void ShowGUI(BasePlayer player) { PlayerUI ui = FindPlayerUI(player); bool dirty = false; int counter = 0; foreach (SharedBagInfo entry in data.sharedBags.Where(i => i.isPublic || i.sharedWith.Contains(player.userID))) { SleepingBag sleepingBag = SleepingBag.FindForPlayer(entry.owner, entry.bagId, true); if (sleepingBag == null) { dirty = true; // no longer a valid shared bag continue; } uint bagId; if (SpawnDummyBag(sleepingBag, player, out bagId)) { string messageName = "SharedBagNameText"; if (!entry.isPublic) { messageName = "SharedBagNameTextPrivate"; } string bagName = string.Format(GetMessage(messageName, player.UserIDString), new object[] { sleepingBag.niceName, GetPlayerName(sleepingBag.deployerUserID) }); CreateRespawnButton(ui, bagId, bagName, counter++); } } // save changes to shared mappings if (dirty) { ValidateSharedBags(); } } void RefreshUI(PlayerUI ui, uint bagId) { BagUIInfo bag = ui.FindBagInfo(bagId); if (bag == null || !ui.IsPanelOpen(bagId)) { return; } CreateRespawnButton(ui, bag.id, bag.name, bag.index); } // build respawn button void CreateRespawnButton(PlayerUI ui, uint bagId, string bagName, int index) { BagUIInfo bag = new BagUIInfo() { id = bagId, index = index, name = bagName }; // set up button position float xPosMin = data.ui.screenMarginX; float yPosMin = data.ui.screenMarginY + ((data.ui.verticalSpacer + data.ui.buttonHeight) * index); float xPosMax = xPosMin + data.ui.buttonWidth; float yPosMax = yPosMin + data.ui.buttonHeight; Vector2 buttonAnchorMin = new Vector2(xPosMin, yPosMin); Vector2 buttonAnchorMax = new Vector2(xPosMax, yPosMax); // set up icon layout float iconXMin = data.ui.iconPaddingX - data.ui.iconWidth; float iconYMin = data.ui.iconPaddingY; float iconXMax = data.ui.iconPanelWidth - data.ui.iconWidth; float iconYMax = 1f - iconYMin; Vector2 iconPosMin = new Vector2(iconXMin, iconYMin); Vector2 iconPosMax = new Vector2(iconXMax, iconYMax); // set up text layout float spawnTextYMin = data.ui.spawnTextPaddingY; Vector2 spawnTextPosMin = new Vector2(iconXMax, spawnTextYMin); Vector2 spawnTextPosMax = Vector2.one; float bagNameTextYMin = data.ui.bagNameTextPaddingY; Vector2 bagNameTextPosMin = new Vector2(iconXMax, bagNameTextYMin); Vector2 bagNameTextPosMax = new Vector2(1f, spawnTextYMin); string headerText = GetMessage("SharedHeaderText", ui.Player?.UserIDString); // build GUI elements string elementName = UIElementPrefix + bagId; string message = ui.Message(bagId); CuiElementContainer container = UI.CreateElementContainer(elementName, data.ui.buttonColor, buttonAnchorMin, buttonAnchorMax); UI.CreateLabel(ref container, elementName, headerText, 16, spawnTextPosMin, spawnTextPosMax, TextAnchor.MiddleLeft); UI.CreateLabel(ref container, elementName, bagName, 12, bagNameTextPosMin, bagNameTextPosMax, TextAnchor.UpperLeft, "RobotoCondensed-Regular.ttf"); UI.CreateImage(ref container, elementName, data.ui.bagIcon, data.ui.bagIconColor, iconPosMin, iconPosMax); UI.CreateButton(ref container, elementName, new Color(1f, 1f, 1f, 0.05f), string.Empty, 1, Vector2.zero, Vector2.one, ui.BuildCommand(GUIRespawnCommand, bagId)); ui.CreateUI(bag, container); if (!string.IsNullOrEmpty(message)) { OverlayMessageUI(ui, bagId, message, buttonAnchorMin, buttonAnchorMax); } } // create a message overlay on a button void OverlayMessageUI(PlayerUI ui, uint id, string message, Vector2 aMin, Vector2 aMax, float displayTime = 5f) { string panelName = UIElementPrefix + id + "_Message"; CuiElementContainer container = UI.CreateElementContainer(panelName, new Color(0.7f, 0.3f, 0.3f, 0.9f), aMin, aMax, fadeIn: 0f); UI.CreateLabel(ref container, panelName, $"<color=white>{message}</color>", 14, Vector2.zero, Vector2.one, fadeIn: 0f); ui.CreateMessageUI(id, container); timer.In(displayTime, () => ui.DestroyMessageUI(id)); } // destroy a player's GUI elements void DestroyGUI(BasePlayer player, bool kill = false) { if (kill) { FindPlayerUI(player).Kill(); } else { FindPlayerUI(player).DestroyUI(); } } // destroy all player GUI elements void DestroyAllGUI() { foreach (BasePlayer player in BasePlayer.activePlayerList) { DestroyGUI(player, true); } } #endregion #region Helper Procedures // spawn a dummy bag at location of shared bag/bed to be used as a respawn point bool SpawnDummyBag(SleepingBag bag, BasePlayer player, out uint bagId) { bagId = 0; BaseEntity entity = GameManager.server.CreateEntity(bag.PrefabName, bag.transform.position, bag.transform.rotation, false); entity.limitNetworking = true; entity.Spawn(); if (entity != null && entity is SleepingBag) { SleepingBag newBag = entity as SleepingBag; newBag.model.enabled = false; newBag.deployerUserID = player.userID; newBag.secondsBetweenReuses = 0f; bagId = newBag.net.ID; dummyBags[bagId] = player.userID; return(true); } return(false); } // Destroy all dummy bags for a player void DestroyDummyBags(BasePlayer player) { uint[] bags = dummyBags.Where(x => x.Value == player.userID).Select(pair => pair.Key).ToArray(); if (bags == null || bags.Length == 0) { return; } foreach (uint bagId in bags) { SleepingBag.DestroyBag(player, bagId); } } // Destroy all dummy bags void DestroyAllDummyBags() { foreach (KeyValuePair <uint, ulong> entry in dummyBags) { SleepingBag bag = SleepingBag.FindForPlayer(entry.Value, entry.Key, true); if (bag != null) { bag.Kill(BaseNetworkable.DestroyMode.None); } } dummyBags.Clear(); } // validate shared bag list void ValidateSharedBags() { if (!data.HasSharedBags()) { return; } List <uint> toRemove = new List <uint>(); // check each bag in the shared bags list and remove any invalid bags foreach (SharedBagInfo entry in data.sharedBags) { SleepingBag sleepingBag = SleepingBag.FindForPlayer(entry.owner, entry.bagId, true); if (sleepingBag == null) { toRemove.Add(entry.bagId); // no longer a valid shared bag } } if (data.sharedBags.RemoveWhere(i => toRemove.Contains(i.bagId)) > 0) { Puts(GetMessage("Prefix") + string.Format(GetMessage("ValidateStats"), new object[] { toRemove.Count })); SaveData(); } } // handle raycast from player bool GetRaycastTarget(BasePlayer player, out object closestEntity) { closestEntity = false; RaycastHit hit; if (!Physics.Raycast(player.eyes.HeadRay(), out hit, 5f)) { return(false); } closestEntity = hit.GetEntity(); return(true); } // get a player name (using cache if possible) string GetPlayerName(ulong userID) { if (playerNameCache.ContainsKey(userID)) { return(playerNameCache[userID]); } else { BasePlayer player = BasePlayer.FindByID(userID); if (player == null) { player = BasePlayer.FindSleeping(userID); } if (player != null) { playerNameCache[userID] = player.displayName; return(player.displayName); } } return("unknown"); }