turns-00051.parquet:21690
7c7550e4aa67b2c5137ae9ed
turn 1/4gpt-4o-2024-08-06EnglishBrazil3141 words
degenerate_repetitionAbsentFinal dense release
USER
using System;
using System.Collections.Generic;
using System.Linq;
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Rust;
using UnityEngine;
using Random = UnityEngine.Random;
using Time = UnityEngine.Time;
namespace Oxide.Plugins
{
[Info("Dungeons", "Marte6", "1.1.1")]
public class Dungeons : RustPlugin
{
private ConfigData _configData;
private Timer _autoSpawnTimer;
private object _currentDungeon;
private List<BaseEntity> _spawnedEntities = new();
private Dictionary<DungeonTierConfig, string> tierNameMap;
private const string XmasDungeonPrefabPath = "assets/prefabs/missions/portal/xmasportalentry.prefab";
private const string HalloweenDungeonPrefabPath = "assets/prefabs/missions/portal/halloweenportalentry.prefab";
#region Initialization
private void Init()
{
tierNameMap = new Dictionary<DungeonTierConfig, string>
{
{ _configData.Tiers.Easy, "Easy" },
{ _configData.Tiers.Normal, "Normal" },
{ _configData.Tiers.Medium, "Medium" },
{ _configData.Tiers.Hard, "Hard" },
{ _configData.Tiers.Nightmare, "Nightmare" },
};
RemoveAllExistingDungeons();
if (_configData.AutoSpawn.EnableAutoSpawn)
{
StartAutoSpawnTimer();
}
}
private void RemoveAllExistingDungeons()
{
var existingXmasDungeons = UnityEngine.Object.FindObjectsOfType<XmasDungeon>();
foreach (var dungeon in existingXmasDungeons)
{
Puts($"Removing existing Xmas Dungeon at position {dungeon.transform.position}");
dungeon.Kill(BaseNetworkable.DestroyMode.None);
}
var existingHalloweenDungeons = UnityEngine.Object.FindObjectsOfType<HalloweenDungeon>();
foreach (var dungeon in existingHalloweenDungeons)
{
Puts($"Removing existing Halloween Dungeon at position {dungeon.transform.position}");
dungeon.Kill(BaseNetworkable.DestroyMode.None);
}
}
#endregion
#region Hooks
private object CanEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo)
{
if (!IsDungeonCreatedEntity(entity))
{
return null;
}
return true;
}
private object OnEntityTakeDamage(BaseCombatEntity victim, HitInfo info)
{
if (victim == null || info == null)
return false;
BaseEntity attacker = info.Initiator;
if (IsDungeonCreatedEntity(victim) && attacker is AutoTurret turret && IsDungeonCreatedEntity(turret))
{
return false;
}
return null;
}
private bool IsDungeonCreatedEntity(BaseEntity entity)
{
return entity != null && _spawnedEntities.Contains(entity);
}
#endregion
#region Dungeon Management
[ChatCommand("removedun")]
private void RemoveDungeon()
{
RemoveExistingDungeon();
Puts("Dungeon removed.");
}
[ChatCommand("dun")]
private void AutoSpawnDungeon()
{
RemoveExistingDungeon();
foreach (var player in BasePlayer.activePlayerList)
{
if (!TryFindDungeonSpawnPoint(player.transform.position, out var position, out var rotation))
continue;
var selectedTier = CreateDungeon(position, rotation);
if (selectedTier != null)
{
NotifyPlayersOfDungeonLocation(position, selectedTier);
return;
}
}
Puts("Failed to auto-spawn dungeon after checking all players.");
}
private void StartAutoSpawnTimer()
{
_autoSpawnTimer = timer.Every(_configData.AutoSpawn.AutoSpawnIntervalMinutes * 60f, AutoSpawnDungeon);
}
private void NotifyPlayersOfDungeonLocation(Vector3 position, DungeonTierConfig tierConfig)
{
if (tierNameMap.TryGetValue(tierConfig, out string tierName))
{
foreach (var player in BasePlayer.activePlayerList)
{
player.ShowToast(GameTip.Styles.Blue_Normal, $"A {tierName} Dungeon has appeared at coordinates: ({position.x:F0}, {position.z:F0})!", false);
player.ChatMessage($"A {tierName} Dungeon has appeared at coordinates: ({position.x:F0}, {position.z:F0})!");
}
}
}
private DungeonTierConfig CreateDungeon(Vector3 position, Quaternion rotation)
{
string chosenPrefabPath = RandomlySelectDungeonPrefab();
BasePortal dungeon = null;
if (string.IsNullOrEmpty(chosenPrefabPath))
{
Puts("Failed to select a valid dungeon prefab.");
return null;
}
Puts($"Attempting to create a dungeon. Prefab: {chosenPrefabPath}");
if (chosenPrefabPath == XmasDungeonPrefabPath)
{
dungeon = GameManager.server.CreateEntity(XmasDungeonPrefabPath, position, rotation) as XmasDungeon;
}
else if (chosenPrefabPath == HalloweenDungeonPrefabPath)
{
dungeon = GameManager.server.CreateEntity(HalloweenDungeonPrefabPath, position, rotation) as HalloweenDungeon;
}
if (dungeon != null)
{
dungeon.Spawn();
Puts("Dungeon spawned, checking cell count...");
var proceduralDungeon = (dungeon is XmasDungeon xmasDungeon) ? xmasDungeon.dungeonInstance.Get(true) : (dungeon as HalloweenDungeon)?.dungeonInstance.Get(true);
if (proceduralDungeon != null)
{
Puts($"Current dungeon cell count: {proceduralDungeon.spawnedCells.Count}");
if (proceduralDungeon.spawnedCells.Count >= 1)
{
Puts("Dungeon is valid with sufficient cells, determining tier.");
DungeonTierConfig selectedTier = DetermineDungeonTier(proceduralDungeon.spawnedCells.Count);
IntegrateEntitiesIntoDungeon(dungeon, selectedTier);
_currentDungeon = dungeon;
return selectedTier;
}
else
{
Puts("Insufficient cells in dungeon, destroying dungeon.");
RemoveDungeonEntities();
dungeon.Kill(BaseNetworkable.DestroyMode.None);
}
}
else
{
Puts("Failed to retrieve procedural dungeon instance, destroying dungeon.");
RemoveDungeonEntities();
dungeon.Kill(BaseNetworkable.DestroyMode.None);
}
}
else
{
Puts("Failed to create dungeon entity.");
}
return null;
}
private DungeonTierConfig DetermineDungeonTier(int cellCount)
{
if (cellCount < 5)
{
return _configData.Tiers.Easy;
}
else if (cellCount >= 5 && cellCount < 8)
{
return _configData.Tiers.Normal;
}
else if (cellCount >= 8 && cellCount < 11)
{
return _configData.Tiers.Medium;
}
else if (cellCount >= 11 && cellCount < 14)
{
return _configData.Tiers.Hard;
}
else
{
return _configData.Tiers.Nightmare;
}
}
private void RemoveDungeonEntities()
{
Puts("Removing spawned dungeon entities...");
foreach (var entity in _spawnedEntities)
{
if (entity != null && !entity.IsDestroyed)
{
entity.Kill(BaseNetworkable.DestroyMode.None);
Puts($"Entity {entity.ShortPrefabName} at {entity.transform.position} destroyed.");
}
}
_spawnedEntities.Clear();
Puts("All dungeon entities removed.");
}
private string RandomlySelectDungeonPrefab()
{
var availablePrefabs = new List<string>();
if (_configData.DungeonSpawn.EnableXmasDungeon)
availablePrefabs.Add(XmasDungeonPrefabPath);
if (_configData.DungeonSpawn.EnableHalloweenDungeon)
availablePrefabs.Add(HalloweenDungeonPrefabPath);
if (availablePrefabs.Count == 0)
return null;
return availablePrefabs[Random.Range(0, availablePrefabs.Count)];
}
private void IntegrateEntitiesIntoDungeon(BasePortal dungeon, DungeonTierConfig tierConfig)
{
var proceduralDungeon = (dungeon is XmasDungeon xmasDungeon) ? xmasDungeon.dungeonInstance.Get(true) : (dungeon as HalloweenDungeon)?.dungeonInstance.Get(true);
if (proceduralDungeon == null)
{
Puts("Failed to get procedural dungeon instance.");
return;
}
var allSpawnEntries = GatherAllSpawnEntries(tierConfig);
int remainingEntities = allSpawnEntries.Count;
foreach (var cell in proceduralDungeon.spawnedCells)
{
int entityCountForCell = Mathf.Min(remainingEntities, tierConfig.AutoTurretConfig.Total);
if (entityCountForCell > 0)
{
SetupSpawnGroup(cell, allSpawnEntries.Take(entityCountForCell).ToList(), tierConfig, entityCountForCell);
allSpawnEntries.RemoveRange(0, entityCountForCell);
remainingEntities -= entityCountForCell;
}
if (remainingEntities <= 0)
{
break;
}
}
}
private List<SpawnGroup.SpawnEntry> GatherAllSpawnEntries(DungeonTierConfig tierConfig)
{
var allSpawnEntries = new List<SpawnGroup.SpawnEntry>();
// Adiciona NPCs conforme definidos na configuração do tier da masmorra
foreach (var config in tierConfig.NpcSpawnConfigs)
{
if (GameManifest.pathToGuid.TryGetValue(config.PrefabName, out var guid))
{
// Por cada NPC definido na configuração, adiciona uma entrada de spawn
for (int i = 0; i < config.Total; i++)
{
allSpawnEntries.Add(
new SpawnGroup.SpawnEntry
{
prefab = new GameObjectRef { guid = guid },
weight = 1,
mobile = true,
}
);
}
}
}
// Exemplo de adição de turrets
for (int i = 0; i < tierConfig.AutoTurretConfig.Total; i++)
{
if (GameManifest.pathToGuid.TryGetValue("assets/prefabs/npc/autoturret/autoturret_deployed.prefab", out var turretGuid))
{
allSpawnEntries.Add(
new SpawnGroup.SpawnEntry
{
prefab = new GameObjectRef { guid = turretGuid },
weight = 1,
mobile = false,
}
);
}
}
// Adiciona caixas de loot por tier
for (int i = 0; i < tierConfig.TotalLootBoxes; i++)
{
if (GameManifest.pathToGuid.TryGetValue("assets/prefabs/deployable/woodenbox/woodbox_deployed.prefab", out var boxGuid))
{
allSpawnEntries.Add(
new SpawnGroup.SpawnEntry
{
prefab = new GameObjectRef { guid = boxGuid },
weight = 1,
mobile = false,
}
);
}
}
return allSpawnEntries.OrderBy(x => Random.value).ToList();
}
private void SetupSpawnGroup(ProceduralDungeonCell cell, List<SpawnGroup.SpawnEntry> allSpawnEntries, DungeonTierConfig tierConfig, int maxPopulation)
{
var spawnGroup = cell.gameObject.AddComponent<SpawnGroup>();
spawnGroup.prefabs = allSpawnEntries;
spawnGroup.maxPopulation = maxPopulation; // Use calculated maxPopulation
spawnGroup.numToSpawnPerTickMin = 0;
spawnGroup.numToSpawnPerTickMax = 0;
spawnGroup.fillOnSpawn = true;
spawnGroup.wantsInitialSpawn = true;
spawnGroup.SpawnInitial();
foreach (var instance in spawnGroup.spawnInstances)
{
var entity = instance.GetComponent<BaseEntity>();
if (entity != null && !_spawnedEntities.Contains(entity))
{
_spawnedEntities.Add(entity);
Puts($"Entity {entity.ShortPrefabName} spawned at {entity.transform.position}");
if (entity is AutoTurret autoTurret)
{
ConfigureAutoTurret(autoTurret, tierConfig.AutoTurretConfig);
TurretComponent.Configure(autoTurret, 30f);
}
if (entity is StorageContainer box)
{
box.skinID = _configData.LootBoxConfig.SmallWoodBoxSkinID;
AddLockToBox(box);
FillLootBox(box.inventory, _configData.LootBoxConfig.LootItems);
}
}
}
}
private void AddLockToBox(StorageContainer box)
{
CodeLock codeLock = GameManager.server.CreateEntity("assets/prefabs/locks/keypad/lock.code.prefab") as CodeLock;
codeLock.SetParent(box, box.GetSlotAnchorName(BaseEntity.Slot.Lock));
codeLock.Spawn();
codeLock.code = Random.Range(1000, 9999).ToString();
codeLock.hasCode = true;
codeLock.guestCode = string.Empty;
codeLock.hasGuestCode = false;
codeLock.guestPlayers.Clear();
codeLock.whitelistPlayers.Clear();
codeLock.SetFlag(BaseEntity.Flags.Locked, true);
}
private void FillLootBox(ItemContainer container, List<ItemConfig> lootItems)
{
var shuffledItems = lootItems.OrderBy(x => Random.Range(0f, 1f)).ToList();
int maxDifferentItems = _configData.LootBoxConfig.MaxDifferentItemsPerBox;
int differentItemsCount = 0;
foreach (var itemInfo in shuffledItems)
{
if (differentItemsCount >= maxDifferentItems)
break;
if (Random.Range(0f, 100f) <= itemInfo.InclusionChancePercentage)
{
var itemDefinition = ItemManager.FindItemDefinition(itemInfo.ShortName);
if (itemDefinition != null)
{
int amount = Random.Range(itemInfo.MinimumAmount, itemInfo.MaximumAmount + 1);
if (container.itemList.Count < container.capacity)
{
Item item = ItemManager.Create(itemDefinition, amount);
item.MoveToContainer(container);
differentItemsCount++;
}
}
}
}
}
private void ConfigureAutoTurret(AutoTurret autoTurret, TurretConfig turretConfig)
{
autoTurret.health = turretConfig.Health;
autoTurret.SetMaxHealth(autoTurret.health);
var weapon = ItemManager.CreateByName(turretConfig.WeaponShortName);
if (weapon != null)
{
foreach (var attachmentShortName in turretConfig.AttachmentShortNames)
{
var attachment = ItemManager.CreateByName(attachmentShortName);
attachment?.MoveToContainer(weapon.contents);
}
weapon.MoveToContainer(autoTurret.inventory, 0);
autoTurret.UpdateAttachedWeapon();
}
foreach (var reserveAmmoInfo in turretConfig.ReserveAmmo)
{
var ammoItem = ItemManager.CreateByName(reserveAmmoInfo.ShortName, Random.Range(reserveAmmoInfo.MinimumAmount, reserveAmmoInfo.MaximumAmount + 1));
ammoItem.MoveToContainer(autoTurret.inventory);
}
if (turretConfig.ClipAmmo != null)
{
var loadedAmmoItem = ItemManager.CreateByName(turretConfig.ClipAmmo.ShortName, Random.Range(turretConfig.ClipAmmo.MinimumAmount, turretConfig.ClipAmmo.MaximumAmount + 1));
BaseProjectile heldEntity = weapon.GetHeldEntity() as BaseProjectile;
heldEntity.primaryMagazine.ammoType = loadedAmmoItem.info;
}
autoTurret.UpdateTotalAmmo();
autoTurret.EnsureReloaded();
autoTurret.SetPeacekeepermode(false);
autoTurret.InitiateStartup();
autoTurret.SetIsOnline(true);
autoTurret.SendNetworkUpdateImmediate();
}
#endregion
#region Utility and Cleanup
private bool TryFindDungeonSpawnPoint(Vector3 center, out Vector3 suitablePosition, out Quaternion suitableRotation)
{
for (int attempt = 0; attempt < _configData.AutoSpawn.MaxSpawnAttempts; attempt++)
{
Vector3 candidatePosition = TerrainUtilities.GetRandomPosition(center, _configData.AutoSpawn.MinimumSearchRadius, _configData.AutoSpawn.MaximumSearchRadius);
if (IsValidSpawnPosition(candidatePosition, out suitablePosition, out suitableRotation))
return true;
}
suitablePosition = Vector3.zero;
suitableRotation = Quaternion.identity;
return false;
}
private bool IsValidSpawnPosition(Vector3 position, out Vector3 suitablePosition, out Quaternion suitableRotation)
{
suitablePosition = Vector3.zero;
suitableRotation = Quaternion.identity;
if (TerrainUtilities.InsideRock(position, _configData.AutoSpawn.RocksAvoidanceRadius))
{
return false;
}
if (TerrainUtilities.InRadTown(position) || TerrainUtilities.HasEntityNearby(position, _configData.AutoSpawn.NearbyEntitiesAvoidanceRadius, Layers.Mask.Construction))
{
return false;
}
if (TerrainUtilities.InWater(position) || TerrainUtilities.OnRoadOrRail(position))
{
return false;
}
if (TerrainUtilities.InNoBuildZone(position, _configData.AutoSpawn.DistanceFromNoBuildZones))
{
return false;
}
if (TerrainUtilities.GetTerrainInfo(position, out var hitInfo))
{
suitablePosition = hitInfo.point;
suitableRotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
return true;
}
return false;
}
private void RemoveExistingDungeon()
{
if (_currentDungeon is BaseEntity dungeon)
{
Puts("Removing existing dungeon...");
foreach (var entity in _spawnedEntities)
{
if (entity != null && !entity.IsDestroyed)
{
entity.Kill(BaseNetworkable.DestroyMode.None);
Puts($"Entity {entity.ShortPrefabName} destroyed.");
}
}
_spawnedEntities.Clear();
if (!dungeon.IsDestroyed)
{
dungeon.Kill(BaseNetworkable.DestroyMode.None);
Puts("Current dungeon entity destroyed.");
}
else
{
Puts("Current dungeon entity already destroyed.");
}
_currentDungeon = null;
}
else
{
Puts("No existing dungeon to remove.");
}
}
private void Unload()
{
RemoveDungeonEntities();
RemoveAllExistingDungeons();
_autoSpawnTimer?.Destroy();
}
#endregion
#region Terrain Utilities
private static class TerrainUtilities
{
public static Vector3 GetRandomPosition(Vector3 center, float minRadius, float maxRadius, bool adjustToWaterHeight = false)
{
Vector3 randomDirection = Random.onUnitSphere;
float randomDistance = Random.Range(minRadius, maxRadius);
Vector3 randomPosition = center + randomDirection * randomDistance;
randomPosition.y = adjustToWaterHeight ? TerrainMeta.WaterMap.GetHeight(randomPosition) : TerrainMeta.HeightMap.GetHeight(randomPosition);
return randomPosition;
}
public static bool InsideRock(Vector3 position, float radius)
{
var colliders = Pool.GetList<Collider>();
Vis.Colliders(position, radius, colliders, Layers.Mask.World, QueryTriggerInteraction.Ignore);
bool result = colliders.Any(collider =>
collider.name.Contains("rock", StringComparison.OrdinalIgnoreCase)
|| collider.name.Contains("cliff", StringComparison.OrdinalIgnoreCase)
|| collider.name.Contains("formation", StringComparison.OrdinalIgnoreCase)
);
Pool.FreeList(ref colliders);
return result;
}
public static bool InRadTown(Vector3 position)
{
return TerrainMeta.Path.Monuments.Any(monument => monument.IsInBounds(position) && monument.shouldDisplayOnMap)
|| (TerrainMeta.TopologyMap.GetTopology(position) & (int)TerrainTopology.Enum.Monument) != 0;
}
public static bool HasEntityNearby(Vector3 position, float radius, int mask, string prefabName = null)
{
var hitColliders = Pool.GetList<Collider>();
GamePhysics.OverlapSphere(position, radius, hitColliders, mask, QueryTriggerInteraction.Ignore);
bool result = hitColliders.Any(collider =>
{
BaseEntity entity = collider.gameObject.ToBaseEntity();
return entity != null && (prefabName == null || entity.PrefabName == prefabName);
});
Pool.FreeList(ref hitColliders);
return result;
}
public static bool InWater(Vector3 position)
{
return WaterLevel.Test(position, false, false);
}
public static bool OnRoadOrRail(Vector3 position)
{
return (TerrainMeta.TopologyMap.GetTopology(position) & (int)(TerrainTopology.Enum.Road | TerrainTopology.Enum.Roadside | TerrainTopology.Enum.Rail | TerrainTopology.Enum.Railside))
!= 0;
}
public static bool GetTerrainInfo(Vector3 startPosition, out RaycastHit hitInfo, float range = 1f, LayerMask mask = default)
{
mask = mask == default ? (LayerMask)Layers.Mask.Terrain : mask;
return Physics.Linecast(startPosition + Vector3.up * range, startPosition - Vector3.up * range, out hitInfo, mask);
}
public static bool InNoBuildZone(Vector3 position, float radius)
{
return Physics.CheckSphere(position, radius, Layers.Mask.Prevent_Building, QueryTriggerInteraction.Ignore);
}
}
#endregion
#region Turret Component
internal class TurretComponent : FacepunchBehaviour
{
private AutoTurret turretEntity;
private float detectionRadius;
internal static void Configure(AutoTurret turretEntity, float detectionRadius)
{
var component = turretEntity.gameObject.AddComponent<TurretComponent>();
component.Initialize(turretEntity, detectionRadius);
}
private void Initialize(AutoTurret turretEntity, float detectionRadius)
{
this.turretEntity = turretEntity;
this.detectionRadius = detectionRadius;
var triggerCollider = turretEntity.targetTrigger.GetComponent<SphereCollider>();
triggerCollider.enabled = false;
turretEntity.Invoke(
() =>
{
turretEntity.CancelInvoke(turretEntity.ServerTick);
turretEntity.SetTarget(null);
},
1.1f
);
turretEntity.InvokeRepeating(new Action(TurretTick), Random.Range(1.2f, 2.2f), 0.015f);
turretEntity.InvokeRepeating(SearchTargets, 3f, 1f);
}
private void SearchTargets()
{
if (turretEntity.targetTrigger.entityContents == null)
turretEntity.targetTrigger.entityContents = new HashSet<BaseEntity>();
else
turretEntity.targetTrigger.entityContents.Clear();
int found = BaseEntity.Query.Server.GetPlayersInSphereFast(transform.position, detectionRadius, AIBrainSenses.playerQueryResults, IsValidTarget);
if (found == 0)
return;
turretEntity.authDirty = true;
for (int i = 0; i < found; i++)
{
var player = AIBrainSenses.playerQueryResults[i];
if (Interface.CallHook("OnEntityEnter", turretEntity.targetTrigger, player) != null)
continue;
if (player.IsSleeping() || (player.InSafeZone() && !player.IsHostile()))
continue;
turretEntity.targetTrigger.entityContents.Add(player);
}
}
private bool IsValidTarget(BasePlayer player)
{
return player != null && !player.IsNpc;
}
private void TurretTick()
{
if (turretEntity.isClient || turretEntity.IsDestroyed)
return;
float timeTick = (float)turretEntity.timeSinceLastServerTick;
turretEntity.timeSinceLastServerTick = 0;
if (!turretEntity.IsOnline())
{
turretEntity.OfflineTick();
}
else if (!turretEntity.IsBeingControlled)
{
if (!turretEntity.HasTarget())
{
turretEntity.IdleTick(timeTick);
}
else
{
TargetTick();
}
}
turretEntity.UpdateFacingToTarget(timeTick);
if (turretEntity.totalAmmoDirty && Time.time > turretEntity.nextAmmoCheckTime)
{
turretEntity.UpdateTotalAmmo();
turretEntity.totalAmmoDirty = false;
turretEntity.nextAmmoCheckTime = Time.time + 0.5f;
}
}
private void TargetTick()
{
if (Time.realtimeSinceStartup >= turretEntity.nextVisCheck)
{
turretEntity.nextVisCheck = Time.realtimeSinceStartup + UnityEngine.Random.Range(0.2f, 0.3f);
turretEntity.targetVisible = turretEntity.ObjectVisible(turretEntity.target);
if (turretEntity.targetVisible)
{
turretEntity.lastTargetSeenTime = UnityEngine.Time.realtimeSinceStartup;
}
}
turretEntity.EnsureReloaded();
BaseProjectile weapon = turretEntity.GetAttachedWeapon();
if (
Time.time >= turretEntity.nextShotTime
&& turretEntity.targetVisible
&& Mathf.Abs(turretEntity.AngleToTarget(turretEntity.target, turretEntity.currentAmmoGravity != 0f)) < turretEntity.GetMaxAngleForEngagement()
)
{
if (weapon)
{
if (weapon.primaryMagazine.contents > 0)
{
turretEntity.FireAttachedGun(turretEntity.AimOffset(turretEntity.target), turretEntity.aimCone, null, turretEntity.PeacekeeperMode() ? turretEntity.target : null);
float delay = weapon.isSemiAuto ? weapon.repeatDelay * 1.5f : weapon.repeatDelay;
delay = weapon.ScaleRepeatDelay(delay);
turretEntity.nextShotTime = Time.time + delay;
}
else
{
turretEntity.nextShotTime = Time.time + 5f;
}
}
else if (turretEntity.HasFallbackWeapon())
{
turretEntity.FireGun(turretEntity.AimOffset(turretEntity.target), turretEntity.aimCone, null, turretEntity.target);
turretEntity.nextShotTime = Time.time + 0.115f;
}
else if (turretEntity.HasGenericFireable())
{
turretEntity.AttachedWeapon.ServerUse();
turretEntity.nextShotTime = Time.time + 0.115f;
}
else
{
turretEntity.nextShotTime = Time.time + 1f;
}
}
var targetPlayer = turretEntity.target as BasePlayer;
if (
turretEntity.target != null
&& (
!IsRealPlayer(targetPlayer)
|| turretEntity.target.IsDead()
|| Time.realtimeSinceStartup - turretEntity.lastTargetSeenTime > 3f
|| Vector3.Distance(turretEntity.transform.position, turretEntity.target.transform.position) > turretEntity.sightRange
|| (turretEntity.PeacekeeperMode() && !turretEntity.IsEntityHostile(turretEntity.target))
)
)
turretEntity.SetTarget(null);
}
public static bool IsRealPlayer(BasePlayer player) => player != null && player.userID.IsSteamId();
}
#endregion
#region Configuration
private class ConfigData
{
[JsonProperty(PropertyName = "Dungeon Spawn Settings")]
public DungeonSpawnSettings DungeonSpawn { get; set; } = new DungeonSpawnSettings();
[JsonProperty(PropertyName = "Auto Spawn Settings")]
public AutoSpawnSettings AutoSpawn { get; set; } = new AutoSpawnSettings();
[JsonProperty(PropertyName = "Loot Box Config")]
public LootBoxConfig LootBoxConfig { get; set; } = new LootBoxConfig();
[JsonProperty(PropertyName = "Tiers")]
public DungeonTiers Tiers { get; set; } = new DungeonTiers();
[JsonProperty(PropertyName = "Version")]
public string Version { get; set; } = "1.1.1";
}
private class DungeonTiers
{
[JsonProperty(PropertyName = "Easy")]
public DungeonTierConfig Easy { get; set; } =
new DungeonTierConfig
{
Enabled = true,
TotalLootBoxes = 2,
NpcSpawnConfigs = new List<NpcSpawnConfig>
{
new NpcSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_full_pistol.prefab", Total = 1 },
},
AutoTurretConfig = new TurretConfig
{
Total = 1,
Health = 300,
WeaponShortName = "pistol.revolver",
},
};
[JsonProperty(PropertyName = "Normal")]
public DungeonTierConfig Normal { get; set; } =
new DungeonTierConfig
{
Enabled = true,
TotalLootBoxes = 3,
NpcSpawnConfigs = new List<NpcSpawnConfig>
{
new NpcSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_full_shotgun.prefab", Total = 3 },
},
AutoTurretConfig = new TurretConfig
{
Total = 2,
Health = 600,
WeaponShortName = "smg.2",
},
};
[JsonProperty(PropertyName = "Medium")]
public DungeonTierConfig Medium { get; set; } =
new DungeonTierConfig
{
Enabled = true,
TotalLootBoxes = 4,
NpcSpawnConfigs = new List<NpcSpawnConfig>
{
new NpcSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/banditguard/npc_bandit_guard.prefab", Total = 4 },
},
AutoTurretConfig = new TurretConfig
{
Total = 3,
Health = 1000,
WeaponShortName = "smg.mp5",
},
};
[JsonProperty(PropertyName = "Hard")]
public DungeonTierConfig Hard { get; set; } =
new DungeonTierConfig
{
Enabled = true,
TotalLootBoxes = 5,
NpcSpawnConfigs = new List<NpcSpawnConfig>
{
new NpcSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_cargo_turret_lr300.prefab", Total = 6 },
},
AutoTurretConfig = new TurretConfig
{
Total = 4,
Health = 1500,
WeaponShortName = "rifle.ak",
},
};
[JsonProperty(PropertyName = "Nightmare")]
public DungeonTierConfig Nightmare { get; set; } =
new DungeonTierConfig
{
Enabled = true,
TotalLootBoxes = 6,
NpcSpawnConfigs = new List<NpcSpawnConfig>
{
new NpcSpawnConfig { PrefabName = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc_ch47_gunner.prefab", Total = 8 },
},
AutoTurretConfig = new TurretConfig
{
Total = 5,
Health = 2000,
WeaponShortName = "rifle.ak",
},
};
}
private class DungeonTierConfig
{
[JsonProperty(PropertyName = "Enabled")]
public bool Enabled { get; set; }
[JsonProperty(PropertyName = "Total Loot Boxes")]
public int TotalLootBoxes { get; set; }
[JsonProperty(PropertyName = "NPC Spawn Configs", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<NpcSpawnConfig> NpcSpawnConfigs { get; set; }
[JsonProperty(PropertyName = "Auto Turret Config")]
public TurretConfig AutoTurretConfig { get; set; } = new TurretConfig();
}
public class LootBoxConfig
{
[JsonProperty(PropertyName = "Small Wood Box Skin ID")]
public ulong SmallWoodBoxSkinID { get; set; } = 2998755525;
[JsonProperty(PropertyName = "Max Different Items Per Box")]
public int MaxDifferentItemsPerBox { get; set; } = 6;
[JsonProperty(PropertyName = "Loot Items", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<ItemConfig> LootItems { get; set; } =
new List<ItemConfig>
{
new ItemConfig
{
ShortName = "ammo.rifle",
InclusionChancePercentage = 15,
MinimumAmount = 100,
MaximumAmount = 300,
},
};
}
private class AutoSpawnSettings
{
[JsonProperty(PropertyName = "Enable Auto Spawn")]
public bool EnableAutoSpawn { get; set; } = false;
[JsonProperty(PropertyName = "Auto Spawn Interval Minutes")]
public float AutoSpawnIntervalMinutes { get; set; } = 30f;
[JsonProperty(PropertyName = "Minimum Search Radius For Dungeon Position")]
public float MinimumSearchRadius { get; set; } = 20f;
[JsonProperty(PropertyName = "Maximum Search Radius For Dungeon Position")]
public float MaximumSearchRadius { get; set; } = 50f;
[JsonProperty(PropertyName = "Max Spawn Attempts")]
public int MaxSpawnAttempts { get; set; } = 20;
[JsonProperty(PropertyName = "Nearby Entities Avoidance Radius")]
public float NearbyEntitiesAvoidanceRadius { get; set; } = 6f;
[JsonProperty(PropertyName = "Rocks Avoidance Radius")]
public float RocksAvoidanceRadius { get; set; } = 5f;
[JsonProperty(PropertyName = "Distance From No Build Zones")]
public float DistanceFromNoBuildZones { get; set; } = 10f;
}
private class NpcSpawnConfig
{
[JsonProperty(PropertyName = "Prefab")]
public string PrefabName { get; set; }
[JsonProperty(PropertyName = "Total")]
public int Total { get; set; }
}
private class TurretConfig
{
[JsonProperty(PropertyName = "Total")]
public int Total { get; set; } = 1;
[JsonProperty(PropertyName = "Health")]
public float Health { get; set; } = 1000f;
[JsonProperty(PropertyName = "Weapon Short Name")]
public string WeaponShortName { get; set; } = "rifle.ak";
[JsonProperty(PropertyName = "Clip Ammo")]
public ItemConfig ClipAmmo { get; set; } =
new ItemConfig
{
ShortName = "ammo.rifle",
MinimumAmount = 30,
MaximumAmount = 30,
};
[JsonProperty(PropertyName = "Reserve Ammo", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<ItemConfig> ReserveAmmo { get; set; } =
new List<ItemConfig>
{
new ItemConfig
{
ShortName = "ammo.rifle",
MinimumAmount = 128,
MaximumAmount = 128,
},
};
[JsonProperty(PropertyName = "Attachment Short Names", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<string> AttachmentShortNames { get; set; } = new List<string> { "weapon.mod.lasersight" };
}
private class DungeonSpawnSettings
{
[JsonProperty(PropertyName = "Enable Xmas Dungeon")]
public bool EnableXmasDungeon { get; set; } = true;
[JsonProperty(PropertyName = "Enable Halloween Dungeon")]
public bool EnableHalloweenDungeon { get; set; } = false;
}
public class ItemConfig
{
[JsonProperty(PropertyName = "ShortName")]
public string ShortName { get; set; }
[JsonProperty(PropertyName = "Inclusion Chance Percentage")]
public float InclusionChancePercentage { get; set; }
[JsonProperty(PropertyName = "Minimum Amount")]
public int MinimumAmount { get; set; }
[JsonProperty(PropertyName = "Maximum Amount")]
public int MaximumAmount { get; set; }
}
protected override void LoadConfig()
{
base.LoadConfig();
try
{
_configData = Config.ReadObject<ConfigData>();
if (_configData == null)
{
LoadDefaultConfig();
}
}
catch (Exception ex)
{
PrintError($"The configuration file is corrupted: {ex}");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void LoadDefaultConfig()
{
PrintWarning("Creating a new configuration file");
_configData = new ConfigData();
}
protected override void SaveConfig()
{
Config.WriteObject(_configData, true);
}
#endregion
}
}
ASSISTANT
This script is a Rust plugin, developed using the Oxide modding framework for the game Rust. It introduces a dynamic dungeon system, with configurable spawn rates, difficulty tiers, and loot distribution. Here's a breakdown of its primary components: 1. **Initialization and Configurations**: - The script initializes configurations and if enabled, sets a timer for automatic dungeon spawns. The configuration supports festive dungeons (Xmas and Halloween), defining which dungeons can be spawned. 2. **Dungeon Management**: - Dungeons can be spawned either automatically at intervals or manually through chat commands. - Existing dungeons are removed at initialization to prevent duplication. The plugin verifies for valid positions to place the dungeon, avoiding problematic locations like rocks, water, roads, or no-build zones. 3. **Dungeon Features**: - The script maintains a list of spawned entities such as NPCs and auto-turrets. It specifies different tiers of dungeons, each tier with unique enemy configurations and loot distributions. - Turrets are added with specific configurations including health, weapons, and ammo setup. - Storage boxes are randomly populated with items based on the specified loot configuration. 4. **Utility Functions**: - Commons checks ensure that dungeons are spawned in appropriate locations, such as avoiding rocks or in-water spawns. - Helper functions for logistical tasks like finding valid spawn points and ensuring entities are destroyed when no longer needed. 5. **Custom Extensions**: - The script demonstrates advanced Rust game modding capabilities using Oxide, extending base functionalities with custom behaviors like targeting systems for turrets. 6. **Configuration Management**: - The plugin reads from and writes its configuration data to a JSON file. The configurations allow for a detailed setup of each dungeon tier, including entities and loot settings. - When default configs need to be set or errors occur during loading, appropriate logging messages inform the server admin. This plugin provides a comprehensive expansion to Rust's standard gameplay by generating dungeons dynamically, providing players with new challenges and loot opportunities, and allowing server administrators significant customization over the dungeon experience.