C#:
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Facepunch;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Rust;
using UnityEngine;
using Random = UnityEngine.Random;
namespace Oxide.Plugins
{
[Info("Dungeon Events", "Marte6", "3.7.0")]
[Description("Create dungeons populated with NPCs, turrets and loot containers.")]
public class DungeonEvents : RustPlugin
{
#region Fields
[PluginReference]
private Plugin NpcSpawn,
Friends,
Economics,
ServerRewards,
Notify,
NightVision,
ZoneManager,
Duelist,
RaidableBases,
AbandonedBases,
RestoreUponDeath,
SkillTree;
private static DungeonEvents _instance;
private Configuration _config;
private List<ActiveDungeon> _activeDungeons = new List<ActiveDungeon>();
private List<BasePlayer> _playersBuying = new List<BasePlayer>();
private List<BasePlayer> _wasInDungeonPlayers = new List<BasePlayer>();
private Dictionary<ulong, Dictionary<string, DateTime>> _buyCooldowns = new Dictionary<ulong, Dictionary<string, DateTime>>();
private List<DungeonMarker> _activeDungeonMarkers = new List<DungeonMarker>();
private Queue<DungeonRequest> _dungeonQueue = new Queue<DungeonRequest>();
private Queue<DungeonRequest> _dungeonFailedAutoSpawnQueue = new Queue<DungeonRequest>();
private Queue<DungeonRequest> _dungeonFailedBuyQueue = new Queue<DungeonRequest>();
private Timer _dungeonTimer;
private bool isCreatingDungeon = false;
private const float TimeTimers = 10f;
private const string PermissionAdmin = "dungeonevents.admin";
private const string PermissionBuy = "dungeonevents.buy";
private const string PermissionEnter = "dungeonevents.enter";
private const ulong OwnerID = 10203040505;
private const string UiEventPanel = "yFifW5s1zAY09h5cU0qeIQUNV6GGwevG";
private const string UiMainPanel = "5cU06GGweeIQ09hyFifW5qvGVsUNAY1z";
private const string UiCommandClose = "lDM82GJH3biLcI95ic8zzteteOeQx77h";
private Dictionary<DungeonTierConfig, TierInfo> _tierInfoMap = new Dictionary<DungeonTierConfig, TierInfo>();
private DateTime _lastAutoSpawnTime = DateTime.MinValue;
private WaitForSeconds SmallDelay = new WaitForSeconds(0.15f);
private WaitForSeconds BigDelay = new WaitForSeconds(0.50f);
private Coroutine _startupRoutine;
private const string WallPrefab = "assets/prefabs/building core/wall/wall.prefab";
private const string FoundationPrefab = "assets/prefabs/building core/foundation/foundation.prefab";
private const string CeilingPrefab = "assets/prefabs/building core/floor/floor.prefab";
private const string TurretPrefab = "assets/prefabs/npc/autoturret/autoturret_deployed.prefab";
private const string LightPrefab = "assets/bundled/prefabs/modding/cinematic/cinelights/cinelight_point_red.prefab";
private const string PortalPrefab = "assets/prefabs/missions/portal/halloweenportalexit.prefab";
private const string WallFramePrefab = "assets/prefabs/building core/wall.frame/wall.frame.prefab";
private const string DoorPrefab = "assets/prefabs/building/wall.frame.garagedoor/wall.frame.garagedoor.prefab";
private readonly List<ActiveDungeon> _activeRooms = new List<ActiveDungeon>();
private readonly Dictionary<Vector3, ZoneInfo> _blockedZones = new Dictionary<Vector3, ZoneInfo>();
private readonly Vector3 _wallOffset = new Vector3(-1.5f, 0, -1.5f);
private static readonly Vector3 CeilingLightOffset = new Vector3(-1.5f, 0, -1.5f);
private static readonly List<string> RockKeywords = new List<string> { "rock", "formation", "junk", "cliff", "invisible" };
private int _gridSize = 5;
private const int SubCells = 2;
private const int MinimumActiveCells = 13;
private const int MaxGenerationAttempts = 1000;
private const float CellSpacing = 6f;
private const float SubCellSpacing = 3f;
private const float WallRotationOffset = 90f;
private const float CeilingHeight = 3f;
private const float CorridorLength = 12f;
private const bool SpawnTurrets = false;
private const bool PlaceCeiling = true;
private const bool PlaceLights = true;
private uint _randomSeed;
#endregion
#region Hooks
void Init()
{
_instance = this;
permission.RegisterPermission(PermissionEnter, this);
permission.RegisterPermission(PermissionAdmin, this);
permission.RegisterPermission(PermissionBuy, this);
}
void OnServerInitialized()
{
float smallDelaySec = Mathf.Clamp(_config.SmallDelay, 0.15f, 5f);
float bigDelaySec = Mathf.Clamp(_config.BigDelay, 0.50f, 5f);
SmallDelay = new WaitForSeconds(smallDelaySec);
BigDelay = new WaitForSeconds(bigDelaySec);
_startupRoutine = ServerMgr.Instance.StartCoroutine(StartupRoutine());
NextTick(RegisterCommands);
}
void Unload()
{
if (_startupRoutine != null)
ServerMgr.Instance.StopCoroutine(_startupRoutine);
_dungeonTimer?.Destroy();
_dungeonQueue.Clear();
DestroyAllUis();
RemoveAllDungeons();
}
object CanDropActiveItem(BasePlayer player)
{
if (RestoreUponDeath != null)
{
return null;
}
if (player == null)
return null;
var wasInDungeon = _wasInDungeonPlayers.FirstOrDefault(d => d == player);
if (wasInDungeon != null)
{
var activeItem = player.GetActiveItem();
if (activeItem != null)
return false;
}
return null;
}
object OnBackpackDrop(Item backpack, PlayerInventory inventory)
{
if (!_config.PreventDropBackpack)
return null;
var player = inventory?.containerMain?.playerOwner;
if (RestoreUponDeath != null)
return null;
if (player == null)
return null;
var wasInDungeon = _wasInDungeonPlayers.FirstOrDefault(d => d == player);
if (wasInDungeon != null)
return true;
return null;
}
object CanDropBackpack(ulong userID, Vector3 backpackPluginHook)
{
var player = BasePlayer.Find(userID.ToString());
if (player == null)
return null;
var wasInDungeon = _wasInDungeonPlayers.FirstOrDefault(d => d == player);
if (wasInDungeon != null)
return false;
return null;
}
object CanPickupEntity(BasePlayer player, BaseEntity entity)
{
if (player == null)
return null;
if (GetDungeonForPlayer(player) != null)
return false;
return null;
}
object CanDeployItem(BasePlayer player, Deployer deployer, ulong entityId)
{
if (player == null)
return null;
var dungeon = GetDungeonForPlayer(player);
if (dungeon != null)
return false;
return null;
}
object CanBuild(Planner planner, Construction prefab, Construction.Target target)
{
var player = planner?.GetOwnerPlayer();
if (player == null)
return null;
if (GetDungeonForPlayer(player) != null)
return false;
Vector3 buildPos = target.position;
if (buildPos == Vector3.zero && target.socket != null && target.entity != null)
buildPos = target.GetWorldPosition();
float r = _config.RadiusSphere;
float r2 = r * r;
foreach (var dungeon in _activeDungeons)
{
if (dungeon.PortalPosition == Vector3.zero)
continue;
float dx = buildPos.x - dungeon.PortalPosition.x;
float dz = buildPos.z - dungeon.PortalPosition.z;
if (dx * dx + dz * dz <= r2)
return false;
}
return null;
}
object OnPlayerCommand(BasePlayer player, string command, string[] args)
{
if (player == null)
return null;
if (GetDungeonForPlayer(player) == null)
return null;
if (_config.BlockCommands.Contains("/" + command.ToLower()))
{
NotifyPlayer(player, "BlockedCommands", 0);
return true;
}
return null;
}
object OnServerCommand(ConsoleSystem.Arg arg)
{
if (arg == null || arg.cmd == null)
return null;
BasePlayer player = arg.Player();
if (player != null && (GetDungeonForPlayer(player) != null))
{
if (_config.BlockCommands.Contains(arg.cmd.Name.ToLower()) || _config.BlockCommands.Contains(arg.cmd.FullName.ToLower()))
{
NotifyPlayer(player, "BlockedCommands", 0);
return true;
}
}
return null;
}
object OnCustomNpcTarget(ScientistNPC scientistNPC, BasePlayer player)
{
if (scientistNPC == null || !IsDungeonEntity(scientistNPC))
return null;
if (player == null || !player.userID.IsSteamId())
return false;
return null;
}
object CanEntityBeTargeted(BasePlayer player, AutoTurret turret)
{
if (turret == null || !IsDungeonEntity(turret))
return null;
if (player == null || !player.userID.IsSteamId())
return false;
return true;
}
object OnEntityEnter(TargetTrigger trigger, ScientistNPC scientistNPC)
{
if (trigger == null || scientistNPC == null || scientistNPC.net == null || scientistNPC.userID.IsSteamId())
return null;
AutoTurret autoTurret = trigger.GetComponentInParent<AutoTurret>();
if (autoTurret == null || autoTurret.net == null)
return null;
if (IsDungeonEntity(autoTurret))
return true;
return null;
}
object OnPortalUse(BasePlayer player, BasePortal portal)
{
var activeDungeon = GetActiveDungeonByPortal(portal);
if (activeDungeon == null)
return null;
if (!activeDungeon.Spawned)
{
NotifyPlayer(player, "DungeonStillSpawningMessage", 0);
return false;
}
if (_config.LockDungeonToPlayer && !CanEnter(player, activeDungeon))
{
NotifyPlayer(player, "NoAccess", 0);
return false;
}
return null;
}
void OnCorpsePopulate(ScientistNPC npc, NPCPlayerCorpse corpse)
{
if (npc == null || corpse == null)
return;
if (!IsDungeonEntity(npc))
return;
var activeDungeon = GetActiveDungeonByEntity(npc);
if (activeDungeon == null)
return;
bool isBoss = npc.GetComponent<BossMonoBehaviour>() != null;
var corpseData = corpse.gameObject.AddComponent<CorpseLootData>();
corpseData.IsBoss = isBoss;
corpseData.LootConfig = isBoss ? activeDungeon.TierConfig?.BossNpcConfig : activeDungeon.TierConfig?.NpcConfig;
NextTick(() =>
{
if (corpse == null || corpse.IsDestroyed)
return;
var storedData = corpse.GetComponent<CorpseLootData>();
if (storedData == null)
return;
if (storedData.LootConfig == null)
return;
Effect.server.Run("assets/prefabs/weapons/flashbang/effects/fx-flashbang-boom.prefab", corpse.transform.position);
if (corpse.containers == null || corpse.containers.Length == 0 || corpse.containers[0] == null)
return;
var container = corpse.containers[0];
container.Clear();
var items = GetNpcLootItems(storedData.LootConfig);
if (items == null || items.Count == 0)
return;
int maxDifferentItems = storedData.IsBoss ? (activeDungeon?.TierConfig?.MaxItemsPerBossNpc ?? int.MaxValue) : (activeDungeon?.TierConfig?.MaxItemsPerNpc ?? int.MaxValue);
var lootItems = items.OrderBy(_ => Random.value).ToList();
int differentItemsCount = 0;
foreach (var itemCfg in lootItems)
{
if (differentItemsCount >= maxDifferentItems)
break;
if (Random.value * 100f > itemCfg.InclusionChancePercentage)
continue;
var def = ItemManager.FindItemDefinition(itemCfg.ShortName);
if (def == null)
continue;
int min = itemCfg.MinimumAmount;
int max = itemCfg.MaximumAmount;
if (min <= 0 || max <= 0)
continue;
if (max < min)
{
int t = min;
min = max;
max = t;
}
int amount = Random.Range(min, max + 1);
if (amount <= 0)
continue;
if (container.itemList.Count >= container.capacity)
break;
var item = ItemManager.Create(def, amount, itemCfg.SkinID);
if (item == null)
continue;
if (!string.IsNullOrEmpty(itemCfg.CustomName))
item.name = itemCfg.CustomName;
item.MoveToContainer(container);
differentItemsCount++;
}
});
}
void OnPlayerDeath(BasePlayer player, HitInfo info)
{
if (player == null || !player.userID.IsSteamId())
return;
ActiveDungeon dungeon;
try
{
dungeon = GetDungeonForPlayer(player);
}
catch (Exception)
{
return;
}
if (dungeon == null)
return;
var basePortal = dungeon.BasePortal;
var insidePortal = dungeon.InsidePortal;
if (basePortal == null || insidePortal == null)
return;
if (basePortal.transform == null || insidePortal.transform == null)
return;
try
{
TeleportPlayerToOutsidePortal(player, dungeon);
}
catch (Exception) { }
if (dungeon.Entities == null)
return;
foreach (var door in dungeon.Entities.OfType<Door>())
{
if (door == null || door.IsDestroyed || door.transform == null)
continue;
try
{
door.SetOpen(false);
door.SendNetworkUpdate();
}
catch (Exception) { }
}
}
object CanRaidBlock(BasePlayer player)
{
var dungeon = GetDungeonForPlayer(player);
if (dungeon != null)
return false;
return null;
}
void OnEntityDeath(BaseCombatEntity entity, HitInfo info)
{
if (entity == null || info?.InitiatorPlayer == null)
return;
ActiveDungeon dungeon = GetActiveDungeonByEntity(entity);
if (dungeon == null || !dungeon.Spawned)
return;
bool isNpc = entity is ScientistNPC;
bool isTurret = entity is AutoTurret;
bool isBox = entity is StorageContainer;
if (!isNpc && !isTurret && !isBox)
return;
RewardConfig r = dungeon.TierConfig?.RewardConfig;
if (r == null)
return;
int econ =
isNpc ? r.NpcEconomicsReward
: isTurret ? r.TurretEconomicsReward
: r.BoxEconomicsReward;
int sr =
isNpc ? r.NpcServerRewards
: isTurret ? r.TurretServerRewards
: r.BoxServerRewards;
BasePlayer attacker = info.InitiatorPlayer;
if (econ > 0 && Economics != null)
{
Economics.Call("Deposit", attacker.userID, (double)econ);
attacker.ChatMessage(Msg(attacker.UserIDString, "EconRewardMessage", econ));
}
if (sr > 0 && ServerRewards != null)
{
ServerRewards.Call("AddPoints", attacker.userID, sr);
attacker.ChatMessage(Msg(attacker.UserIDString, "ServerRewardMessage", sr));
}
}
object OnEntityTakeDamage(BuildingBlock block, HitInfo info)
{
if (!IsDungeonEntity(block))
return null;
info.damageTypes?.ScaleAll(0f);
return true;
}
object OnEntityTakeDamage(ScientistNPC scientistNPC, HitInfo info)
{
if (scientistNPC == null || info == null || info.Initiator == null)
return null;
if (info.Initiator is AutoTurret turret && IsDungeonEntity(turret))
{
info.damageTypes?.ScaleAll(0f);
return null;
}
if (!IsDungeonEntity(scientistNPC))
return null;
var monitor = scientistNPC.GetComponent<NpcDamageMonitor>() ?? scientistNPC.gameObject.AddComponent<NpcDamageMonitor>();
if (Time.time - monitor.LastHitTime > 2f)
monitor.Hits = 0;
monitor.Hits++;
monitor.LastHitTime = Time.time;
if (monitor.Hits >= 5)
{
monitor.Hits = 0;
AttemptNpcReposition(scientistNPC);
}
return null;
}
object CanEntityTakeDamage(BuildingBlock block, HitInfo info)
{
if (!IsDungeonEntity(block))
return null;
info.damageTypes?.ScaleAll(0f);
return false;
}
object CanEntityTakeDamage(BasePlayer victim, HitInfo info)
{
if (info?.Initiator == null || info.Initiator.net == null || !IsPlayer(victim))
return null;
if (info.Initiator is ScientistNPC && IsDungeonEntity(info.Initiator))
return true;
if (info.Initiator is AutoTurret && IsDungeonEntity(info.Initiator))
return true;
return null;
}
object CanEntityTakeDamage(AutoTurret autoTurret, HitInfo hitinfo)
{
if (autoTurret == null || hitinfo == null)
return null;
if (!IsDungeonEntity(autoTurret))
return null;
if (hitinfo.InitiatorPlayer == null || !IsPlayer(hitinfo.Initiator as BasePlayer))
return false;
return true;
}
object CanEntityTakeDamage(StorageContainer storageContainer, HitInfo hitinfo)
{
if (storageContainer == null || hitinfo == null)
return null;
if (!IsDungeonEntity(storageContainer))
return null;
if (hitinfo.InitiatorPlayer == null || !IsPlayer(hitinfo.Initiator as BasePlayer))
return false;
return true;
}
object CanEntityTakeDamage(Door victim, HitInfo hitinfo)
{
if (victim == null || hitinfo == null)
return null;
if (!IsDungeonEntity(victim))
return null;
if (hitinfo.InitiatorPlayer == null || !IsPlayer(hitinfo.Initiator as BasePlayer))
return false;
return true;
}
#endregion
#region Data Initialization
protected override void LoadConfig()
{
base.LoadConfig();
try
{
JObject raw;
var threshold = new VersionNumber(3, 6, 0);
VersionNumber parsedVersion = new VersionNumber(0, 0, 0);
var hasVersion = TryReadRawConfig(out raw) && TryParseConfigVersion(raw, out parsedVersion);
if (!hasVersion || parsedVersion < threshold)
{
PrintWarning($"Legacy config detected {(hasVersion ? parsedVersion.ToString() : "unknown")} updating to {threshold}+.");
LoadDefaultConfig();
_config.Version = Version;
EnsureLootDataFiles();
InitializeTierInfoMap();
SaveConfig();
return;
}
var mutated = MigrateTiersObjectToArray(raw);
_config = raw.ToObject<Configuration>() ?? new Configuration();
var changed = false;
if (_config.Version == null || _config.Version < Version)
{
_config.Version = Version;
changed = true;
}
EnsureLootDataFiles();
InitializeTierInfoMap();
if (mutated || changed)
SaveConfig();
}
catch (Exception ex)
{
PrintError($"Error reading config: {ex.Message}");
LoadDefaultConfig();
_config.Version = Version;
EnsureLootDataFiles();
InitializeTierInfoMap();
SaveConfig();
}
}
protected override void LoadDefaultConfig()
{
PrintWarning("Creating a new configuration file");
_config = new Configuration { Version = Version };
}
protected override void SaveConfig()
{
Config.WriteObject(_config, true);
}
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(
new Dictionary<string, string>
{
["AlreadyOwnsDungeonMessage"] = "You already own an active dungeon. You cannot purchase another one until it expires or is removed.",
["DungeonAppeared"] = "A {0} has appeared at grid {1}!",
["DungeonRemovedDetail"] = "Removed dungeon of tier {0} at grid {1}.",
["DungeonSpawnedSuccess"] = "Dungeon spawned successfully at grid {0}!",
["NpcSpawnNotLoaded"] = "NpcSpawn plugin is not loaded.",
["DungeonStillSpawningMessage"] = "The dungeon is still spawning. Please wait until it's ready before entering.",
["InsufficientFundsMessage"] = "You don't have enough funds to buy the {0} tier. Required: {1}.",
["InvalidTierMessage"] = "Invalid tier name '{0}'. Available tiers: {1}.",
["ThisTierDisabled"] = "This Tier is disabled for purchases",
["MaxActiveDungeonsReached"] = "Cannot spawn: Maximum number of active dungeons reached.",
["NoAccess"] = "You don't have permission to do this.",
["PurchaseSuccessMessage"] = "You have successfully purchased the {0} tier!",
["ReceivedRequest"] = "Your request has been added to the queue. Please wait.",
["UsageCreateMessage"] = "Usage: /createdungeon <tier_name>",
["CleanedUp"] = "All entities related to this plugin have been cleaned up.",
["NoInactiveDungeonsToRemove"] = "There are no inactive dungeons to remove.",
["WithdrawFailedMessage"] = "Failed to withdraw funds for {0}.",
["DungeonBuyTitle"] = "Buy Dungeon",
["BuyButton"] = "{0} ({1})",
["BuyButtonWithName"] = "{0} ({1} {2})",
["CloseButton"] = "Close",
["DungeonRemovalCountdown"] = "Your dungeon will be removed in {0} minute(s)!",
["BlockedCommands"] = "You cannot use commands inside a dungeon!",
["EconRewardMessage"] = "You received {0} RP!",
["ServerRewardMessage"] = "You received {0} RN!",
["DungeonPurchased"] = "A {0} dungeon purchased by {1} has appeared at grid {2}!",
["DungeonCooldownMessage"] = "You must wait {0} seconds before purchasing another dungeon.",
["DungeonClearEconomyReward"] = "You received {0} RP for clearing the dungeon!",
["DungeonClearServerReward"] = "You received {0} RN for clearing the dungeon!",
["Dungeonclosed"] = "Dungeon closed. You have been teleported to the exit.",
["DungeonClearXpReward"] = "You received {0} XP for clearing the dungeon!",
["NoDungeonsToRemove"] = "There are no dungeons to remove.",
["TooFarToRemove"] = "You must be within 10 meters of a dungeon to remove it.",
["NotDungeonOwner"] = "You don't own an active dungeon.",
["ConfigReloaded"] = "Configuration reloaded.",
["PurchasesEnabled"] = "Purchases are now enabled.",
["PurchasesDisabled"] = "Purchases are now disabled.",
},
this
);
lang.RegisterMessages(
new Dictionary<string, string>
{
["AlreadyOwnsDungeonMessage"] = "Você já possui uma masmorra ativa. Não é possível comprar outra antes que ela expire ou seja removida.",
["DungeonAppeared"] = "Uma {0} apareceu na grade {1}!",
["DungeonRemovedDetail"] = "Masmorra de nível {0} removida na grade {1}.",
["DungeonSpawnedSuccess"] = "Masmorra gerada com sucesso na grade {0}!",
["NpcSpawnNotLoaded"] = "O plugin NpcSpawn não está carregado.",
["DungeonStillSpawningMessage"] = "A masmorra ainda está sendo gerada. Por favor, aguarde até que esteja pronta antes de entrar.",
["InsufficientFundsMessage"] = "Você não tem fundos suficientes para comprar o nível {0}. Requerido: {1}.",
["InvalidTierMessage"] = "Nome de nível inválido '{0}'. Níveis disponíveis: {1}.",
["ThisTierDisabled"] = "Esta dificuldade está desativada para compras",
["MaxActiveDungeonsReached"] = "Não é possível gerar: número máximo de masmorras ativas alcançado.",
["NoAccess"] = "Você não tem permissão para fazer isso.",
["PurchaseSuccessMessage"] = "Você comprou com sucesso o nível {0}!",
["ReceivedRequest"] = "Seu pedido foi adicionado à fila. Por favor aguarde.",
["UsageCreateMessage"] = "Uso: /createdungeon <nome_do_nível>",
["CleanedUp"] = "Todos os objetos relacionados a este plugin foram removidos.",
["NoInactiveDungeonsToRemove"] = "Não há masmorras inativas para remover.",
["WithdrawFailedMessage"] = "Falha ao retirar fundos para {0}.",
["DungeonBuyTitle"] = "Comprar Masmorra",
["BuyButton"] = "{0} ({1})",
["BuyButtonWithName"] = "{0} ({1} {2})",
["CloseButton"] = "Fechar",
["DungeonRemovalCountdown"] = "Sua masmorra será removida em {0} minuto(s)!",
["BlockedCommands"] = "Você não pode usar comandos dentro de uma masmorra!",
["EconRewardMessage"] = "Você recebeu {0} RP!",
["ServerRewardMessage"] = "Você recebeu {0} pontos de RN!",
["DungeonPurchased"] = "Uma masmorra {0} comprada por {1} apareceu na grade {2}!",
["DungeonCooldownMessage"] = "Você deve esperar {0} segundo(s) antes de comprar outra masmorra.",
["DungeonClearEconomyReward"] = "Você recebeu {0} RP por limpar a masmorra!",
["DungeonClearServerReward"] = "Você recebeu {0} pontos de RN por limpar a masmorra!",
["Dungeonclosed"] = "Masmorra fechada. Você foi teleportado para a saída.",
["DungeonClearXpReward"] = "Você recebeu {0} XP por limpar a masmorra!",
["NoDungeonsToRemove"] = "Não há masmorras para remover.",
["TooFarToRemove"] = "Você deve estar a até 10 metros de uma masmorra para removê-la.",
["NotDungeonOwner"] = "Você não possui uma masmorra ativa.",
["ConfigReloaded"] = "Configuração recarregada.",
["PurchasesEnabled"] = "Compras agora estão habilitadas.",
["PurchasesDisabled"] = "Compras agora estão desabilitadas.",
},
this,
"pt-BR"
);
lang.RegisterMessages(
new Dictionary<string, string>
{
["AlreadyOwnsDungeonMessage"] = "Du besitzt bereits ein aktives Verlies. Du kannst kein weiteres kaufen, bevor es abläuft oder entfernt wird.",
["DungeonAppeared"] = "Ein {0} ist bei Gitter {1} erschienen!",
["DungeonRemovedDetail"] = "Verlies der Stufe {0} bei Gitter {1} wurde entfernt.",
["DungeonSpawnedSuccess"] = "Verlies erfolgreich bei Gitter {0} erstellt!",
["NpcSpawnNotLoaded"] = "Das Plugin NpcSpawn ist nicht geladen.",
["DungeonStillSpawningMessage"] = "Das Verlies wird noch erstellt. Bitte warte, bis es bereit ist, bevor du es betrittst.",
["InsufficientFundsMessage"] = "Du hast nicht genug Geld, um die Stufe {0} zu kaufen. Erforderlich: {1}.",
["InvalidTierMessage"] = "Ungültiger Stufenname '{0}'. Verfügbare Stufen: {1}.",
["ThisTierDisabled"] = "Diese Schwierigkeitsstufe ist für Käufe deaktiviert",
["MaxActiveDungeonsReached"] = "Kann nicht erstellt werden: Maximale Anzahl aktiver Verliese erreicht.",
["NoAccess"] = "Du hast keine Berechtigung, dies zu tun.",
["PurchaseSuccessMessage"] = "Du hast die Stufe {0} erfolgreich gekauft!",
["ReceivedRequest"] = "Deine Anfrage wurde der Warteschlange hinzugefügt. Bitte warte.",
["UsageCreateMessage"] = "Verwendung: /createdungeon <stufen_name>",
["CleanedUp"] = "Alle mit diesem Plugin verbundenen Objekte wurden entfernt.",
["NoInactiveDungeonsToRemove"] = "Es gibt keine inaktiven Verliese zum Entfernen.",
["WithdrawFailedMessage"] = "Fehler beim Abheben der Mittel für {0}.",
["DungeonBuyTitle"] = "Verlies kaufen",
["BuyButton"] = "{0} ({1})",
["BuyButtonWithName"] = "{0} ({1} {2})",
["CloseButton"] = "Schließen",
["DungeonRemovalCountdown"] = "Dein Verlies wird in {0} Minute(n) entfernt!",
["BlockedCommands"] = "Du kannst in einem Verlies keine Befehle verwenden!",
["EconRewardMessage"] = "Du hast {0} Münzen erhalten!",
["ServerRewardMessage"] = "Du hast {0} RP erhalten!",
["DungeonPurchased"] = "Ein {0} Verlies, gekauft von {1}, ist bei Gitter {2} erschienen!",
["DungeonCooldownMessage"] = "Du musst {0} Sekunde(n) warten, bevor du ein weiteres Verlies kaufen kannst.",
["DungeonClearEconomyReward"] = "Du hast {0} RP erhalten, weil du das Verlies vollständig geleert hast!",
["DungeonClearServerReward"] = "Du hast {0} Serverpunkte erhalten, weil du das Verlies vollständig geleert hast!",
["Dungeonclosed"] = "Verlies geschlossen. Du wurdest zum Ausgang teleportiert.",
["DungeonClearXpReward"] = "Du hast {0} XP erhalten, weil du das Verlies vollständig geleert hast!",
["NoDungeonsToRemove"] = "Es gibt keine Verliese zum Entfernen.",
["TooFarToRemove"] = "Du musst dich innerhalb von 10 Metern eines Verlieses befinden, um es zu entfernen.",
["NotDungeonOwner"] = "Du besitzt kein aktives Verlies.",
["ConfigReloaded"] = "Konfiguration neu geladen.",
["PurchasesEnabled"] = "Käufe sind jetzt aktiviert.",
["PurchasesDisabled"] = "Käufe sind jetzt deaktiviert.",
},
this,
"de"
);
lang.RegisterMessages(
new Dictionary<string, string>
{
["AlreadyOwnsDungeonMessage"] = "Ya posees una mazmorra activa. No puedes comprar otra hasta que expire o sea eliminada.",
["DungeonAppeared"] = "¡Una {0} ha aparecido en la cuadrícula {1}!",
["DungeonRemovedDetail"] = "Mazmorra de nivel {0} eliminada en la cuadrícula {1}.",
["DungeonSpawnedSuccess"] = "¡Mazmorra creada exitosamente en la cuadrícula {0}!",
["NpcSpawnNotLoaded"] = "El plugin NpcSpawn no está cargado.",
["DungeonStillSpawningMessage"] = "La mazmorra aún está generándose. Por favor, espera antes de entrar.",
["InsufficientFundsMessage"] = "No tienes suficientes fondos para comprar el nivel {0}. Requerido: {1}.",
["InvalidTierMessage"] = "Nombre de nivel inválido '{0}'. Niveles disponibles: {1}.",
["ThisTierDisabled"] = "Esta dificultad está deshabilitada para compras",
["MaxActiveDungeonsReached"] = "No se puede generar: se alcanzó el número máximo de mazmorras activas.",
["NoAccess"] = "No tienes permiso para hacer esto.",
["PurchaseSuccessMessage"] = "¡Has comprado con éxito el nivel {0}!",
["ReceivedRequest"] = "Tu solicitud se ha añadido a la cola. Por favor, espera.",
["UsageCreateMessage"] = "Uso: /createdungeon <nombre_del_nivel>",
["CleanedUp"] = "Todos los objetos relacionados con este plugin han sido eliminados.",
["NoInactiveDungeonsToRemove"] = "No hay mazmorras inactivas para eliminar.",
["WithdrawFailedMessage"] = "Error al retirar fondos para {0}.",
["DungeonBuyTitle"] = "Comprar Mazmorra",
["BuyButton"] = "{0} ({1})",
["BuyButtonWithName"] = "{0} ({1} {2})",
["CloseButton"] = "Cerrar",
["DungeonRemovalCountdown"] = "¡Tu mazmorra será eliminada en {0} minuto(s)!",
["BlockedCommands"] = "¡No puedes usar comandos dentro de una mazmorra!",
["EconRewardMessage"] = "¡Has recibido {0} RP!",
["ServerRewardMessage"] = "¡Has recibido {0} puntos de RN!",
["DungeonPurchased"] = "¡Una mazmorra {0} comprada por {1} ha aparecido en la cuadrícula {2}!",
["DungeonCooldownMessage"] = "Debes esperar {0} segundo(s) antes de comprar otra mazmorra.",
["DungeonClearEconomyReward"] = "¡Has recibido {0} RP por vaciar completamente la mazmorra!",
["DungeonClearServerReward"] = "¡Has recibido {0} puntos de RN por vaciar completamente la mazmorra!",
["Dungeonclosed"] = "Mazmorra cerrada. Has sido teletransportado a la salida.",
["DungeonClearXpReward"] = "¡Has recibido {0} XP por vaciar completamente la mazmorra!",
["NoDungeonsToRemove"] = "No hay mazmorras para eliminar.",
["TooFarToRemove"] = "Debes estar a menos de 10 metros de una mazmorra para eliminarla.",
["NotDungeonOwner"] = "No posees una mazmorra activa.",
["ConfigReloaded"] = "Configuración recargada.",
["PurchasesEnabled"] = "Las compras ahora están habilitadas.",
["PurchasesDisabled"] = "Las compras ahora están deshabilitadas.",
},
this,
"es"
);
lang.RegisterMessages(
new Dictionary<string, string>
{
["AlreadyOwnsDungeonMessage"] = "У вас уже есть активное подземелье. Вы не можете приобрести другое, пока оно не истечет или не будет удалено.",
["DungeonAppeared"] = "Подземелье {0} появилось в сетке {1}!",
["DungeonRemovedDetail"] = "Удалено подземелье уровня {0} в сетке {1}.",
["DungeonSpawnedSuccess"] = "Подземелье успешно создано в сетке {0}!",
["NpcSpawnNotLoaded"] = "Плагин NpcSpawn не загружен.",
["DungeonStillSpawningMessage"] = "Подземелье еще загружается. Пожалуйста, подождите, прежде чем войти.",
["InsufficientFundsMessage"] = "У вас недостаточно средств для покупки уровня {0}. Нужно: {1}.",
["InvalidTierMessage"] = "Неверное название уровня '{0}'. Доступные уровни: {1}.",
["ThisTierDisabled"] = "Этот уровень сложности отключен для покупок",
["MaxActiveDungeonsReached"] = "Невозможно создать: достигнуто максимальное количество активных подземелий.",
["NoAccess"] = "У вас нет разрешения на это действие.",
["PurchaseSuccessMessage"] = "Вы успешно приобрели уровень {0}!",
["ReceivedRequest"] = "Ваш запрос был добавлен в очередь. Пожалуйста, подождите.",
["UsageCreateMessage"] = "Использование: /createdungeon <название_уровня>",
["CleanedUp"] = "Все объекты, связанные с этим плагином, были удалены.",
["NoInactiveDungeonsToRemove"] = "Нет неактивных подземелий для удаления.",
["WithdrawFailedMessage"] = "Ошибка при попытке вывести средства для {0}.",
["DungeonBuyTitle"] = "Купить Подземелье",
["BuyButton"] = "{0} ({1})",
["BuyButtonWithName"] = "{0} ({1} {2})",
["CloseButton"] = "Закрыть",
["DungeonRemovalCountdown"] = "Ваше подземелье будет удалено через {0} минут(ы)!",
["BlockedCommands"] = "Вы не можете использовать команды внутри подземелья!",
["EconRewardMessage"] = "Вы получили {0} RP!",
["ServerRewardMessage"] = "Вы получили {0} RN!",
["DungeonPurchased"] = "Подземелье {0}, купленное {1}, появилось на сетке {2}!",
["DungeonCooldownMessage"] = "Вы должны подождать {0} секунд(ы), прежде чем купить другое подземелье.",
["DungeonClearEconomyReward"] = "Вы получили {0} RP за полное опустошение подземелья!",
["DungeonClearServerReward"] = "Вы получили {0} RN за полное опустошение подземелья!",
["Dungeonclosed"] = "Подземелье закрыто. Вы были телепортированы к выходу.",
["DungeonClearXpReward"] = "Вы получили {0} XP за полное опустошение подземелья!",
["NoDungeonsToRemove"] = "Нет подземелий для удаления.",
["TooFarToRemove"] = "Вы должны находиться в пределах 10 метров от подземелья, чтобы удалить его.",
["NotDungeonOwner"] = "Вы не являетесь владельцем активного подземелья.",
["ConfigReloaded"] = "Конфигурация перезагружена.",
["PurchasesEnabled"] = "Покупки теперь включены.",
["PurchasesDisabled"] = "Покупки теперь отключены.",
},
this,
"ru"
);
}
#endregion
#region Configuration
private class Configuration
{
[JsonProperty("Enable Purchases")]
public bool EnablePurchases { get; set; } = true;
[JsonProperty("Use Notify Plugin")]
public bool UseNotify { get; set; } = false;
[JsonProperty("Enable Debug Logs")]
public bool EnableDebugLogs { get; set; } = false;
[JsonProperty("Enable Warning Logs")]
public bool EnableWarningLogs { get; set; } = true;
[JsonProperty("Enable Toast Messages")]
public bool EnableShowToast { get; set; } = true;
[JsonProperty("Prevent Drop Backpack")]
public bool PreventDropBackpack { get; set; } = true;
[JsonProperty("Teleport Player To Outside On Death")]
public bool TeleportPlayerOnDeath { get; set; } = true;
[JsonProperty("Spawn Point Configuration")]
public SpawnPointConfiguration SpawnPointConfiguration { get; set; } = new SpawnPointConfiguration();
[JsonProperty("Place Sphere on Dungeon Portal")]
public bool EnablePlaceSphere { get; set; } = true;
[JsonProperty("Radius Contruction Block on Dungeon Portal")]
public int RadiusSphere { get; set; } = 20;
[JsonProperty("Custom AutoTurret Behavior (Use in case AutoTurrets aren't shooting)")]
public bool CustomAutoTurretBehavior { get; set; } = false;
[JsonProperty("NightVision Plugin Time On Leaving Dungeon(-1 for no override)")]
public int NightVisionTime { get; set; } = 12;
[JsonProperty("Lock dungeon for first player")]
public bool LockDungeonToPlayer { get; set; } = true;
[JsonProperty("Economy Plugin (1 - Economics, 2 - ServerRewards, 3 - Custom)")]
public int EconomyPlugin { get; set; } = 3;
[JsonProperty("Custom Economy Item Name")]
public string CustomEconomyItemName { get; set; } = "scrap";
[JsonProperty("Custom Economy Display Name")]
public string CustomEconomyDisplayName { get; set; } = "Scrap";
[JsonProperty("Economics Display Name")]
public string EconomicsDisplayName { get; set; } = "RP";
[JsonProperty("ServerRewards Display Name")]
public string ServerRewardsDisplayName { get; set; } = "RN";
public string GetEconomyItemName()
{
return EconomyPlugin switch
{
1 => "economics",
2 => "serverrewards",
_ => CustomEconomyItemName,
};
}
public string GetEconomyDisplayName()
{
return EconomyPlugin switch
{
1 => EconomicsDisplayName,
2 => ServerRewardsDisplayName,
_ => CustomEconomyDisplayName,
};
}
[JsonProperty("Allow team members")]
public bool AllowTeam { get; set; } = true;
[JsonProperty("Allow friends")]
public bool AllowFriends { get; set; } = true;
[JsonProperty("Small Delay (seconds) – increase if FPS drops")]
public float SmallDelay { get; set; } = 0.15f;
[JsonProperty("Big Delay (seconds) – increase if FPS drops")]
public float BigDelay { get; set; } = 0.50f;
[JsonProperty("Dungeon Settings")]
public DungeonSpawnSettings DungeonSpawn { get; set; } = new DungeonSpawnSettings();
[JsonProperty("Blocked commands inside a dungeon", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public HashSet<string> BlockCommands { get; set; } = new HashSet<string> { "/remove", "/tpa", "remove.toggle" };
[JsonProperty("Tiers", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<DungeonTierConfig> Tiers { get; set; } =
new List<DungeonTierConfig>
{
new DungeonTierConfig
{
TierName = "Easy",
MapName = "Dungeon: Easy",
MarkerColorHex = "#3cd834",
BuildingGradeSelection = 3,
ContainerColor = 0,
DungeonRooms = 1,
DungeonRoomSize = 5,
BuyCost = 300,
EntitySpawnLimits = new EntitySpawnLimits
{
NpcsCounts = 9,
BossesCounts = 1,
TurretsCounts = 5,
LootBoxesCounts = 5,
},
AutoTurretConfig = new TurretConfig { Health = 300, WeaponShortName = "pistol.revolver" },
LootBoxSkinID = 2998755525,
MaxItemsPerBox = 3,
MaxItemsPerNpc = 3,
MaxItemsPerBossNpc = 3,
NpcConfig = new NpcConfig
{
Name = "Wild Scavenger",
Health = 100,
DamageScale = 0.5f,
WearItems = new WearConfig
{
Items = new List<WearItemConfig>
{
new() { ShortName = "tshirt.long", SkinID = 10118 },
new() { ShortName = "burlap.gloves", SkinID = 949616124 },
new() { ShortName = "attire.snowman.helmet", SkinID = 0 },
new() {
// ... [Code preview truncated due to length. Full source in attached file] ...