C#:
// Plugin formatted by redBDGR's Plugin Formatting Tool
using ConVar;
using Network;
using Oxide.Core.Plugins;
using Rust;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Oxide.Plugins {
[Info("RadStorm", "redBDGR", "1.0.8")]
[Description("Dynamic radiation storm events")]
class RadStorm : RustPlugin {
private static RadStorm plugin;
private bool Changed;
[PluginReference]
Plugin GUIAnnouncements;
private const string permissionNameADMIN = "radstorm.admin";
private const string permissionNameEXEMPT = "radstorm.exempt";
private float stormLength = 120f;
private float radiationAmount = 50f;
private bool hurtSleepers = true;
private float minStormTime = 3600;
private float maxStormTime = 7200;
private bool randomEventEnabled = true;
private bool deathAnnouncementsEnabled = true;
private float stormChargeTime = 100;
private bool scaleRadiationBasedOnClothing;
private bool useChatAnnouncements = true;
private bool useGUIAnnouncements = false;
private string GUIBannerColour = "Grey";
private string GUITextColour = "Yellow";
private bool announceDeaths = true;
private float weatherRainAmount = 1f;
private float weatherFogAmount = 1f;
private float weatherWindAmount = 1f;
private float gatherRateBonus = 1f;
private float pickupRateBonus = 1f;
private float bonusRateBonus = 1f;
RadiationStorm storm = null;
protected override void LoadDefaultConfig() {
Config.Clear();
LoadVariables();
}
private void LoadVariables() {
stormLength = Convert.ToSingle(GetConfig("Storm Settings", "Storm Length", 300f));
radiationAmount = Convert.ToSingle(GetConfig("Storm Settings", "Radiation Amount", 50f));
hurtSleepers = Convert.ToBoolean(GetConfig("Storm Settings", "Hurt Sleepers", true));
minStormTime = Convert.ToSingle(GetConfig("Storm Settings", "Min Storm Inbound Time", 3600f));
maxStormTime = Convert.ToSingle(GetConfig("Storm Settings", "Max Storm Inbound Time", 7200f));
randomEventEnabled = Convert.ToBoolean(GetConfig("Storm Settings", "Random Event Enabled", true));
deathAnnouncementsEnabled = Convert.ToBoolean(GetConfig("Storm Settings", "Death Announcements Enabled", true));
stormChargeTime = Convert.ToSingle(GetConfig("Storm Settings", "Storm Buildup Time", 100f));
scaleRadiationBasedOnClothing = Convert.ToBoolean(GetConfig("Storm Settings", "Scale Radiation Based On Radiation Protection", true));
useChatAnnouncements = Convert.ToBoolean(GetConfig("Notification Settings", "Use chat announcements", true));
useGUIAnnouncements = Convert.ToBoolean(GetConfig("Notification Settings", "Use GUI announcements", false));
GUIBannerColour = Convert.ToString(GetConfig("Notification Settings", "GUIAnnouncements Banner Colour", "Grey"));
GUITextColour = Convert.ToString(GetConfig("Notification Settings", "GUIAnnouncements Text Colour", "Yellow"));
weatherRainAmount = Convert.ToSingle(GetConfig("Storm Settings", "Maximum rain amount", 1f));
weatherFogAmount = Convert.ToSingle(GetConfig("Storm Settings", "Maximum fog amount", 1f));
weatherWindAmount = Convert.ToSingle(GetConfig("Storm Settings", "Maximum wind amount", 1f));
gatherRateBonus = Convert.ToSingle(GetConfig("Bonus Settings", "Gather Rate Bonus", 1f));
pickupRateBonus = Convert.ToSingle(GetConfig("Bonus Settings", "Collectibles Rate Bonus", 1f));
bonusRateBonus = Convert.ToSingle(GetConfig("Bonus Settings", "Gather Finish Bonus Rate Bonus", 1f));
if (!Changed) return;
SaveConfig();
Changed = false;
}
private void LoadLang() {
lang.RegisterMessages(new Dictionary<string, string> {
["Killed by storm"]
= "{0} got caught in the storm", ["RadStorm building"] = "A radiation storm is building!", ["RadStorm fullstrength"] = "The storm is at full strength...Players stuck outside will be riddled with radiation", ["RadStorm receding"] = "The radiation storm is receeding", ["RadStorm receded"] = "The radiation storm has receded", ["5 minute warning"] = "Weather forcasts have predicted a radiation storm.Its ETA is 5 minutes", ["60 second warning"] = "A radiation storm will be hitting the island in 60 seconds, make sure you're inside or wearing a hazmat suit!",
}
, this);
}
private void Init() {
plugin = this;
permission.RegisterPermission(permissionNameADMIN, this);
permission.RegisterPermission(permissionNameEXEMPT, this);
LoadVariables();
LoadLang();
if (randomEventEnabled) if (maxStormTime > 0) RandomStorm();
}
private void Loaded() {
if (!GUIAnnouncements) {
PrintError("GUIAnnouncements.cs was not found...disabling features");
useGUIAnnouncements = false;
}
}
private void Unload() {
if (storm) storm.DestroyThis();
}
private object OnPlayerDie(BasePlayer player, HitInfo info) {
if (!deathAnnouncementsEnabled) return null;
if (player == null || info == null || info.damageTypes == null) return null;
if (!storm) return null;
if (info.damageTypes.GetMajorityDamageType() != DamageType.Radiation) return null;
rust.BroadcastChat(string.Format(msg("Killed by storm"), player.displayName));
return null;
}
private void OnPlayerMetabolize(PlayerMetabolism metabolism, BaseCombatEntity entity, float delta) {
if (storm == null) return;
if (storm.isFullStrength == false) return;
BasePlayer player = entity.GetComponent<BasePlayer>();
if (player == null) return;
if (!storm.outsidePlayerCache.Contains(player.UserIDString)) return;
float radAmount = radiationAmount;
if (scaleRadiationBasedOnClothing) radAmount = Mathf.Clamp(radAmount - player.RadiationProtection(), 0f, radAmount);
metabolism.radiation_level.MoveTowards(radAmount, 0.2f);
metabolism.radiation_poison.MoveTowards(radAmount, 0.2f);
}
private object OnCollectiblePickup(Item item, BasePlayer player) {
if (!storm) return null;
item.amount = Mathf.RoundToInt(item.amount * pickupRateBonus);
return null;
}
private object OnDispenserBonus(ResourceDispenser dispenser, BasePlayer player, Item item) {
if (!storm) return null;
item.amount = Mathf.RoundToInt(item.amount * bonusRateBonus);
return null;
}
private object OnDispenserGather(ResourceDispenser dispenser, BaseEntity entity, Item item) {
if (!storm) return null;
item.amount = Mathf.RoundToInt(item.amount * gatherRateBonus);
return null;
}
private class RadiationStorm : FacepunchBehaviour {
public GameObject parentObj;
public List<string> outsidePlayerCache = new List<string>();
private bool radiationWasFalse = false;
public bool isFullStrength = false;
private float originalRainAmount = 0f;
private float originalFogAmount = 0f;
private float originalWindAmount = 0f;
public float rainAmount = 0f;
private float fogAmount = 0f;
private float windAmount = 0f;
private float maxRain = plugin.weatherRainAmount;
private float maxFog = plugin.weatherFogAmount;
private float maxWind = plugin.weatherWindAmount;
Coroutine onlineDamage;
Coroutine offlineDamage;
private void Awake() {
parentObj = gameObject;
rainAmount = Climate.Instance.Overrides.Rain;
if (rainAmount < 0) {
originalRainAmount = -1f;
rainAmount = 0f;
}
else originalFogAmount = rainAmount;
fogAmount = Climate.Instance.Overrides.Fog;
if (fogAmount < 0) {
originalFogAmount = -1f;
fogAmount = 0f;
}
else originalFogAmount = fogAmount;
windAmount = Climate.Instance.Overrides.Wind;
if (windAmount < 0) {
originalWindAmount = -1f;
windAmount = 0f;
}
else originalWindAmount = windAmount;
StartStorm();
}
private void OnDestroy() {
if (radiationWasFalse) ConVar.Server.radiation = false;
}
public void DestroyThis() {
CancelInvoke(StormCycle);
CancelInvoke(IncreaseStormSeverity);
CancelInvoke(DecreaseStormSeverity);
StopCoroutine(onlineDamage);
StopCoroutine(offlineDamage);
ConsoleSystem.Run(ConsoleSystem.Option.Server.Quiet(), $"weather.rain {originalRainAmount}");
ConsoleSystem.Run(ConsoleSystem.Option.Server.Quiet(), $"weather.fog {originalFogAmount}");
ConsoleSystem.Run(ConsoleSystem.Option.Server.Quiet(), $"weather.wind {originalWindAmount}");
Destroy(parentObj);
}
public void StartStorm() {
plugin.SendNotificaion(plugin.msg("RadStorm building"));
float buildTimePeriod = plugin.stormChargeTime / 20.0f;
if (ConVar.Server.radiation == false) {
ConVar.Server.radiation = true;
radiationWasFalse = true;
}
InvokeRepeating(IncreaseStormSeverity, buildTimePeriod, buildTimePeriod);
Invoke(StormCycle, 5f);
}
public void StopStorm() {
StopCoroutine(offlineDamage);
StopCoroutine(onlineDamage);
CancelInvoke(IncreaseStormSeverity);
InvokeRepeating(DecreaseStormSeverity, 5f, 5f);
if (radiationWasFalse) {
ConVar.Server.radiation = false;
}
}
private void StormCycle() {
onlineDamage = StartCoroutine(CacheOutdoorPlayers());
if (plugin.hurtSleepers) offlineDamage = StartCoroutine(DamageOfflinePlayers());
}
private void IncreaseStormSeverity() {
if (rainAmount < maxRain) rainAmount += 0.05f * maxRain;
if (fogAmount < maxFog) fogAmount += 0.05f * maxFog;
if (windAmount < maxWind) windAmount += 0.05f * maxWind;
ConsoleSystem.Run(ConsoleSystem.Option.Server.Quiet(), $"weather.rain {rainAmount}");
ConsoleSystem.Run(ConsoleSystem.Option.Server.Quiet(), $"weather.fog {fogAmount}");
ConsoleSystem.Run(ConsoleSystem.Option.Server.Quiet(), $"weather.wind {windAmount}");
if (rainAmount >= maxRain && fogAmount >= maxFog && windAmount >= maxWind) {
rainAmount = maxRain;
fogAmount = maxFog;
windAmount = maxWind;
plugin.SendNotificaion(plugin.msg("RadStorm fullstrength"));
this.isFullStrength = true;
InvokeRepeating(DecreaseStormSeverity, plugin.stormLength, 5f);
CancelInvoke(IncreaseStormSeverity);
}
}
private void DecreaseStormSeverity() {
if (rainAmount == maxRain) {
plugin.SendNotificaion(plugin.msg("RadStorm receding"));
this.isFullStrength = false;
}
if (rainAmount > 0f) rainAmount -= 0.05f * maxRain;
if (fogAmount > 0f) fogAmount -= 0.05f * maxFog;
if (windAmount > 0f) windAmount -= 0.05f * maxWind;
ConsoleSystem.Run(ConsoleSystem.Option.Server.Quiet(), $"weather.rain {rainAmount}");
ConsoleSystem.Run(ConsoleSystem.Option.Server.Quiet(), $"weather.fog {fogAmount}");
ConsoleSystem.Run(ConsoleSystem.Option.Server.Quiet(), $"weather.wind {windAmount}");
if (rainAmount <= 0f && fogAmount <= 0f && windAmount <= 0f) {
plugin.SendNotificaion(plugin.msg("RadStorm receded"));
CancelInvoke(DecreaseStormSeverity);
DestroyThis();
}
}
private IEnumerator CacheOutdoorPlayers() {
while (true) {
yield return new WaitForSeconds(2f);
foreach (BasePlayer player in BasePlayer.activePlayerList) {
yield return new WaitForSeconds(0.1f);
/* if (TerrainMeta.BiomeMap.GetBiome(player.transform.position, 1) >= 0.5f) { UnCachePlayer(player.UserIDString); yield return new WaitForSeconds(0.1f); continue; } */
if (plugin.permission.UserHasPermission(player.UserIDString, permissionNameEXEMPT)) {
UnCachePlayer(player.UserIDString);
continue;
}
if (player.inventory.containerWear.FindItemsByItemName("hazmatsuit") != null) {
UnCachePlayer(player.UserIDString);
continue;
}
if (player.IsHeadUnderwater()) {
UnCachePlayer(player.UserIDString);
continue;
}
if (!player.IsOutside()) {
UnCachePlayer(player.UserIDString);
continue;
}
CachePlayer(player.UserIDString);
}
}
}
private IEnumerator DamageOfflinePlayers() {
while (true) {
yield return new WaitForSeconds(2f);
List<string> hurtList = new List<string>();
foreach (BasePlayer player in BasePlayer.sleepingPlayerList) {
/* if (TerrainMeta.BiomeMap.GetBiome(player.transform.position, 1) >= 0.5f) { UnCachePlayer(player.UserIDString); yield return new WaitForSeconds(0.1f); continue; } */
if (plugin.permission.UserHasPermission(player.UserIDString, permissionNameEXEMPT)) {
UnCachePlayer(player.UserIDString);
continue;
}
if (player.inventory.containerWear.FindItemsByItemName("hazmatsuit") != null) {
yield return new WaitForSeconds(0.05f);
continue;
}
if (!player.IsOutside()) {
yield return new WaitForSeconds(0.05f);
continue;
}
player.metabolism.radiation_level.value = 25f;
player.metabolism.radiation_poison.value = 25f;
player.UpdateRadiation(25f);
hurtList.Add(player.UserIDString);
yield return new WaitForSeconds(0.1f);
}
for (int i = 0; i < hurtList.Count; i++) {
BasePlayer player = BasePlayer.Find(hurtList[i]);
if (player == null) {
yield return new WaitForSeconds(0.05f);
continue;
}
player.Hurt(2.5f, DamageType.Radiation);
yield return new WaitForSeconds(0.05f);
}
}
}
private void UnCachePlayer(string id) {
if (outsidePlayerCache.Contains(id)) outsidePlayerCache.Remove(id);
}
private void CachePlayer(string id) {
if (!outsidePlayerCache.Contains(id)) outsidePlayerCache.Add(id);
}
}
[ChatCommand("radstorm")]
private void RadstormChatCMD(BasePlayer player, string command, string[] args) {
if (!permission.UserHasPermission(player.UserIDString, permissionNameADMIN)) {
player.ChatMessage("You are not allowed to use this comand");
return;
}
if (args.Length < 1) {
player.ChatMessage("Invalid Syntax! /radstorm <start/stop/forcestop>");
return;
}
switch (args[0]) {
case "start" : if (storm) storm.DestroyThis();
StartStorm();
break;
case "stop" : if (storm) storm.StopStorm();
break;
case "forcestop" : if (storm) storm.DestroyThis();
break;
default : player.ChatMessage("Invalid Syntax! /radstorm <start/stop/forcestop>");
break;
}
}
[ConsoleCommand("radstorm")]
private void RadStormCMD(ConsoleSystem.Arg arg) {
if (arg.Connection != null) {
BasePlayer player = arg.Player();
if (player) {
if (!permission.UserHasPermission(player.UserIDString, permissionNameADMIN)) return;
}
}
if (arg.Args.Length < 1) {
arg.ReplyWith("Invalid Syntax! radstorm <start/stop/forcestop>");
return;
}
switch (arg.Args[0]) {
case "start" : if (storm) storm.DestroyThis();
StartStorm();
break;
case "stop" : if (storm) storm.StopStorm();
break;
case "forcestop" : if (storm) storm.DestroyThis();
break;
default : arg.ReplyWith("Invalid Syntax! radstorm <start/stop/forcestop>");
break;
}
}
private void StartStorm() {
GameObject stormObj = new GameObject();
storm = stormObj.AddComponent<RadiationStorm>();
}
private void StopStorm() {
if (storm == null) return;
storm.StopStorm();
}
private void RandomStorm() {
float randomTime = UnityEngine.Random.Range(minStormTime, maxStormTime);
timer.Once(randomTime - 300f, () => {
SendNotificaion(msg("5 minute warning"));
timer.Once(240f, () => {
SendNotificaion(msg("60 second warning"));
timer.Once(60, () => {
StartStorm();
timer.Once(stormLength + 200f, () => {
RandomStorm();
}
);
}
);
}
);
}
);
}
private void SendNotificaion(string text, BasePlayer player = null) {
if (useChatAnnouncements) Server.Broadcast(text);
if (useGUIAnnouncements) {
if (player != null) GUIAnnouncements?.Call("CreateAnnouncement", text, GUIBannerColour, GUITextColour, player);
else foreach (BasePlayer _player in BasePlayer.activePlayerList) if (_player.IsConnected) GUIAnnouncements?.Call("CreateAnnouncement", text, GUIBannerColour, GUITextColour, _player);
}
}
private object GetConfig(string menu, string datavalue, object defaultValue) {
var data = Config[menu] as Dictionary<string, object>;
if (data == null) {
data = new Dictionary<string, object>();
Config[menu] = data;
Changed = true;
}
object value;
if (!data.TryGetValue(datavalue, out value)) {
value = defaultValue;
data[datavalue] = value;
Changed = true;
}
return value;
}
private string msg(string key, string id = null) => lang.GetMessage(key, this, id);
}
}
// If you wish to view this code in a formatted state, please contact redBDGR on discord @ redBDGR #0001