C#:
using Facepunch;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.UI;
namespace Oxide.Plugins {
[Info("CustomPortals", "_senyaa", "1.3.1")]
class CustomPortals : RustPlugin {
#region Dependencies
[PluginReference]
Plugin ServerRewards, NoEscape, Clans, Economics;
static bool? TakePoints(ulong playerID, int amount) => Instance.ServerRewards?.Call("TakePoints", playerID, amount) as bool?;
static int? CheckPoints(ulong ID) => Instance.ServerRewards?.Call("CheckPoints", ID) as int?;
static bool? IsRaidBlocked(string target) => Instance.NoEscape?.Call("IsRaidBlocked", target) as bool?;
static bool? IsCombatBlocked(string target) => Instance.NoEscape?.Call("IsCombatBlocked", target) as bool?;
static bool? IsClanMember(string playerId, string otherId) => Instance.Clans?.Call("IsClanMember", playerId, otherId) as bool?;
static bool? IsMemberOrAlly(string playerId, string otherId) => Instance.Clans?.Call("IsMemberOrAlly", playerId, otherId) as bool?;
static bool? Economics_Withdraw(string playerId, double amount) => Instance.Economics?.Call("Withdraw", playerId, amount) as bool?;
#endregion
#region Constants
const string PORTAL_PREFAB = "assets/prefabs/missions/portal/halloweenportalexit.prefab";
const string RADIUS_MAP_MARKER_PREFAB = "assets/prefabs/tools/map/genericradiusmarker.prefab";
const string VENDING_MAP_MARKER_PREFAB = "assets/prefabs/deployable/vendingmachine/vending_mapmarker.prefab";
const string SET_PORTAL_NAME_CCMD = "customportals.portalname.set";
const string SAVE_PORTAL_NAME_CCMD = "customportals.portalname.save";
const string CANCEL_PORTAL_NAME_CCMD = "customportals.portalname.cancel";
const string TOGGLE_PORTAL_BROADCAST_CCMD = "customportals.togglebroadcast";
const string SET_PORTAL_PERMISSION_CCMD = "customportals.permission.set";
const string SAVE_PORTAL_PERMISSION_CCMD = "customportals.permission.save";
const string RENAMING_CUI_NAME = "CustomPortals_RenameUI";
const int HAMMER_ITEMID = 200773292;
const string ADMIN_PERMISSION = "customportals.admin";
const string PORTAL_BUY_PERMISSION = "customportals.buy";
const string PORTAL_FREE_PERMISSION = "customportals.free";
const string PORTAL_USE_PERMISSION = "customportals.use";
const string PORTAL_FREE_ENTRY_PERMISSION = "customportals.freeuse";
const string PORTAL_LIMIT_BYPASS_PERMISSION = "customportals.bypasslimit";
const string PORTAL_BYPASS_LINKING_LIMITATIONS = "customportals.bypasslinkinglimits";
const string PLAYER_DATA_FILENAME = "CustomPortals_PlayerGeneratedData";
const string MONUMENT_DATA_FILENAME = "CustomPortals_MonumentData";
const string CACHED_MAP_DATA_FILENAME = "CustomPortals_CachedCustomMapData";
static Vector3 PORTAL_PLACEMENT_OFFSET = new Vector3(0, 0.35f, 0);
static LayerMask PLAYER_LAYER_MASK = LayerMask.GetMask("Player (Server)");
static LayerMask OBSTRUCTION_MASK = LayerMask.GetMask(new[] {
"Construction", "Deployed", "World", "Terrain"
}
);
#endregion
#region Configuration
class Configuration : SerializableConfiguration {
[JsonProperty("(0.1) Portal Price (set value to 0 to make it free, use ServerRewards or Economics as a key to use RP points or Economics balance respectively)")]
public KeyValuePair<string, int> PortalPrice = new KeyValuePair<string, int>("scrap", 150);
[JsonProperty("(0.2) Entry price (set value to 0 to make it free, use ServerRewards or Economics as a key to use RP points or Economics balance respectively)")]
public KeyValuePair<string, int> PricePerEntry = new KeyValuePair<string, int>("scrap", 0);
[JsonProperty("(0.3) How much HP is reduced when the portal is picked up (0-100)")]
public float HP_Reduction = 0f;
[JsonProperty("(1) Portal Item Name")]
public string PortalItemName = "Portal";
[JsonProperty("(2) Portal Item ID")]
public int PortalItemID = 198438816;
[JsonProperty("(3) Portal Skin ID")]
public ulong PortalSkinID = 2878854808;
[JsonProperty("(4) Amount of portals /buyportal command gives")]
public int AmountPerBuy = 1;
[JsonProperty("(5) Display portal name")]
public bool Display_Portal_Name = false;
[JsonProperty("(6.1) Allow placing portals ONLY when player has building privilege")]
public bool Allow_Placement_Only_In_Building_Privilege = false;
[JsonProperty("(6.2) Allow picking up the portal only in building privilege")]
public bool Allow_Pickup_Only_In_Building_Privilege = false;
[JsonProperty("(6.3) Allow setting up the portal only in building privilege")]
public bool Allow_Setting_Up_Only_In_Building_Privilege = false;
[JsonProperty("(7.1) Enforce player portal limits")]
public bool Enforce_Player_Portal_Limits = false;
[JsonProperty("(7.2) Portal limits | Each entry in this list creates a permission (e.g.4 -> customportals.limit.4), grant this permission to players to enforce limits.")]
public List<int> Portal_Limits = new List<int>() {
4, 8, 12, 16
}
;
[JsonProperty("(8.1) Allow players to enable broadcast on their portals (show portals on map)")]
public bool Allow_Players_To_Enable_Broadcast = false;
[JsonProperty("(8.2) Show monument portals on map")]
public bool Show_Monument_Portals_On_Map = false;
[JsonProperty("(9) Allow to link portals from 0 - everybody; 1 - only yourself; 2 - only yourself and teammates")]
public int AllowLinkingFrom = 0;
[JsonProperty("[NoEscape] Block portal if the player is combat blocked")]
public bool Disable_If_Combat_Blocked = false;
[JsonProperty("[NoEscape] Block portal if the player is raid blocked")]
public bool Disable_If_Raid_Blocked = false;
[JsonProperty("[Clans] Restrict portal usage only to clan members")]
public bool Restrict_To_Clans_Members = false;
[JsonProperty("[Clans] Restrict portal usage only to clan members and allies")]
public bool Restrict_To_Clans_Members_And_Allies = false;
}
#endregion
#region Configuration Boilerplate
static Configuration config;
private class SerializableConfiguration {
public string ToJson() => JsonConvert.SerializeObject(this);
public Dictionary<string, object> ToDictionary() => JsonHelper.Deserialize(ToJson()) as Dictionary<string, object>;
}
private static class JsonHelper {
public static object Deserialize(string json) => ToObject(JToken.Parse(json));
private static object ToObject(JToken token) {
switch (token.Type) {
case JTokenType.Object : return token.Children<JProperty>().ToDictionary(prop => prop.Name, prop => ToObject(prop.Value));
case JTokenType.Array : return token.Select(ToObject).ToList();
default : return ((JValue)token).Value;
}
}
}
private bool ValidateConfig(Dictionary<string, object> currentWithDefaults, Dictionary<string, object> currentRaw) {
bool changed = false;
List<string> oldKeys = new List<string>();
foreach (string key in currentRaw.Keys) {
if (currentWithDefaults.Keys.Contains(key)) {
continue;
}
changed = true;
oldKeys.Add(key);
}
foreach (string key in oldKeys) {
currentRaw.Remove(key);
}
foreach (string key in currentWithDefaults.Keys) {
object currentRawValue;
if (currentRaw.TryGetValue(key, out currentRawValue)) {
Dictionary<string, object> defaultDictValue = currentWithDefaults[key] as Dictionary<string, object>;
Dictionary<string, object> currentDictValue = currentRawValue as Dictionary<string, object>;
if (defaultDictValue != null) {
if (currentDictValue == null) {
currentRaw[key] = currentWithDefaults[key];
changed = true;
continue;
}
if (ValidateConfig(defaultDictValue, currentDictValue)) {
changed = true;
}
}
continue;
}
currentRaw[key] = currentWithDefaults[key];
changed = true;
}
return changed;
}
protected override void LoadDefaultConfig() => config = new Configuration();
protected override void LoadConfig() {
base.LoadConfig();
try {
config = Config.ReadObject<Configuration>();
if (config == null) {
throw new JsonException();
}
Dictionary<string, object> currentWithDefaults = config.ToDictionary();
Dictionary<string, object> currentRaw = Config.ToDictionary(x => x.Key, x => x.Value);
if (ValidateConfig(currentWithDefaults, currentRaw)) {
PrintWarning("Configuration appears to be outdated; updating and saving");
SaveConfig();
}
}
catch {
PrintWarning($"Configuration file {Name}.json is invalid; using defaults");
LoadDefaultConfig();
}
}
protected override void SaveConfig() {
Puts($"Configuration changes saved to {Name}.json");
Config.WriteObject(config, true);
}
#endregion
#region Localization
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
["portalGiven"]
= "Portal has been given to you", ["alreadyLinked"] = "Portals with this name are already linked", ["noPerms"] = "You don't have permission to use this command", ["notEnoughPoints"] = "You don't have enough points to buy the portal", ["notEnoughResources"] = "You don't have enough {0} to buy the portal", ["uiInfoText"] = "Portals with the same name will link with each other", ["uiHeader"] = "RENAME PORTAL", ["changeBtnText"] = "CHANGE NAME", ["cancelBtnText"] = "CANCEL", ["noMonument"] = "You are not on a monument", ["adminCommandUsage"] = "<size=15>Usage:</size>\n/customportals add <portal name> - adds portal to the monument\n/customportals remove - removes portal from the monument\n/customportals reset - removes all portals from the monument", ["lookAtSurface"] = "Look at the surface you want to spawn on", ["addedToMonument"] = "Portal with the name \"{0}\" has been added to {1}", ["removedFromMonument"] = "Removed from {0}", ["lookAtPortal"] = "Make sure you are looking directly at the portal", ["allRemoved"] = "Wiped all data for {0}", ["cannotUse"] = "You can't use this portal", ["setPerm"] = "SET PERMISSION", ["uiAdminInfoText"] = "Set permission for this portal (admin only)", ["combatBlocked"] = "Can't use portals when combat blocked", ["raidBlocked"] = "Can't use portals when raid blocked", ["notInClan"] = "You are not in the same clan as the owner of the portal", ["notInClanOrAlliedClan"] = "You are not in the same or allied clan as the owner of the portal", ["notEnoughPointsToEnter"] = "You don't have enough points to use this portal", ["notEnoughResourcesToEnter"] = "You don't have enough {0} to use this portal", ["canPlaceOnlyInBuildingPrivilege"] = "You need to have building privilege to place portals", ["canSetupOnlyInBuildingPrivilege"] = "You need to have building privilege to setup this portal", ["limitExceeded"] = "You can't place more portals.You've exceeded your limit of {0} portals", ["broadcastOn"] = "ENABLE BROADCAST", ["broadcastOff"] = "DISABLE BROADCAST", ["cantLinkToOthersPortal"] = "Can't link to that portal"
}
, this, "en");
lang.RegisterMessages(new Dictionary<string, string> {
["portalGiven"]
= "Вам был выдан портал", ["alreadyLinked"] = "Порталы с таким названием уже соединены", ["noPerms"] = "У вас нет доступа к этой команде", ["notEnoughPoints"] = "Вам не хватает очков для покупки портала", ["notEnoughResources"] = "Вам не хватает {0} для покупки портала", ["uiInfoText"] = "Порталы с одинаковым названием будут соединены", ["uiHeader"] = "ПЕРЕИМЕНОВАТЬ", ["changeBtnText"] = "ПЕРЕИМЕНОВАТЬ", ["cancelBtnText"] = "ОТМЕНА", ["noMonument"] = "Вы не на РТ", ["adminCommandUsage"] = "<size=15>Использование:</size>\n/customportals add <название портала> - добавить портал на РТ\n/customportals remove - удалить портал с РТ\n/customportals reset - удалить все порталы с РТ", ["lookAtSurface"] = "Смотрите на поверхность на которой хотите заспаунить портал", ["addedToMonument"] = "Портал с названием \"{0}\" был добавлен на {1}", ["removedFromMonument"] = "Удалён с {0}", ["lookAtPortal"] = "Убедитесь, что смотрите на портал", ["allRemoved"] = "Вся информация {0} очищена!", ["cannotUse"] = "Вы не можете использовать этот портал", ["setPerm"] = "SET PERMISSION", ["uiAdminInfoText"] = "Настройте разрешения именно для этого портала", ["combatBlocked"] = "Нельзя использовать порталы в комбат-блоке", ["raidBlocked"] = "Нельзя использовать порталы в рейд-блоке", ["notInClan"] = "Вы не являетесь членом клана владельца портала", ["notInClanOrAlliedClan"] = "Вы не являетесь членом или союзником клана владельца портала", ["notEnoughPointsToEnter"] = "У вас не хватает валюты для использования этого портала", ["notEnoughResourcesToEnter"] = "Вам не хватает {0} для использования этого портала", ["canPlaceOnlyInBuildingPrivilege"] = "Ставить порталы можно только когда есть привилегия строительства", ["canSetupOnlyInBuildingPrivilege"] = "У вас должна быть привилегия строительства чтобы настроить этот портал", ["limitExceeded"] = "Вы больше не можете ставить порталы.Ваш лимит - {0}", ["broadcastOn"] = "ПОКАЗЫВАТЬ МЕСТОПОЛОЖЕНИЕ", ["broadcastOff"] = "СКРЫВАТЬ МЕСТОПОЛОЖЕНИЕ", ["cantLinkToOthersPortal"] = "Нельзя привязать к этому порталу"
}
, this, "ru");
}
static string GetText(string textName, BasePlayer player) => Instance.lang.GetMessage(textName, Instance, player?.UserIDString);
#endregion
#region Fields
public static CustomPortals Instance;
static Dictionary<BasePlayer, string> PortalNameBuffer;
static Dictionary<BasePlayer, string> PortalPermissionBuffer;
static Dictionary<BasePortal, CustomPortalComponent> CustomPortalsDict;
static Dictionary<BasePlayer, CustomPortalComponent> PlayersCurrentlyRenamingPortals;
static Dictionary<MonumentInfo, List<CustomPortalComponent>> MonumentPortals;
static Dictionary<string, List<PositionData>> CustomMapPortals;
static List<BaseNetworkable> ToRemove;
static DataFileSystem dataFiles;
#endregion
#region Types
public class CustomPortalComponent : FacepunchBehaviour {
public static CustomPortalComponent Spawn(Vector3 position, Quaternion rotation, string name = "Unnamed", bool isManuallyPlaced = false, string placedBy = "", string portalPermission = "", bool broadcastEnabled = false, BasePlayer placedByPlayer = null, float itemCondition = 100f) {
BaseEntity portal = GameManager.server.CreateEntity(PORTAL_PREFAB, position, rotation);
portal.Spawn();
if (placedBy != "") {
portal.OwnerID = Convert.ToUInt64(placedBy);
}
portal.EnableSaving(false);
CustomPortalComponent comp = portal.gameObject.AddComponent<CustomPortalComponent>();
comp.IsManuallyPlaced = isManuallyPlaced;
comp.PlacedBy = placedBy;
comp.PortalPermission = portalPermission;
comp.ItemCondition = itemCondition;
if (name != "Unnamed") {
comp.SetPortalName(name, placedByPlayer);
}
comp.SetBroadcastEnabled(broadcastEnabled);
return comp;
}
public static Item CreateItem(int amount = 1, float condition = 100f) {
Item item = ItemManager.CreateByItemID(config.PortalItemID, amount, config.PortalSkinID);
item.name = config.PortalItemName;
item.condition = condition;
return item;
}
public string PortalName {
get {
return _portalName;
}
private set {
_portalName = value;
if (_vendingMarker != null && !_vendingMarker.IsDestroyed) {
_vendingMarker.markerShopName = "Portal - " + value;
_vendingMarker.SendNetworkUpdateImmediate();
}
}
}
public string PortalPermission {
get {
return _permission;
}
set {
_permission = value.Trim();
if (value != "" && !Instance.permission.PermissionExists($"{Instance.Name.ToLower()}.portal.{value}", Instance)) {
Instance.permission.RegisterPermission($"{Instance.Name.ToLower()}.portal.{value}", Instance);
}
}
}
public CustomPortalComponent LinkedPortal {
get;
private set;
}
public bool IsManuallyPlaced {
get;
private set;
}
public string PlacedBy {
get;
private set;
}
public bool IsBroadcastEnabled {
get;
private set;
}
public float ItemCondition {
get;
private set;
}
MapMarkerGenericRadius _marker;
VendingMachineMapMarker _vendingMarker;
BasePortal _portal;
string _permission;
string _portalName;
public BasePortal GetEntity() => _portal;
public void Kill() => _portal.AdminKill();
void Awake() {
_portal = GetComponent<BasePortal>();
PortalName = "Unnamed";
CustomPortalsDict.Add(_portal, this);
if (config.Display_Portal_Name) {
InvokeRepeating(UpdateNameText, 0f, 1f);
}
}
void OnDestroy() {
DisableBroadcast();
CancelInvoke(UpdateNameText);
CustomPortalsDict?.Remove(_portal);
}
public object OnPlayerEnter(BasePlayer player) {
if (!Instance.permission.UserHasPermission(player.UserIDString, PORTAL_USE_PERMISSION) || (_permission != "" && !Instance.permission.UserHasPermission(player.UserIDString, $"{Instance.Name.ToLower()}.portal.{PortalPermission}"))) {
Instance.PrintToChat(player, GetText("cannotUse", player));
return false;
}
if (config.Disable_If_Combat_Blocked && IsCombatBlocked(player.UserIDString) == true) {
Instance.PrintToChat(player, GetText("combatBlocked", player));
return false;
}
if (config.Disable_If_Raid_Blocked && IsRaidBlocked(player.UserIDString) == true) {
Instance.PrintToChat(player, GetText("raidBlocked", player));
return false;
}
if (PlacedBy != "" && player.UserIDString != PlacedBy) {
if (config.Restrict_To_Clans_Members && IsClanMember(player.UserIDString, PlacedBy) == false) {
Instance.PrintToChat(player, GetText("notInClan", player));
return false;
}
if (config.Restrict_To_Clans_Members_And_Allies && IsMemberOrAlly(player.UserIDString, PlacedBy) == false) {
Instance.PrintToChat(player, GetText("notInClanOrAlliedClan", player));
return false;
}
}
Item activeItem = player.GetActiveItem();
if (IsManuallyPlaced && ((!config.Allow_Pickup_Only_In_Building_Privilege && !player.IsBuildingBlocked()) || (config.Allow_Pickup_Only_In_Building_Privilege && player.IsBuildingAuthed())) && activeItem != null && activeItem.info.itemid == HAMMER_ITEMID) {
player.GiveItem(CreateItem(1, ItemCondition - config.HP_Reduction), BaseEntity.GiveItemReason.PickedUp);
_portal.AdminKill();
return false;
}
if (LinkedPortal == null && IsManuallyPlaced) {
if (!((!config.Allow_Setting_Up_Only_In_Building_Privilege && !player.IsBuildingBlocked()) || (config.Allow_Setting_Up_Only_In_Building_Privilege && player.IsBuildingAuthed()))) {
player.ShowToast(GameTip.Styles.Red_Normal, GetText("canSetupOnlyInBuildingPrivilege", player));
return false;
}
StartSettingUpPortal(player);
return false;
}
string price_key = config.PricePerEntry.Key.ToLower();
if ((price_key == "serverrewards" || price_key == "economics") && !CheckDependencyAndPrintWarning(price_key, player)) {
return false;
}
if (!Instance.permission.UserHasPermission(player.UserIDString, PORTAL_FREE_ENTRY_PERMISSION) && config.PricePerEntry.Value > 0) {
switch (price_key) {
case "serverrewards" : int? points = CheckPoints(player.userID);
if (points == null || points < config.PricePerEntry.Value) {
Instance.PrintToChat(player, GetText("notEnoughPointsToEnter", player));
return false;
}
TakePoints(player.userID, config.PricePerEntry.Value);
break;
case "economics" : if (Economics_Withdraw(player.UserIDString, config.PricePerEntry.Value) == false) {
Instance.PrintToChat(player, GetText("notEnoughPointsToEnter", player));
return false;
}
break;
default : ItemDefinition itemDef = ItemManager.FindItemDefinition(config.PricePerEntry.Key);
if (player.inventory.GetAmount(itemDef.itemid) < config.PricePerEntry.Value) {
Instance.PrintToChat(player, string.Format(GetText("notEnoughResourcesToEnter", player), itemDef.displayName.english.ToLower()));
return false;
}
player.inventory.Take(null, itemDef.itemid, config.PricePerEntry.Value);
break;
}
}
return null;
}
public void StartSettingUpPortal(BasePlayer player) {
if (PlayersCurrentlyRenamingPortals.ContainsKey(player)) {
PlayersCurrentlyRenamingPortals[player] = this;
}
else {
PlayersCurrentlyRenamingPortals.Add(player, this);
}
ShowRenamingUI(player, PortalName, config.Allow_Players_To_Enable_Broadcast, IsBroadcastEnabled);
if (Instance.permission.UserHasPermission(player.UserIDString, ADMIN_PERMISSION)) {
ShowAdminUI(player, PortalPermission);
}
}
public bool SetPortalName(string newName, BasePlayer changedBy = null) {
CustomPortalComponent portalWithTheSameName = null;
int count = 0;
foreach (CustomPortalComponent portal in CustomPortalsDict.Values) {
if (portal == this) {
continue;
}
if (portal.PortalName != newName || portal.PortalName == "Unnamed") {
continue;
}
portalWithTheSameName = portal;
count++;
}
if (count >= 2) {
Instance.PrintWarning($"Trying to rename a portal to \"{newName}\", but there are more than 2 portals with that name!");
return false;
}
string otherPortalOwnerID = portalWithTheSameName?.PlacedBy;
if (otherPortalOwnerID != null && changedBy != null && config.AllowLinkingFrom > 0 && otherPortalOwnerID != "" && !Instance.permission.UserHasPermission(changedBy.UserIDString, PORTAL_BYPASS_LINKING_LIMITATIONS)) {
bool isOk = false;
switch (config.AllowLinkingFrom) {
case 1 : isOk = otherPortalOwnerID == PlacedBy;
break;
case 2 : isOk = otherPortalOwnerID == PlacedBy;
foreach (ulong teammateId in changedBy.Team.members) {
if (teammateId.ToString() != otherPortalOwnerID) {
continue;
}
isOk = true;
break;
}
break;
}
if (!isOk) {
ShowRenamingErrorUI(changedBy, GetText("cantLinkToOthersPortal", changedBy));
return false;
}
}
PortalName = newName;
if (count == 1) {
LinkPortal(portalWithTheSameName);
}
else {
Unlink();
}
return true;
}
public void Unlink() {
if (LinkedPortal == null) {
return;
}
LinkedPortal._portal.targetPortal = null;
LinkedPortal._portal.LinkPortal();
LinkedPortal.LinkedPortal = null;
_portal.targetPortal = null;
_portal.LinkPortal();
LinkedPortal = null;
}
public void LinkPortal(CustomPortalComponent portalToLinkTo) {
if (portalToLinkTo?._portal?.net == null) {
return;
}
LinkedPortal = portalToLinkTo;
LinkedPortal.LinkedPortal = this;
_portal.targetPortal = LinkedPortal._portal;
LinkedPortal._portal.targetPortal = _portal;
_portal.isUsablePortal = true;
LinkedPortal._portal.isUsablePortal = true;
_portal.LinkPortal();
LinkedPortal._portal.LinkPortal();
_portal.SendNetworkUpdateImmediate();
LinkedPortal._portal.SendNetworkUpdateImmediate();
}
public void SetBroadcastEnabled(bool isEnabled) {
if (isEnabled) {
EnableBroadcast();
}
else {
DisableBroadcast();
}
}
public void ToggleBroadcast() {
if (IsBroadcastEnabled) {
DisableBroadcast();
}
else {
EnableBroadcast();
}
}
void EnableBroadcast() {
if (_marker != null && !_marker.IsDestroyed) {
return;
}
_marker = GameManager.server.CreateEntity(RADIUS_MAP_MARKER_PREFAB, transform.position) as MapMarkerGenericRadius;
_marker.alpha = 1f;
_marker.color1 = Color.magenta;
_marker.color2 = Color.white;
_marker.radius = 0.1f;
_marker.Spawn();
_marker.SendUpdate();
_marker.EnableSaving(false);
_vendingMarker = GameManager.server.CreateEntity(VENDING_MAP_MARKER_PREFAB, transform.position) as VendingMachineMapMarker;
_vendingMarker.markerShopName = "Portal - " + PortalName;
_vendingMarker.Spawn();
_vendingMarker.EnableSaving(false);
IsBroadcastEnabled = true;
}
void DisableBroadcast() {
IsBroadcastEnabled = false;
_marker?.Kill();
_vendingMarker?.Kill();
_vendingMarker = null;
_marker = null;
}
void UpdateNameText() {
List<BasePlayer> players = Pool.GetList<BasePlayer>();
Vis.Entities(transform.position, 5f, players, PLAYER_LAYER_MASK, QueryTriggerInteraction.Ignore);
foreach (BasePlayer player in players) {
if (player.IsNpc || player.IPlayer == null) {
continue;
}
if (Physics.Raycast(player.eyes.HeadRay(), player.Distance(transform.position), OBSTRUCTION_MASK, QueryTriggerInteraction.Ignore)) {
continue;
}
bool wasAdmin = player.IsAdmin;
if (!wasAdmin) {
player.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, true);
player.SendNetworkUpdateImmediate();
}
player.SendConsoleCommand("ddraw.text", 1f, Color.cyan, transform.position + Vector3.up * 1.5f, PortalName);
if (!wasAdmin) {
player.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, false);
player.SendNetworkUpdateImmediate();
}
}
Pool.FreeList(ref players);
}
}
#endregion
#region Oxide Hooks
void Init() {
Instance = this;
dataFiles = new DataFileSystem($"{Interface.Oxide.DataDirectory}\\{Name}");
CustomPortalsDict = new Dictionary<BasePortal, CustomPortalComponent>();
PortalNameBuffer = new Dictionary<BasePlayer, string>();
PortalPermissionBuffer = new Dictionary<BasePlayer, string>();
CustomMapPortals = new Dictionary<string, List<PositionData>>();
ToRemove = new List<BaseNetworkable>();
PlayersCurrentlyRenamingPortals = new Dictionary<BasePlayer, CustomPortalComponent>();
MonumentPortals = new Dictionary<MonumentInfo, List<CustomPortalComponent>>();
permission.RegisterPermission(ADMIN_PERMISSION, this);
permission.RegisterPermission(PORTAL_BUY_PERMISSION, this);
permission.RegisterPermission(PORTAL_FREE_PERMISSION, this);
permission.RegisterPermission(PORTAL_USE_PERMISSION, this);
permission.RegisterPermission(PORTAL_FREE_ENTRY_PERMISSION, this);
permission.RegisterPermission(PORTAL_LIMIT_BYPASS_PERMISSION, this);
permission.RegisterPermission(PORTAL_BYPASS_LINKING_LIMITATIONS, this);
}
void Loaded() {
foreach (int limit in config.Portal_Limits) {
string perm = $"customportals.limit.{limit}";
if (permission.PermissionExists(perm, this)) {
continue;
}
permission.RegisterPermission(perm, this);
}
}
void Unload() {
SavePlayerData();
CacheCustomMapData();
foreach (CustomPortalComponent portal in CustomPortalsDict.Values) {
portal.Kill();
}
}
void OnServerInitialized(bool initial) {
PortalDataLoader.Instantiate().BeginLoadingData(initial);
}
void OnServerSave() => SavePlayerData();
void OnNewSave(string filename) {
PrintWarning("Server wiped! Clearing portal data...");
ClearPlayerData();
ClearCustomMapCache();
}
object OnPortalUse(BasePlayer player, BasePortal portal) {
if (!CustomPortalsDict.ContainsKey(portal)) {
return null;
}
return CustomPortalsDict[portal].OnPlayerEnter(player);
}
object CanBuild(Planner plan, Construction prefab, Construction.Target target) {
if (plan == null) {
return null;
}
Item item = plan.GetItem();
if (item == null) {
return null;
}
BasePlayer player = plan.GetOwnerPlayer();
if (player == null) {
return null;
}
if (!(item.info.itemid == config.PortalItemID && item.skin == config.PortalSkinID)) {
return null;
}
if (config.Enforce_Player_Portal_Limits && !permission.UserHasPermission(player.UserIDString, PORTAL_LIMIT_BYPASS_PERMISSION)) {
int playerPortals = 0;
foreach (CustomPortalComponent portal in CustomPortalsDict.Values) {
if (portal.PlacedBy != player.UserIDString) {
continue;
}
playerPortals++;
}
int playerLimit = -1;
foreach (string perm in permission.GetUserPermissions(player.UserIDString)) {
if (!perm.StartsWith("customportals.limit.")) {
continue;
}
int limit = Convert.ToInt32(perm.Split('.')[2]);
if (playerLimit < limit) {
playerLimit = limit;
}
}
if (playerLimit != -1 && playerPortals >= playerLimit) {
player.ShowToast(GameTip.Styles.Red_Normal, string.Format(GetText("limitExceeded", player), playerLimit));
return false;
}
}
if (config.Allow_Placement_Only_In_Building_Privilege && !player.IsBuildingAuthed()) {
player.ShowToast(GameTip.Styles.Red_Normal, GetText("canPlaceOnlyInBuildingPrivilege", player));
return false;
}
return null;
}
void OnEntityBuilt(Planner plan, GameObject go) {
if (plan == null) {
return;
}
Item item = plan.GetItem();
if (item == null) {
return;
}
BasePlayer player = plan.GetOwnerPlayer();
if (player == null) {
return;
}
if (item.info.itemid == config.PortalItemID && item.skin == config.PortalSkinID) {
CustomPortalComponent portal = CustomPortalComponent.Spawn(go.transform.position - PORTAL_PLACEMENT_OFFSET, go.transform.rotation, isManuallyPlaced : true, placedBy : player.UserIDString, placedByPlayer : player, itemCondition : item.condition);
NextFrame(() => {
go.GetComponent<BaseEntity>().AdminKill();
}
);
}
}
void OnWorldPrefabSpawned(GameObject gameObject, string category) {
if (!category.ToLower().Contains("customportals")) {
return;
}
try {
string portalName = category.ToLower().Split('.')[1].Split(':')[0].ToLower().Trim();
BaseNetworkable ent = gameObject.GetComponent<BaseNetworkable>();
PositionData posData = new PositionData(gameObject.transform.position, gameObject.transform.rotation);
if (!CustomMapPortals.ContainsKey(portalName)) {
CustomMapPortals.Add(portalName, new List<PositionData>());
}
CustomMapPortals[portalName].Add(posData);
ToRemove.Add(ent);
}
catch {
PrintWarning($"Portal name \"{category}\" is invalid!");
}
}
#endregion
#region UI
public static void ShowRenamingUI(BasePlayer player, string initialName, bool showBroadcastBtn, bool isBroadcastEnabled = false) {
CuiElementContainer elements = new CuiElementContainer();
elements.Add(new CuiElement {
Name = RENAMING_CUI_NAME, Parent = "Overlay", Components = {
new CuiNeedsCursorComponent(), new CuiNeedsKeyboardComponent(), new CuiImageComponent {
Material = "assets/content/ui/uibackgroundblur-ingamemenu.mat", Sprite = "assets/content/ui/ui.background.transparent.radial.psd", Color = "0.1286 0.1808 0.1512 0.954"
}
, new CuiRectTransformComponent {
AnchorMin = "0 0", AnchorMax = "1 1"
}
}
}
);
elements.Add(new CuiElement {
Name = RENAMING_CUI_NAME + "_textboxPanel", Parent = RENAMING_CUI_NAME, Components = {
new CuiImageComponent {
Color = "0.9686 0.9216 0.8824 0.298", Material = "assets/content/ui/uibackgroundblur-ingamemenu.mat"
}
, new CuiRectTransformComponent {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-156 -18", OffsetMax = "156 18"
}
}
}
);
elements.Add(new CuiElement {
Name = RENAMING_CUI_NAME + "_textboxPanel_textbox", Parent = RENAMING_CUI_NAME + "_textboxPanel", Components = {
new CuiInputFieldComponent {
Color = "1 1 1 1", LineType = InputField.LineType.SingleLine, Command = SET_PORTAL_NAME_CCMD, IsPassword = false, CharsLimit = 25, ReadOnly = false, Font = "robotocondensed-bold.ttf", FontSize = 18, Align = TextAnchor.MiddleCenter, Text = initialName,
}
, new CuiRectTransformComponent {
AnchorMin = "0 0", AnchorMax = "1 1",
}
}
}
);
elements.Add(new CuiElement {
Name = RENAMING_CUI_NAME + "_cancelBtn", Parent = RENAMING_CUI_NAME, Components = {
new CuiButtonComponent {
Color = "0.909 0.458 0.388 0.77", Material = "assets/content/ui/uibackgroundblur-ingamemenu.mat", Command = CANCEL_PORTAL_NAME_CCMD,
}
, new CuiRectTransformComponent {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-156 -57", OffsetMax = "-2 -23"
}
}
}
);
elements.Add(new CuiElement {
Name = RENAMING_CUI_NAME + "_cancelBtn_text", Parent = RENAMING_CUI_NAME + "_cancelBtn", Components = {
new CuiTextComponent {
Text = GetText("cancelBtnText", player), Color = "0.9686 0.9216 0.8824 1", Align = TextAnchor.MiddleCenter, Font = "robotocondensed-bold.ttf" , FontSize = 18
}
, new CuiRectTransformComponent {
AnchorMin = "0 0", AnchorMax = "1 1",
}
}
}
);
elements.Add(new CuiElement {
Name = RENAMING_CUI_NAME + "_saveBtn", Parent = RENAMING_CUI_NAME, Components = {
new CuiButtonComponent {
Color = "0.5725 0.6549 0.4235 1", Command = SAVE_PORTAL_NAME_CCMD, Material = "assets/content/ui/uibackgroundblur-ingamemenu.mat"
}
, new CuiRectTransformComponent {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "2 -57", OffsetMax = "156 -23"
}
}
}
);
elements.Add(new CuiElement {
Name = RENAMING_CUI_NAME + "_saveBtn_text", Parent = RENAMING_CUI_NAME + "_saveBtn", Components = {
new CuiTextComponent {
Text = GetText("changeBtnText", player), Color = "0.9686 0.9216 0.8824 1", Align = TextAnchor.MiddleCenter, Font = "robotocondensed-bold.ttf", FontSize = 18
}
, new CuiRectTransformComponent {
AnchorMin = "0 0", AnchorMax = "1 1",
}
}
}
);
elements.Add(new CuiElement {
Name = RENAMING_CUI_NAME + "_title", Parent = RENAMING_CUI_NAME, Components = {
new CuiTextComponent {
Text = GetText("uiHeader", player), Font = "robotocondensed-bold.ttf", FontSize = 44, Align = TextAnchor.MiddleCenter, Color = "0.9686 0.9216 0.8824 0.568"
}
, new CuiRectTransformComponent {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-185 20", OffsetMax = "185 100",
}
}
}
);
elements.Add(new CuiElement {
Name = RENAMING_CUI_NAME + "_infotext", Parent = RENAMING_CUI_NAME, Components = {
new CuiTextComponent {
Text = GetText("uiInfoText", player), Font = "robotocondensed-bold.ttf", FontSize = 16, Align = TextAnchor.UpperCenter, Color = "0.9686 0.9216 0.8824 1"
}
, new CuiOutlineComponent {
Color = "0 0 0 0.4", Distance = "1 -1", UseGraphicAlpha = true
}
, new CuiRectTransformComponent {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-285 -85", OffsetMax = "285 -65",
}
}
}
);
CuiHelper.DestroyUi(player, RENAMING_CUI_NAME);
CuiHelper.AddUi(player, elements);
if (showBroadcastBtn) {
DrawBroadcastBtn(player, isBroadcastEnabled);
}
}
public static void DrawBroadcastBtn(BasePlayer player, bool isBroadcastEnabled) {
CuiElementContainer elements = new CuiElementContainer();
elements.Add(new CuiElement {
Name = RENAMING_CUI_NAME + "_broadcastBtn", Parent = RENAMING_CUI_NAME, Components = {
new CuiButtonComponent {
Color = isBroadcastEnabled ? "0.909 0.458 0.388 0.77" : "0.5725 0.6549 0.4235 1", Command = TOGGLE_PORTAL_BROADCAST_CCMD, FadeIn = 0.1f, Material = "assets/content/ui/uibackgroundblur-ingamemenu.mat"
}
, new CuiRectTransformComponent {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-156 -157", OffsetMax = "156 -123"
}
}
}
);
elements.Add(new CuiElement {
Name = RENAMING_CUI_NAME + "broadcastBtn_text", Parent = RENAMING_CUI_NAME + "_broadcastBtn", Components = {
new CuiTextComponent {
Text = GetText(isBroadcastEnabled ? "broadcastOff" : "broadcastOn", player), Align = TextAnchor.MiddleCenter, Font = "robotocondensed-bold.ttf", Color = "0.9686 0.9216 0.8824 1", FontSize = 18
}
, new CuiRectTransformComponent {
AnchorMin = "0 0", AnchorMax = "1 1",
}
}
}
);
CuiHelper.DestroyUi(player, RENAMING_CUI_NAME + "_broadcastBtn");
CuiHelper.AddUi(player, elements);
}
public static void ShowRenamingErrorUI(BasePlayer player, string error_message) {
CuiElementContainer elements = new CuiElementContainer();
elements.Add(new CuiElement {
Name = RENAMING_CUI_NAME + "_errorText", Parent = RENAMING_CUI_NAME, Components = {
new CuiTextComponent {
Text = error_message, FontSize = 17, Font = "robotocondensed-bold.ttf", FadeIn = 0.1f, Align = TextAnchor.UpperCenter, Color = "0.909 0.258 0.288 1",
}
, new CuiOutlineComponent {
Color = "0 0 0 0.5", Distance = "1 -1", UseGraphicAlpha = true
}
, new CuiRectTransformComponent {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-285 -115", OffsetMax = "285 -90"
}
}
}
);
CuiHelper.DestroyUi(player, RENAMING_CUI_NAME + "_errorText");
CuiHelper.AddUi(player, elements);
}
public static void ShowAdminUI(BasePlayer player, string portalPerm) {
CuiElementContainer elements = new CuiElementContainer();
elements.Add(new CuiElement {
Name = RENAMING_CUI_NAME + "_permissiontextboxPanel", Parent = RENAMING_CUI_NAME, Components = {
new CuiImageComponent {
Color = "0.9686 0.9216 0.8824 0.298", Material = "assets/content/ui/uibackgroundblur-ingamemenu.mat"
}
, new CuiRectTransformComponent {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-156 -260", OffsetMax = "156 -221"
}
}
}
);
elements.Add(new CuiElement {
Name = RENAMING_CUI_NAME + "_permissiontextboxPanel_textbox", Parent = RENAMING_CUI_NAME + "_permissiontextboxPanel", Components = {
new CuiInputFieldComponent {
Color = "1 1 1 1", LineType = InputField.LineType.SingleLine, Command = SET_PORTAL_PERMISSIO
// ... [Code preview truncated due to length. Full source in attached file] ...