C#:
using Network;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using WebSocketSharp;
namespace Oxide.Plugins {
[Info("PocketDimensions", "Nikedemos", "1.0.5")]
[Description("It's bigger on the inside")]
public class PocketDimensions : RustPlugin {
private const string VERSION = "1.0.5";
[PluginReference]
private Plugin CopyPaste, NoEscape, ServerRewards, Economics;
public static PocketDimensions Instance;
#region CONST/STATIC
public const string PREFAB_FOUNDATION_SQUARE = "assets/prefabs/building core/foundation/foundation.prefab";
public const string PREFAB_FOUNDATION_TRIANGLE = "assets/prefabs/building core/foundation.triangle/foundation.triangle.prefab";
public const string PREFAB_FLOOR_SQUARE = "assets/prefabs/building core/floor/floor.prefab";
public const string PREFAB_WALL = "assets/prefabs/building core/wall/wall.prefab";
public const string PREFAB_WALL_DOORWAY = "assets/prefabs/building core/wall.doorway/wall.doorway.prefab";
public const string PREFAB_DOOR_METAL = "assets/prefabs/building/door.hinged/door.hinged.metal.prefab";
public const string PREFAB_TC = "assets/prefabs/deployable/tool cupboard/cupboard.tool.deployed.prefab";
public const string PREFAB_WOOD_STORAGE_BOX = "assets/prefabs/deployable/woodenbox/woodbox_deployed.prefab";
public const string PREFAB_BUNKER_PORTAL = "assets/prefabs/missions/portal/bunker_door_portal.prefab";
public const string PREFAB_MAPMARKER_GENERIC = "assets/prefabs/tools/map/genericradiusmarker.prefab";
public const string SOCKET_NAME_DOORWAY_1 = "wall.doorway/sockets/doorway-female/1";
public const string SOCKET_NAME_DOORWAY_2 = "wall.doorway/sockets/doorway-female/2";
public const string FX_CODELOCK = "assets/prefabs/locks/keypad/effects/lock.code.shock.prefab";
public const string FX_TELEPORT_APPEAR = "assets/prefabs/missions/portal/proceduraldungeon/effects/appear.prefab";
public const string FX_TELEPORT_DISAPPEAR = "assets/prefabs/missions/portal/proceduraldungeon/effects/disappear.prefab";
public const string ITEM_SHORTNAME_WOOD_STORAGE_BOX = "box.wooden";
public static ItemDefinition ItemDefinitionWoodStorageBox;
public static readonly BaseEntity.Flags FLAG_INTERNAL = BaseEntity.Flags.Reserved7;
public static readonly BaseEntity.Flags FLAG_INDESTRUCTIBLE = BaseEntity.Flags.Reserved9;
public static readonly BaseEntity.Flags FLAG_SPECIFIC = BaseEntity.Flags.OnFire;
//used to mark middle foundations and external doorway
public static readonly BasePlayer.PlayerFlags FLAG_IMMUNE_AFTER_TELEPORT = BasePlayer.PlayerFlags.Unused1;
public static readonly BasePlayer.PlayerFlags FLAG_TOAST_TIMEOUT = BasePlayer.PlayerFlags.Unused2;
public const string PREFIX_SHORT = "pd.";
public const string PREFIX_LONG = "pocketdimensions.";
public const string CMD_CHECK = PREFIX_SHORT + "check";
public const string CMD_CONVERT = PREFIX_SHORT + "convert";
public const string CMD_GIVE_BOX = PREFIX_SHORT + "givebox";
public const string CMD_LOST_FOUND = PREFIX_SHORT + "lostnfound";
public const string CMD_EMERGENCY_CLEANUP = PREFIX_SHORT + "emergencycleanup";
public const string PERM_ADMIN = PREFIX_LONG + "admin";
public const string PERM_ALL = PREFIX_LONG + "player.all";
public const string PERM_DEPLOY_PICKUP = PREFIX_LONG + "deploy.pickup";
public const string PERM_ENTER_EXIT = PREFIX_LONG + "player.enter.exit";
public const string PERM_CONVERT_TC = PREFIX_LONG + "player.convert";
public const string PERM_RESPAWN_IN_POCKET = PREFIX_LONG + "player.respawn";
public const ulong SKIN_WATERBASE_TC = 1337424002;
public static bool Unloading = false;
public const int BREAK_TOASTS_EVERY_N_CHARACTERS = 80;
public static Dictionary<string, string> ToastsCached;
public static readonly Vector3 OFFSET_TC_POS_SIZE_1 = new Vector3(0F, 0.08F, -1F);
public static readonly Vector3 OFFSET_PORTAL_POS_SIZE_1 = new Vector3(0F, 1F, 1.5F);
public static readonly Vector3 OFFSET_PORTAL_ROT_SIZE_1 = new Vector3(270F, 0F, 0F);
public const byte LAYER_VEHICLE_WORLD = 15;
public const byte LAYER_DEFAULT = 0;
public const byte LAYER_PLAYER = 17;
public const byte LAYER_DEBRIS = 26;
public const byte LAYER_DEPLOYED = 8;
public const byte LAYER_RAGDOLL = 9;
public const byte LAYER_AI = 11;
public const byte LAYER_CONSTRUCTION = 21;
public const byte LAYER_CONSTRUCTION_SOCKET = 22;
public const byte LAYER_CLUTTER = 25;
public const byte LAYER_WORLD = 16;
public const byte LAYER_PREVENT_BUILDING = 29;
public const int LAYERMASK_INTERESTING_ENTITIES = (1 << LAYER_VEHICLE_WORLD) | (1 << LAYER_DEFAULT) | (1 << LAYER_PLAYER) | (1 << LAYER_DEBRIS) | (1 << LAYER_DEPLOYED) | (1 << LAYER_RAGDOLL) | (1 << LAYER_AI) | (1 << LAYER_CONSTRUCTION) | (1 << LAYER_CONSTRUCTION_SOCKET) | (1 << LAYER_CLUTTER) | (1 << LAYER_WORLD) | (1 << LAYER_PREVENT_BUILDING);
public const uint PREFAB_ID_SMALL_WOODEN_BOX = 1560881570;
#endregion
#region HOOK SUBSCRIPTIONS
void OnServerInitialized() {
Instance = this;
lang.RegisterMessages(LangMessages, this);
permission.RegisterPermission(PERM_ADMIN, this);
permission.RegisterPermission(PERM_ALL, this);
permission.RegisterPermission(PERM_DEPLOY_PICKUP, this);
permission.RegisterPermission(PERM_ENTER_EXIT, this);
permission.RegisterPermission(PERM_CONVERT_TC, this);
permission.RegisterPermission(PERM_RESPAWN_IN_POCKET, this);
LoadConfigData();
ProcessConfigData();
AddCovalenceCommand(CMD_EMERGENCY_CLEANUP, nameof(CommandEmergencyCleanup), PERM_ADMIN);
AddCovalenceCommand(CMD_CONVERT, nameof(CommandConvert), PERM_ADMIN);
AddCovalenceCommand(CMD_CHECK, nameof(CommandViabilityCheck), PERM_ADMIN);
AddCovalenceCommand(CMD_GIVE_BOX, nameof(CommandGiveBox), PERM_ADMIN);
AddCovalenceCommand(CMD_LOST_FOUND, nameof(CommandLostFound), PERM_ADMIN);
ItemDefinitionWoodStorageBox = ItemManager.FindItemDefinition(ITEM_SHORTNAME_WOOD_STORAGE_BOX);
SpawnedPasteRequest.RequestByPosition = new Dictionary<Vector3, SpawnedPasteRequest>();
ReusableCardinalDirectionPositions = new Vector3[4];
ReusableNeighbourPosTable = new Vector3[4][] {
new Vector3[8], new Vector3[8], new Vector3[8], new Vector3[8],
}
;
ReusableFloorList = new List<BuildingBlock>();
ReusableFoundationList = new List<BuildingBlock>();
ReusableCombinedWallAndDoorwayList = new List<BuildingBlock>();
ReusableShellElementList = new List<BuildingBlock>();
ReusableExtraBaseCombatEntities = new List<BaseCombatEntity>();
ReusableVisibilityList = new ListHashSet<BaseEntity>();
ReusableColBuffer = new Collider[2048];
ReusableCopypasteArgs = new string[] {
"autoheight", "false"
}
;
ReusableMatchFoundationToCeiling = new Dictionary<BuildingBlock, BuildingBlock>(9);
ReusableLostAndFoundArray = new DimensionalPocket[1024];
ReusableGatherItemAmountArray = new GatherItemAmount[512];
ReusableLostAndFoundArrayCount = 0;
DimensionalBox.ServerCacheBox = new Dictionary<ulong, DimensionalBox>();
DimensionalPocket.ServerCachePortal = new Dictionary<ulong, DimensionalPocket>();
DimensionalPocket.ServerCacheShellBlockToPocket = new Dictionary<ulong, DimensionalPocket>();
DimensionalPocket.ServerCacheBuildingIDToPocket = new Dictionary<uint, DimensionalPocket>();
ToastsCached = new Dictionary<string, string>();
Unloading = false;
DimensionalPlayer.PrepareStatic();
var findAllPortals = BaseNetworkable.serverEntities.OfType<BasePortal>().ToArray();
BasePortal candidatePortal;
for (var e = 0; e < findAllPortals.Length; e++) {
candidatePortal = findAllPortals[e];
if (!BaseNetworkableEx.IsValid(candidatePortal)) {
continue;
}
if (!IsEntityMarkedInternal(candidatePortal)) {
continue;
}
candidatePortal.gameObject.AddComponent<DimensionalPocket>();
}
var findAllStorageContainers = BaseNetworkable.serverEntities.OfType<StorageContainer>().ToArray();
StorageContainer candidateContainer;
for (var e = 0; e < findAllStorageContainers.Length; e++) {
candidateContainer = findAllStorageContainers[e];
if (!BaseNetworkableEx.IsValid(candidateContainer)) {
continue;
}
if (!IsEntityMarkedInternal(candidateContainer)) {
continue;
}
candidateContainer.gameObject.AddComponent<DimensionalBox>();
}
CheckAllLocks();
CheckAllVendingMachines();
ConversionGuiManager.Initialize();
PositionCheckResult.Reset();
foreach (var player in BasePlayer.activePlayerList) {
OnPlayerConnected(player);
}
}
public void CheckAllVendingMachines() {
if (Configuration.DimensionaPocketsAllowVendingMachineBroadcast) {
return;
}
var findAllVendingMachines = BaseNetworkable.serverEntities.OfType<VendingMachine>().ToArray();
VendingMachine candidateMachine;
for (var e = 0; e < findAllVendingMachines.Length; e++) {
candidateMachine = findAllVendingMachines[e];
OnEntitySpawned(candidateMachine);
}
}
public void CheckAllLocks() {
if (Configuration.DimensionalBoxesAllowLockingWithLocks) {
return;
}
var findAllLocks = BaseNetworkable.serverEntities.OfType<BaseLock>().ToArray();
BaseLock candidateLock;
for (var e = 0; e < findAllLocks.Length; e++) {
candidateLock = findAllLocks[e];
OnEntitySpawned(candidateLock);
}
}
void Unload() {
Unloading = true;
foreach (var player in BasePlayer.activePlayerList) {
ConversionGuiManager.PlayerHideAllUI(player);
}
ConversionGuiManager.Cleanup();
InternalBehaviour.DetachEverythingInternal();
DimensionalBox.ServerCacheBox = null;
DimensionalPocket.ServerCachePortal = null;
DimensionalPocket.ServerCacheShellBlockToPocket = null;
DimensionalPocket.ServerCacheBuildingIDToPocket = null;
ToastsCached = null;
ReusableNeighbourPosTable = null;
ReusableCardinalDirectionPositions = null;
ReusableFloorList = null;
ReusableFoundationList = null;
ReusableCombinedWallAndDoorwayList = null;
ReusableVisibilityList = null;
ReusableColBuffer = null;
ReusableCopypasteArgs = null;
ReusableMatchFoundationToCeiling = null;
ReusableLostAndFoundArray = null;
ReusableGatherItemAmountArray = null;
ReusableShellElementList = null;
ReusableExtraBaseCombatEntities = null;
ItemDefinitionWoodStorageBox = null;
SpawnedPasteRequest.RequestByPosition = null;
DimensionalPlayer.UnloadStatic();
PositionCheckResult.Reset();
Instance = null;
Unloading = false;
}
object OnPlayerCommand(BasePlayer player, string command, string[] args) {
if (Instance == null) {
return null;
}
if (!PositionWithinDimensionLayer(player.transform.position)) {
return null;
}
if (!Configuration.ForbiddenCommandsCheckingEnabled) {
return null;
}
if (Configuration.ForbiddenCommandsInsideDimensions.IsNullOrEmpty()) {
return null;
}
if (!Configuration.ForbiddenCommandsInsideDimensions.Contains(command)) {
return null;
}
if (HasAdminPermission(player)) {
return null;
}
ToastPlayer(player, MSG(MSG_COMMAND_FORBIDDEN_INSIDE, player.UserIDString, command));
return true;
}
//RemoverTool fix
object canRemove(BasePlayer player, StorageContainer container) {
if (container == null) {
return null;
}
if (container.PrefabName != PREFAB_WOOD_STORAGE_BOX) {
return null;
}
if (!IsEntityMarkedInternal(container)) {
return null;
}
return true;
//return non null
}
//TruePVE fix
object CanEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo) {
if (!BaseNetworkableEx.IsValid(entity)) {
return null;
}
if (hitInfo == null) {
return null;
}
if (!hitInfo.damageTypes.Has(Rust.DamageType.Generic)) {
return null;
}
if (Mathf.Abs(hitInfo.damageTypes.Get(Rust.DamageType.Generic) - SPECIAL_DAMAGE_AMOUNT) > 0.001F) {
return null;
}
return true;
}
void OnEntitySpawned(VendingMachine machine) {
if (Instance == null) {
return;
}
if (Instance.Configuration.DimensionaPocketsAllowVendingMachineBroadcast) {
//nothing to see here
return;
}
machine.Invoke(() => {
if (Instance == null) {
return;
}
if (machine == null) {
return;
}
if (!machine.IsBroadcasting()) {
return;
}
var buildingID = machine.buildingID;
if (buildingID == 0) {
return;
}
if (!DimensionalPocket.ServerCacheBuildingIDToPocket.ContainsKey(buildingID)) {
return;
}
machine.SetFlag(BaseEntity.Flags.Reserved4, false);
machine.UpdateMapMarker();
}
, 0.02F);
}
void OnToggleVendingBroadcast(VendingMachine machine, BasePlayer player) {
OnEntitySpawned(machine);
if (Instance == null) {
return;
}
if (Instance.Configuration.DimensionaPocketsAllowVendingMachineBroadcast) {
//nothing to see here
return;
}
if (!machine.IsBroadcasting()) {
return;
}
var buildingID = machine.buildingID;
if (buildingID == 0) {
return;
}
if (!DimensionalPocket.ServerCacheBuildingIDToPocket.ContainsKey(buildingID)) {
return;
}
ToastPlayer(player, MSG(MSG_NO_VENDING_MACHINE_BROADCASTING_IN_POCKETS, player.UserIDString));
}
object OnSleepingBagValidCheck(SleepingBag sleepingBag, ulong playerID, bool ignoreTimers) {
if (Instance == null) {
return null;
}
if (sleepingBag == null) {
return null;
}
var player = BasePlayer.FindByID(playerID);
if (player == null) {
return null;
}
var buildingID = sleepingBag.buildingID;
if (buildingID == 0) {
return null;
}
if (!DimensionalPocket.ServerCacheBuildingIDToPocket.ContainsKey(buildingID)) {
return null;
}
bool hasRespawnInPocketPermission = HasRespawnInPocketPermission(player);
//if both the permission is present, AND you CAN respawn inside without the box being deployed, no need to go further
if (hasRespawnInPocketPermission && !Configuration.DimensionalPocketsRespawningInsideRequiresDeployment) {
return null;
}
//check if the player had just respawned in a non-deployed pocket Dimension
object returnObject = null;
if (!hasRespawnInPocketPermission) {
returnObject = false;
}
else {
//check if the pocket is deployed
CheckPositionResultDeep(sleepingBag.transform.position);
if (PositionCheckResult.BottomPocketFound != null && !PositionCheckResult.PocketIsDeployed) {
returnObject = false;
ToastPlayer(player, MSG(MSG_CANT_RESPAWN_INSIDE_NON_DEPLOYED, player.UserIDString));
}
}
return returnObject;
}
void OnEntitySpawned(BaseLock baseLock) {
if (Instance == null) {
return;
}
if (Configuration.DimensionalBoxesAllowLockingWithLocks) {
return;
}
if (baseLock == null) {
return;
}
var lockOwnerThing = baseLock.parentEntity.Get(true) as StorageContainer;
if (lockOwnerThing == null) {
return;
}
if (lockOwnerThing.PrefabName != PREFAB_WOOD_STORAGE_BOX) {
return;
}
if (!IsEntityMarkedInternal(lockOwnerThing)) {
return;
}
DimensionalBox box;
if (!DimensionalBox.ServerCacheBox.TryGetValue(lockOwnerThing.net.ID.Value, out box)) {
return;
}
//drop the lock as an item
baseLock.Invoke(() => {
if (box == null) {
baseLock.Kill();
return;
}
if (box.ThisStorageContainer == null) {
baseLock.Kill();
return;
}
var item = ItemManager.Create(baseLock.itemType, 1, baseLock.skinID);
if (item != null) {
item.Drop(baseLock.transform.position, Vector3.zero, baseLock.transform.rotation);
}
if (baseLock.PrefabName.Contains("code")) {
Effect.server.Run(FX_CODELOCK, baseLock.transform.position, baseLock.transform.up);
}
baseLock.Kill();
}
, 0.01F);
}
object OnEntityKill(BuildingBlock buildingBlock) {
if (Instance == null) {
return null;
}
if (buildingBlock == null) {
return null;
}
if (!IsEntityMarkedInternal(buildingBlock)) {
return null;
}
DimensionalPocket pocket;
if (!DimensionalPocket.ServerCacheShellBlockToPocket.TryGetValue(buildingBlock.net.ID.Value, out pocket)) {
return null;
}
return pocket.OnShellBlockKilled(buildingBlock);
}
object CanBeTargeted(BasePlayer player, DecayEntity entity) {
if (Instance == null) {
return null;
}
if (player.HasPlayerFlag(FLAG_IMMUNE_AFTER_TELEPORT)) {
return false;
}
return null;
}
object OnItemPickup(Item item, BasePlayer player) {
if (Instance == null) {
return null;
}
if (!Instance.Configuration.DimensionalBoxesItemCheckTCAuthInsideWhenHandlingItem) {
return null;
}
if (item == null) {
return null;
}
if (player == null) {
return null;
}
if (!IsDimensionalBoxItem(item)) {
return null;
}
if (item.instanceData == null) {
return null;
}
var pocket = TryGetDimensionalPocketFromNonNullItemWithDataInt(item);
if (pocket == null) {
return null;
}
if (pocket.IsPlayerBuildingBlockedOnFirstTcInsideIfThereIsAnyButOnlyIfAtLeast1Auth(player)) {
ToastPlayer(player, MSG(MSG_CANT_HANDLE_WITHOUT_TC_AUTH2, player.UserIDString));
return true;
}
return null;
}
private void OnPlayerDeath(BasePlayer player, HitInfo info) => OnPlayerMissingCommon(player);
private void OnPlayerDisconnected(BasePlayer player, string reason) => OnPlayerMissingCommon(player);
//not actually a hook
private void OnPlayerMissingCommon(BasePlayer player) {
if (Instance == null) {
return;
}
if (IsPlayerNPC(player)) {
return;
}
DimensionalPlayer dimensionalPlayer;
if (!DimensionalPlayer.ServerCachePlayer.TryGetValue(player.userID, out dimensionalPlayer)) {
return;
}
UnityEngine.Object.DestroyImmediate(dimensionalPlayer);
}
void OnPlayerSleepEnded(BasePlayer player) => OnPlayerConnected(player);
void OnPlayerConnected(BasePlayer player) {
if (Instance == null) {
return;
}
if (!Configuration.DimensionalPocketsTeleportingInsideRequiresDeployment) {
return;
}
if (IsPlayerNPC(player)) {
return;
}
DimensionalPlayer compo;
if (DimensionalPlayer.ServerCachePlayer.ContainsKey(player.userID)) {
compo = DimensionalPlayer.ServerCachePlayer[player.userID];
}
else {
compo = player.gameObject.AddComponent<DimensionalPlayer>();
}
compo.OnPlayerMajorUpdate(player);
}
object CanMoveItem(Item item, PlayerInventory playerLoot, ItemContainerId targetContainer, int targetSlot, int amount) {
//TODO: Test this
//ALso: don't allow the player to drop this if not authed.
if (Instance == null) {
return null;
}
if (!Instance.Configuration.DimensionalBoxesItemCheckTCAuthInsideWhenHandlingItem) {
return null;
}
if (item == null) {
return null;
}
if (!IsDimensionalBoxItem(item)) {
return null;
}
if (item.instanceData == null) {
return null;
}
if (playerLoot == null) {
return null;
}
var player = playerLoot.GetComponent<BasePlayer>();
if (player == null) {
return null;
}
var pocket = TryGetDimensionalPocketFromNonNullItemWithDataInt(item);
if (pocket == null) {
return null;
}
if (pocket.IsPlayerBuildingBlockedOnFirstTcInsideIfThereIsAnyButOnlyIfAtLeast1Auth(player)) {
ToastPlayer(player, MSG(MSG_CANT_HANDLE_WITHOUT_TC_AUTH2, player.UserIDString));
return true;
}
return null;
}
object CanCombineDroppedItem(DroppedItem droppedItem, DroppedItem droppedTargetItem) {
if (Instance == null) {
return null;
}
if (droppedItem == null) {
return null;
}
if (droppedTargetItem == null) {
return null;
}
if (droppedItem.item == null) {
return null;
}
if (droppedTargetItem.item == null) {
return null;
}
if (!(IsDimensionalBoxItem(droppedItem.item) || IsDimensionalBoxItem(droppedItem.item))) {
return null;
}
return false;
}
void OnItemRemove(Item item) {
if (Instance == null) {
return;
}
if (item == null) {
return;
}
if (!IsDimensionalBoxItem(item)) {
return;
}
if (item.instanceData == null) {
return;
}
var pocket = TryGetDimensionalPocketFromNonNullItemWithDataInt(item);
if (pocket == null) {
return;
}
MarkEntityAsSpecific(pocket.ThisPortal);
}
object OnPortalUse(BasePlayer player, BasePortal portal) {
if (Instance == null) {
return null;
}
if (player == null) {
return null;
}
if (!IsEntityMarkedInternal(portal)) {
return null;
}
DimensionalPocket pocket;
if (!DimensionalPocket.ServerCachePortal.TryGetValue(portal.net.ID.Value, out pocket)) {
return null;
}
if (!HasEnterExitPermission(player)) {
ToastPlayer(player, MSG(MSG_NO_PERMISSION_ENTER_EXIT, player.UserIDString));
return true;
}
if (Instance.Configuration.CheckEscapeBlockWhenExitingDimensions) {
if (IsEscapeBlocked(player)) {
ToastPlayer(player, MSG(MSG_ESCAPE_BLOCKED_CANT_EXIT, player.UserIDString));
return true;
}
}
pocket.PlayerUsePortal(player);
//return non null
return true;
}
object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info) {
if (Instance == null) {
return null;
}
if (info == null) {
return null;
}
if (!IsEntityMarkedInternal(entity)) {
return null;
}
//always let decay damage through
if (info.damageTypes.GetMajorityDamageType() == Rust.DamageType.Decay) {
return null;
}
if (!Configuration.DimensionalPocketsExteriorBlocksAllowNonDecayDamage) {
if (DimensionalPocket.ServerCacheShellBlockToPocket.ContainsKey(entity.net.ID.Value)) {
return true;
}
}
if (!IsEntityMarkedIndestructible(entity)) {
return null;
}
//is this a box?...
if (DimensionalBox.ServerCacheBox.ContainsKey(entity.net.ID.Value)) {
if (!Configuration.DimensionalBoxesAllowDamage) {
return true;
}
else {
//adjust the damage
info.damageTypes.ScaleAll(1F / Mathf.Max(0.001F, Configuration.DimensionaBoxesDamageResistanceDivisor));
return null;
}
}
return null;
}
void OnLootEntity(BasePlayer player, BuildingPrivlidge tc) {
if (Instance == null) {
return;
}
if (player == null) {
return;
}
if (!HasConvertTCPermission(player)) {
return;
}
if (tc.buildingID == 0) {
return;
}
if (IsTCWaterBaseTC(tc)) {
return;
}
//is it already converted?
if (DimensionalPocket.ServerCacheBuildingIDToPocket.ContainsKey(tc.buildingID)) {
return;
}
ConversionGuiManager.PlayerOpenTC(player, tc);
}
void OnLootEntityEnd(BasePlayer player, BuildingPrivlidge tc) {
if (Instance == null) {
return;
}
if (player == null) {
return;
}
if (IsTCWaterBaseTC(tc)) {
return;
}
ConversionGuiManager.PlayerHideAllUI(player);
}
object CanLootEntity(BasePlayer player, StorageContainer container) {
if (Instance == null) {
return null;
}
if (player == null) {
return null;
}
if (container == null) {
return null;
}
if (container.PrefabName != PREFAB_WOOD_STORAGE_BOX) {
return null;
}
if (!IsEntityMarkedInternal(container)) {
return null;
}
DimensionalBox box;
if (!DimensionalBox.ServerCacheBox.TryGetValue(container.net.ID.Value, out box)) {
return null;
}
if (!HasEnterExitPermission(player)) {
ToastPlayer(player, MSG(MSG_NO_PERMISSION_ENTER_EXIT, player.UserIDString));
return true;
}
if (Instance.Configuration.CheckEscapeBlockWhenEnteringDimensions) {
if (IsEscapeBlocked(player)) {
ToastPlayer(player, MSG(MSG_CANT_ENTER_ESCAPE_BLOCKED, player.UserIDString));
return true;
}
}
if (Instance.Configuration.DimensionalBoxesCheckBuildingBlockOutsideWhenEnteringDimension) {
if (player.IsBuildingBlocked()) {
ToastPlayer(player, MSG(MSG_CANT_ENTER_BUILDING_BLOCKED, player.UserIDString));
return true;
}
}
if (Instance.Configuration.DimensionalBoxesCheckTCAuthInsideWhenEnteringDimension) {
if (box.PairedPocket.IsPlayerBuildingBlockedOnFirstTcInsideIfThereIsAnyButOnlyIfAtLeast1Auth(player)) {
ToastPlayer(player, MSG(MSG_CANT_ENTER_WITHOUT_TC_AUTH2, player.UserIDString));
return true;
}
}
if (container.IsLocked()) {
player.ShowToast(GameTip.Styles.Red_Normal, StorageContainer.LockedMessage);
return true;
}
;
if (!container.CanOpenLootPanel(player, container.panelName)) {
return true;
}
;
box.PlayerOpenedBox(player);
//return non null
return true;
}
object CanBuild(Planner planner, Construction prefab, Construction.Target target) {
//TODO: Don't allow to deploy the dimension in itself!
//ALSO: Don't allow to deploy the dimension if it already has a paired box - destroy this item to prevent this in the future.
//
if (Instance == null) {
return null;
}
if (prefab.prefabID != PREFAB_ID_SMALL_WOODEN_BOX) {
return null;
}
var player = planner.GetOwnerPlayer();
if (player == null) {
return null;
}
//ok, it's a small box. have you verified the item or what...?
var item = planner.GetItem();
//looking for existing and fresh
bool isExisting = IsDimensionalBoxItem(item);
bool isFresh = IsDimensionalBoxItem(item, true);
if (!(isExisting || isFresh)) {
return null;
}
//now player!
if (!HasDeployPickupPermission(player)) {
ToastPlayer(player, MSG(MSG_NO_PERM_DEPLOY_PICKUP, player.UserIDString));
return true;
}
if (Instance.Configuration.CheckEscapeBlockWhenDeployingDimensions) {
if (IsEscapeBlocked(player)) {
ToastPlayer(player, MSG(MSG_ESCAPE_BLOCKED_CANT_DEPLOY, player.UserIDString));
return true;
}
}
var targetPosition = target.entity && target.entity.transform && target.socket ? target.GetWorldPosition() : target.position;
DimensionalPocket itemDimension = null;
bool itemDimensionNotNull = false;
if (isExisting) {
if (item.instanceData != null) {
if (item.instanceData.dataInt != 0) {
var pocket = TryGetDimensionalPocketFromNonNullItemWithDataInt(item);
if (pocket != null) {
itemDimension = pocket;
itemDimensionNotNull = true;
}
}
}
}
CheckPositionResultDeep(targetPosition, itemDimension);
//now check for any problems.
//first, cannot deploy in itself.
if (PositionCheckResult.BottomPocketFound != null) {
if (Instance.Configuration.DimensionalPocketsMaxInceptionDepth == 0) {
ToastPlayer(player, MSG(MSG_CANT_DEPLOY_DIMENSIONS_INSIDE_DIMENSIONS, player.UserIDString));
return true;
}
//ok, we're trying to deploy a dimension inside a dimension.
if (itemDimensionNotNull) {
//are you trying to deploy in yourself?!
//check if you short circuited
if (PositionCheckResult.ShortCircuitDetected) {
ToastPlayer(player, MSG(MSG_CANT_DEPLOY_SHORT_CIRCUIT, player.UserIDString));
return true;
}
if (itemDimension == PositionCheckResult.BottomPocketFound) {
ToastPlayer(player, MSG(MSG_CANT_DEPLOY_IN_ITSELF, player.UserIDString));
return true;
//return non null
}
//what about deploying in undeployed dimensions?!
//should that be a config value?
}
//for depth, new dimensions should be checked too!
if (PositionCheckResult.NoMoreDepthAllowed) {
ToastPlayer(player, MSG(MSG_CANT_DEPLOY_TOO_DEEP, player.UserIDString, Instance.Configuration.DimensionalPocketsMaxInceptionDepth));
return true;
}
}
return null;
}
void OnEntityBuilt(Planner planner, GameObject gameObject) {
if (Instance == null) {
return;
}
var maybeDimensionalBoxItem = planner.GetItem();
if (maybeDimensionalBoxItem == null) {
return;
}
var maybeStorage = gameObject.GetComponent<StorageContainer>();
if (maybeStorage == null) {
return;
}
//here, only non-fresh matter
if (!IsDimensionalBoxItem(maybeDimensionalBoxItem)) {
return;
}
MarkEntityAsIndestructible(maybeStorage);
MarkEntityAsInternal(maybeStorage);
var newDimensionalBox = maybeStorage.gameObject.AddComponent<DimensionalBox>();
if (!newDimensionalBox.TryBuildFromItem(maybeDimensionalBoxItem)) {
var player = planner.GetOwnerPlayer();
if (player == null) {
return;
}
ToastPlayer(player, MSG(MSG_DIMENSION_DOESNT_EXIST, player.UserIDString));
maybeStorage.Invoke(() => maybeStorage.Kill(BaseNetworkable.DestroyMode.Gib), 0.01F);
}
}
object CanPickupEntity(BasePlayer player, StorageContainer entity) {
if (Instance == null) {
return null;
}
if (player == null) {
return null;
}
if (!IsEntityMarkedInternal(entity)) {
return null;
}
DimensionalBox box;
if (!DimensionalBox.ServerCacheBox.TryGetValue(entity.net.ID.Value, out box)) {
return null;
}
if (!HasDeployPickupPermission(player)) {
ToastPlayer(player, MSG(MSG_NO_PERM_DEPLOY_PICKUP, player.UserIDString));
return false;
}
if (Instance.Configuration.CheckEscapeBlockWhenPickingUpDimensions) {
if (IsEscapeBlocked(player)) {
ToastPlayer(player, MSG(MSG_ESCAPE_BLOCKED_CANT_PICKUP, player.UserIDString));
return false;
}
}
var res = box.CanPickupBox(player);
return res;
}
void OnEntityPickedUp(StorageContainer container, Item createdItem, BasePlayer player) {
if (Instance == null) {
return;
}
if (container.PrefabName != PREFAB_WOOD_STORAGE_BOX) {
return;
}
if (!IsEntityMarkedInternal(container)) {
return;
}
DimensionalBox box;
if (!DimensionalBox.ServerCacheBox.TryGetValue(container.net.ID.Value, out box)) {
return;
}
box.OnPickedUp(createdItem, player);
}
#endregion
#region DIMENSIONAL PLAYER
public class DimensionalPlayer : InternalBehaviour {
public static Hash<ulong, DimensionalPlayer> ServerCachePlayer;
public BasePlayer Player;
public ulong SteamID;
public Vector3 LastCheckedPosition;
public bool WasFlyingLastTimeChecked;
public static void PrepareStatic() {
ServerCachePlayer = new Hash<ulong, DimensionalPlayer>();
}
public static void UnloadStatic() {
ServerCachePlayer = null;
}
public void OnPlayerMajorUpdate(BasePlayer player) {
Player = player;
SteamID = Player.userID;
LastCheckedPosition = Player.transform.position;
WasFlyingLastTimeChecked = Player.IsFlying;
if (!ServerCachePlayer.ContainsKey(SteamID)) {
ServerCachePlayer.Add(SteamID, this);
}
PositionChangeUpdate(true);
if (!Player.IsInvoking(PositionChangeUpdate)) {
Player.InvokeRandomized(PositionChangeUpdate, 0.5F, 0.5F, 0.1F);
}
}
public void PositionChangeUpdate() => PositionChangeUpdate(false);
public void PositionChangeUpdate(bool whenPreparing = false) {
if (Player.HasPlayerFlag(BasePlayer.PlayerFlags.ReceivingSnapshot)) {
return;
}
//when preparing, force an update anyway
if (!whenPreparing) {
if (Player.transform.position == LastCheckedPosition) {
return;
}
if (WasFlyingLastTimeChecked || Player.IsFlying) {
LastCheckedPosition = Player.transform.position;
WasFlyingLastTimeChecked = Player.IsFlying;
return;
}
}
//position changed to something different.
//check if the new position is inside the range for pocket dimensions.
//bool wasInDimensionLayerBefore = PositionWithinDimensionLayer(LastCheckedPosition);
bool isInDimensionLayerNow = PositionWithinDimensionLayer(Player.transform.position);
if (isInDimensionLayerNow) {
bool needTeleportBack = false;
string reasonForTeleportingBack = default(string);
//yes, this is in the range.
//check how much it changed.
var distanceTraversed = Vector3.Distance(LastCheckedPosition, Player.transform.position);
if (distanceTraversed > 40F) {
//that looks like a teleport to a dimension above.
//do a raycast down, 10 meters max.
//if you encounter absolutely nothing, you need to be teleported back.
//by "something" we mean a decay entity.
CheckPositionResultDeep(Player.transform.position);
if (PositionCheckResult.NothingAround) {
//absolutely nothing found. We need to teleport the player back.
needTeleportBack = true;
reasonForTeleportingBack = MSG(MSG_DIMENSION_DOESNT_EXIST, Player.UserIDString);
}
else {
if (PositionCheckResult.BottomPocketFound != null) {
if (!PositionCheckResult.PocketIsDeployed) {
needTeleportBack = true;
reasonForTeleportingBack = MSG(MSG_CANT_TELEPORT_INSIDE_NON_DEPLOYED, Player.UserIDString);
}
}
}
}
if (needTeleportBack) {
TeleportPlayerTo(Player, LastCheckedPosition);
ToastPlayer(Player, reasonForTeleportingBack);
}
}
LastCheckedPosition = Player.transform.position;
WasFlyingLastTimeChecked = Player.IsFlying;
}
void OnDestroy() {
if (Player != null) {
Player.CancelInvoke(PositionChangeUpdate);
}
ServerCachePlayer.Remove(SteamID);
}
}
#endregion
#region LANG
public const string MSG_POCKET_DIMENSION_ITEM_NAME = nameof(MSG_POCKET_DIMENSION_ITEM_NAME);
public const string MSG_CONVERT_COST_CURRENCY_NAME_SERVER_REWARDS = nameof(MSG_CONVERT_COST_CURRENCY_NAME_SERVER_REWARDS);
public const string MSG_CONVERT_COST_CURRENCY_NAME_ECONOMICS = nameof(MSG_CONVERT_COST_CURRENCY_NAME_ECONOMICS);
public const string MSG_CONVERT_COST_LABEL_TEXT_AND_FORMAT = nameof(MSG_CONVERT_COST_LABEL_TEXT_AND_FORMAT);
public const string MSG_CONVERT_BUTTON_CONVERT = nameof(MSG_CONVERT_BUTTON_CONVERT);
public const string MSG_CONVERT_TEST_FAILED = nameof(MSG_CONVERT_TEST_FAILED);
public const string MSG_CONVERTED_SUCCESSFULLY = nameof(MSG_CONVERTED_SUCCESSFULLY);
public const string MSG_CONVERT_COST_CANNOT_AFFORD = nameof(MSG_CONVERT_COST_CANNOT_AFFORD);
public const string MSG_CONVERT_COST_FORMAT_SERVER_REWARDS = nameof(MSG_CONVERT_COST_FORMAT_SERVER_REWARDS);
public const string MSG_CONVERT_COST_FORMAT_ECONOMICS = nameof(MSG_CONVERT_COST_FORMAT_ECONOMICS);
public const string MSG_CONVERT_COST_FORMAT_CUSTOM_ITEM_AMOUNT = nameof(MSG_CONVERT_COST_FORMAT_CUSTOM_ITEM_AMOUNT);
public const string MSG_CONVERT_COST_MISSING_PLUGIN = nameof(MSG_CONVERT_COST_MISSING_PLUGIN);
public const string MSG_COMMAND_FORBIDDEN_INSIDE = nameof(MSG_COMMAND_FORBIDDEN_INSIDE);
public const string MSG_NO_PERMISSION_ENTER_EXIT = nameof(MSG_NO_PERMISSION_ENTER_EXIT);
public const string MSG_NO_PERM_DEPLOY_PICKUP = nameof(MSG_NO_PERM_DEPLOY_PICKUP);
public const string MSG_NO_CONVERT_PERMISSION = nameof(MSG_NO_CONVERT_PERMISSION);
public const string MSG_DIMENSION_DOESNT_EXIST = nameof(MSG_DIMENSION_DOESNT_EXIST);
public const string MSG_CANT_RESPAWN_INSIDE_NON_DEPLOYED = nameof(MSG_CANT_RESPAWN_INSIDE_NON_DEPLOYED);
public const string MSG_CANT_TELEPORT_INSIDE_NON_DEPLOYED = nameof(MSG_CANT_TELEPORT_INSIDE_NON_DEPLOYED);
public const string MSG_CANT_DEPLOY_IN_ITSELF = nameof(MSG_CANT_DEPLOY_IN_ITSELF);
public const string MSG_CANT_DEPLOY_SHORT_CIRCUIT = nameof(MSG_CANT_DEPLOY_SHORT_CIRCUIT);
public const string MSG_CANT_DEPLOY_DIMENSIONS_INSIDE_DIMENSIONS = nameof(MSG_CANT_DEPLOY_DIMENSIONS_INSIDE_DIMENSIONS);
public const string MSG_CANT_DEPLOY_TOO_DEEP = nameof(MSG_CANT_DEPLOY_TOO_DEEP);
public const string MSG_CANT_PICK_UP_WITH_PLAYERS_INSIDE = nameof(MSG_CANT_PICK_UP_WITH_PLAYERS_INSIDE);
public const string MSG_CANT_PICK_UP_WITHOUT_TC_AUTH2 = nameof(MSG_CANT_PICK_UP_WITHOUT_TC_AUTH2);
public const string MSG_CANT_HANDLE_WITHOUT_TC_AUTH2 = nameof(MSG_CANT_HANDLE_WITHOUT_TC_AUTH2);
public const string MSG_CANT_GET_OUT_STUCK = nameof(MSG_CANT_GET_OUT_STUCK);
public const string MSG_CANT_ENTER_ESCAPE_BLOCKED = nameof(MSG_CANT_ENTER_ESCAPE_BLOCKED);
public const string MSG_CANT_ENTER_BUILDING_BLOCKED = nameof(MSG_CANT_ENTER_BUILDING_BLOCKED);
public const string MSG_CANT_ENTER_WITHOUT_TC_AUTH2 = nameof(MSG_CANT_ENTER_WITHOUT_TC_AUTH2);
public const string MSG_ESCAPE_BLOCKED_CANT_EXIT = nameof(MSG_ESCAPE_BLOCKED_CANT_EXIT);
public const string MSG_ESCAPE_BLOCKED_CANT_DEPLOY = nameof(MSG_ESCAPE_BLOCKED_CANT_DEPLOY);
public const string MSG_ESCAPE_BLOCKED_CANT_PICKUP = nameof(MSG_ESCAPE_BLOCKED_CANT_PICKUP);
public const string MSG_VIABILITY_OK = nameof(MSG_VIABILITY_OK);
public const string MSG_VIABILITY_TOO_MANY_TCS = nameof(MSG_VIABILITY_TOO_MANY_TCS);
public const string MSG_VIABILITY_NO_BUILDING_BLOCKS = nameof(MSG_VIABILITY_NO_BUILDING_BLOCKS);
public const string MSG_VIABILITY_ONLY_SQUARE_FOUNDATIONS = nameof(MSG_VIABILITY_ONLY_SQUARE_FOUNDATIONS);
public const string MSG_VIABILITY_MUST_HAVE_9_FOUNDATIONS = nameof(MSG_VIABILITY_MUST_HAVE_9_FOUNDATIONS);
public const string MSG_VIABILITY_MUST_HAVE_AT_LEAST_9_FLOORS = nameof(MSG_VIABILITY_MUST_HAVE_AT_LEAST_9_FLOORS);
public const string MSG_VIABILITY_MUST_HAVE_1_DOORWAY = nameof(MSG_VIABILITY_MUST_HAVE_1_DOORWAY);
public const string MSG_VIABILITY_MUST_HAVE_AT_LEAST_35_WA
// ... [Code preview truncated due to length. Full source in attached file] ...