C#:
using System.Collections.Generic;
using Network;
using UnityEngine;
namespace Oxide.Plugins;
[Info("FLootRespawn", "fufeldev", "1.0.0")]
public class FLootRespawn : RustPlugin
{
private const float RespawnDelay = 30f;
private const float OreRespawnDelay = 180f;
private const float TreeRespawnDelay = 120f;
private const float AnimalRespawnDelay = 240f;
private static readonly string[] BarrelPrefabs =
{
"assets/bundled/prefabs/radtown/loot_barrel_1.prefab",
"assets/bundled/prefabs/radtown/loot_barrel_2.prefab",
"assets/bundled/prefabs/radtown/oil_barrel.prefab",
"assets/bundled/prefabs/autospawn/resource/loot/loot-barrel-1.prefab",
"assets/bundled/prefabs/autospawn/resource/loot/loot-barrel-2.prefab",
"assets/prefabs/missions/tutorialisland/loot-barrel-tutorial.prefab"
};
private static readonly string[] RoadCratePrefabs =
{
"assets/bundled/prefabs/radtown/crate_basic.prefab",
"assets/bundled/prefabs/radtown/crate_basic_jungle.prefab",
"assets/bundled/prefabs/radtown/crate_normal.prefab",
"assets/bundled/prefabs/radtown/crate_normal_2.prefab",
"assets/bundled/prefabs/radtown/crate_normal_2_food.prefab",
"assets/bundled/prefabs/radtown/crate_normal_2_medical.prefab",
"assets/bundled/prefabs/radtown/crate_tools.prefab",
"assets/bundled/prefabs/radtown/crate_mine.prefab",
"assets/bundled/prefabs/radtown/crate_shore.prefab",
"assets/bundled/prefabs/radtown/crate_cannons.prefab",
"assets/bundled/prefabs/radtown/crate_elite.prefab",
"assets/bundled/prefabs/radtown/underwater_labs/crate_elite.prefab"
};
private readonly HashSet<NetworkableId> _processing = new HashSet<NetworkableId>();
private readonly HashSet<NetworkableId> _opened = new HashSet<NetworkableId>();
private readonly HashSet<NetworkableId> _looted = new HashSet<NetworkableId>();
private readonly HashSet<uint> _barrelPrefabs = new HashSet<uint>();
private readonly HashSet<uint> _roadCratePrefabs = new HashSet<uint>();
private readonly HashSet<string> _roadCrateNames = new HashSet<string>
{
"crate_basic",
"crate_basic_jungle",
"crate_normal",
"crate_normal_2",
"crate_normal_2_food",
"crate_normal_2_medical",
"crate_tools",
"crate_mine",
"crate_shore",
"crate_cannons",
"crate_elite"
};
private void OnServerInitialized()
{
_barrelPrefabs.Clear();
foreach (string prefab in BarrelPrefabs)
_barrelPrefabs.Add(StringPool.Get(prefab));
_roadCratePrefabs.Clear();
foreach (string prefab in RoadCratePrefabs)
_roadCratePrefabs.Add(StringPool.Get(prefab));
}
private void Unload()
{
_processing.Clear();
_opened.Clear();
_looted.Clear();
_barrelPrefabs.Clear();
_roadCratePrefabs.Clear();
}
private void OnLootEntity(BasePlayer player, BaseEntity entity)
{
LootContainer container = entity as LootContainer;
if (container?.net != null)
_opened.Add(container.net.ID);
}
private void OnItemRemovedFromContainer(ItemContainer container, Item item)
{
LootContainer loot = container?.entityOwner as LootContainer;
if (loot?.net == null || loot.inventory != container || !_opened.Contains(loot.net.ID)) return;
MarkLooted(loot);
}
private void OnItemSplit(Item item, int amount)
{
LootContainer loot = item?.parent?.entityOwner as LootContainer;
if (loot?.net == null || !_opened.Contains(loot.net.ID)) return;
MarkLooted(loot);
}
private void OnLootEntityEnd(BasePlayer player, StorageContainer container)
{
LootContainer loot = container as LootContainer;
if (loot?.net == null) return;
_opened.Remove(loot.net.ID);
if (IsRoadCrate(loot))
{
_looted.Remove(loot.net.ID);
DropRemainingRoadCrateLoot(player, loot);
ReplaceLootContainer(loot, true);
return;
}
if (!_looted.Remove(loot.net.ID) && !loot.HasBeenLooted) return;
ReplaceLootContainer(loot, true);
}
private void OnEntityDeath(LootContainer loot, HitInfo info)
{
if (loot?.net == null || info?.InitiatorPlayer == null || !_barrelPrefabs.Contains(loot.prefabID)) return;
ReplaceEntity(loot, RespawnDelay, false);
}
private void OnEntityDeath(ResourceEntity entity, HitInfo info)
{
if (entity?.net == null || info?.InitiatorPlayer == null) return;
if (entity is OreResourceEntity)
{
ReplaceEntity(entity, OreRespawnDelay, false);
return;
}
if (entity is TreeEntity)
ReplaceEntity(entity, TreeRespawnDelay, false);
else
ReplaceEntity(entity, OreRespawnDelay, false);
}
private void OnEntityDeath(BaseAnimalNPC animal, HitInfo info)
{
if (animal?.net == null || info?.InitiatorPlayer == null) return;
ReplaceEntity(animal, AnimalRespawnDelay, false);
}
[ConsoleCommand("floot.respawncrates")]
private void CmdRespawnCrates(ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Player();
if (player != null && !player.IsAdmin) return;
int restored;
int count = RespawnAllRoadCrates(out restored);
SendReply(arg, $"Пересоздано ящиков: {count}. Восстановлено пустых спавнеров: {restored}");
}
private void MarkLooted(LootContainer loot)
{
if (loot.net != null && !_processing.Contains(loot.net.ID))
_looted.Add(loot.net.ID);
}
private void ReplaceLootContainer(LootContainer loot, bool killOld)
{
ReplaceEntity(loot, RespawnDelay, killOld);
}
private void DropRemainingRoadCrateLoot(BasePlayer player, LootContainer loot)
{
if (!IsRoadCrateWithLoot(loot)) return;
loot.DropItems(player);
}
private bool IsRoadCrateWithLoot(LootContainer loot)
{
return IsRoadCrate(loot) && loot.inventory?.itemList != null && loot.inventory.itemList.Count > 0;
}
private bool IsRoadCrate(LootContainer loot)
{
return loot != null && (_roadCratePrefabs.Contains(loot.prefabID) || _roadCrateNames.Contains(loot.ShortPrefabName));
}
private int RespawnAllRoadCrates(out int restored)
{
List<LootContainer> crates = Facepunch.Pool.Get<List<LootContainer>>();
foreach (BaseNetworkable networkable in BaseNetworkable.serverEntities)
{
LootContainer loot = networkable as LootContainer;
if (IsRoadCrate(loot) && loot.net != null && !loot.IsDestroyed)
crates.Add(loot);
}
restored = RestoreEmptyRoadCrateSpawners();
int count = crates.Count;
for (int i = 0; i < crates.Count; i++)
RespawnLootContainerNow(crates[i]);
Facepunch.Pool.FreeUnmanaged(ref crates);
return count;
}
private int RestoreEmptyRoadCrateSpawners()
{
SpawnHandler handler = SingletonComponent<SpawnHandler>.Instance;
if (handler == null) return 0;
int count = 0;
for (int i = 0; i < handler.SpawnGroups.Count; i++)
{
ISpawnGroup spawnGroup = handler.SpawnGroups[i];
if (spawnGroup == null || !IsRoadCrateSpawner(spawnGroup)) continue;
ClearDeadSpawnerInstances(spawnGroup);
if (NeedsSpawnerFill(spawnGroup))
{
spawnGroup.Fill();
count++;
}
}
return count;
}
private bool NeedsSpawnerFill(ISpawnGroup spawnGroup)
{
SpawnGroup group = spawnGroup as SpawnGroup;
if (group != null) return group.currentPopulation < group.maxPopulation;
IndividualSpawner individual = spawnGroup as IndividualSpawner;
return individual != null && individual.currentPopulation <= 0;
}
private void ClearDeadSpawnerInstances(ISpawnGroup spawnGroup)
{
SpawnGroup group = spawnGroup as SpawnGroup;
if (group?.spawnInstances != null)
{
for (int i = group.spawnInstances.Count - 1; i >= 0; i--)
{
SpawnPointInstance instance = group.spawnInstances[i];
BaseEntity entity = instance == null ? null : instance.GetComponent<BaseEntity>();
if (entity == null || entity.IsDestroyed)
group.spawnInstances.RemoveAt(i);
}
}
IndividualSpawner individual = spawnGroup as IndividualSpawner;
if (individual?.spawnInstance != null)
{
BaseEntity entity = individual.spawnInstance.GetComponent<BaseEntity>();
if (entity == null || entity.IsDestroyed)
individual.spawnInstance = null;
}
}
private bool IsRoadCrateSpawner(ISpawnGroup spawnGroup)
{
SpawnGroup group = spawnGroup as SpawnGroup;
if (group?.prefabs != null)
{
for (int i = 0; i < group.prefabs.Count; i++)
{
SpawnGroup.SpawnEntry entry = group.prefabs[i];
if (IsRoadCratePrefab(entry?.prefab?.resourcePath)) return true;
}
}
IndividualSpawner individual = spawnGroup as IndividualSpawner;
return IsRoadCratePrefab(individual?.entityPrefab?.resourcePath);
}
private bool IsRoadCratePrefab(string prefab)
{
if (string.IsNullOrEmpty(prefab)) return false;
prefab = prefab.ToLowerInvariant();
for (int i = 0; i < RoadCratePrefabs.Length; i++)
if (prefab == RoadCratePrefabs[i])
return true;
return false;
}
private void RespawnLootContainerNow(LootContainer loot)
{
if (loot?.net == null || !_processing.Add(loot.net.ID)) return;
NetworkableId id = loot.net.ID;
SpawnPointInstance instance = loot.GetComponent<SpawnPointInstance>();
ISpawnPointUser spawnUser = instance?.parentSpawnPointUser;
BaseSpawnPoint spawnPoint = instance?.parentSpawnPoint;
string prefabName = loot.PrefabName;
Vector3 position = loot.transform.position;
Quaternion rotation = loot.transform.rotation;
bool enableSaving = loot.enableSaving;
CloseLooters(loot);
loot.Kill();
NextTick(() =>
{
_processing.Remove(id);
_opened.Remove(id);
_looted.Remove(id);
SpawnDirect(prefabName, position, rotation, enableSaving, spawnUser, spawnPoint);
});
}
private void ReplaceEntity(BaseEntity entityToReplace, float delay, bool killOld)
{
if (entityToReplace.net == null || !_processing.Add(entityToReplace.net.ID)) return;
NetworkableId id = entityToReplace.net.ID;
SpawnPointInstance instance = entityToReplace.GetComponent<SpawnPointInstance>();
ISpawnPointUser spawnUser = instance?.parentSpawnPointUser;
BaseSpawnPoint spawnPoint = instance?.parentSpawnPoint;
string prefabName = entityToReplace.PrefabName;
Vector3 position = entityToReplace.transform.position;
Quaternion rotation = entityToReplace.transform.rotation;
bool enableSaving = entityToReplace.enableSaving;
timer.Once(delay, () =>
{
if (killOld && entityToReplace != null && !entityToReplace.IsDestroyed)
{
CloseLooters(entityToReplace);
entityToReplace.Kill();
}
NextTick(() =>
{
_processing.Remove(id);
_opened.Remove(id);
_looted.Remove(id);
if (killOld && SpawnDirect(prefabName, position, rotation, enableSaving, spawnUser, spawnPoint))
return;
if (spawnUser is SpawnGroup group)
{
group.Spawn();
return;
}
if (spawnUser is IndividualSpawner individual)
{
individual.TrySpawnEntity();
return;
}
BaseEntity entity = GameManager.server.CreateEntity(prefabName, position, rotation);
if (entity == null) return;
entity.enableSaving = enableSaving;
entity.Spawn();
});
});
}
private bool SpawnDirect(string prefabName, Vector3 position, Quaternion rotation, bool enableSaving, ISpawnPointUser spawnUser, BaseSpawnPoint spawnPoint)
{
BaseEntity entity = GameManager.server.CreateEntity(prefabName, position, rotation);
if (entity == null) return false;
entity.enableSaving = enableSaving;
entity.Spawn();
if (spawnUser != null || spawnPoint != null)
{
SpawnPointInstance newInstance = entity.gameObject.AddComponent<SpawnPointInstance>();
newInstance.parentSpawnPointUser = spawnUser;
newInstance.parentSpawnPoint = spawnPoint;
newInstance.Notify();
}
return true;
}
private void CloseLooters(BaseEntity entity)
{
foreach (BasePlayer player in BasePlayer.activePlayerList)
if (player?.inventory?.loot?.entitySource == entity)
player.inventory.loot.Clear();
}
}