C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
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 UnityEngine;
namespace Oxide.Plugins {
[Info("Diseases", "mr01sam", "2.1.1")]
[Description("Players can be inflicted with disease that can be spread to others")]
public class Diseases : CovalencePlugin {
public static Diseases PLUGIN;
/* Permissions */
private const string PermissionUse = "diseases.use";
private const string PermissionAdmin = "diseases.admin";
/* Dependencies */
[PluginReference]
private Plugin ImageLibrary;
/* Constants */
private const float PLAYER_TICK_RATE = 1f;
private static bool debug = false;
/* Global data */
private DiseaseManager diseaseManager;
private EffectManager effectManager;
private Dictionary<string, DiseaseStats> diseaseStats;
#region Oxide Hooks
void Init() {
PLUGIN = this;
/* Unsubscribe */
Unsubscribe(nameof(OnEntityDeath));
Unsubscribe(nameof(OnPlayerSleep));
Unsubscribe(nameof(OnPlayerSleepEnded));
Unsubscribe(nameof(OnItemUse));
Unsubscribe(nameof(OnEntityTakeDamage));
/* Init */
InitCommands();
}
void Unload() {
/* Clear effects and UI */
foreach (BasePlayer player in BasePlayer.activePlayerList) {
effectManager.ClearAllEffects(player);
HideAllIndicators(player);
}
}
void OnServerInitialized(bool initial) {
/* Load images (if ImageLibrary is installed) */
LoadImages();
/* Register perms */
permission.RegisterPermission(PermissionUse, this);
/* Init */
diseaseStats = new Dictionary<string, DiseaseStats>();
diseaseManager = new DiseaseManager();
effectManager = new EffectManager();
/* Load disease files */
diseaseManager.LoadAllDiseases(() => {
foreach (BasePlayer player in BasePlayer.activePlayerList) RefreshIndicator(player);
}
);
/* Unsubscribe */
Subscribe(nameof(OnEntityDeath));
Subscribe(nameof(OnPlayerSleep));
Subscribe(nameof(OnPlayerSleepEnded));
Subscribe(nameof(OnItemUse));
Subscribe(nameof(OnEntityTakeDamage));
}
void OnEntityDeath(BasePlayer player, HitInfo info) {
if (player.IsValid()) {
foreach (string diseasename in diseaseManager.GetPlayerInfections(player)) {
Disease disease = diseaseManager.GetDisease(diseasename);
if (disease != null) {
bool killedByDisease = WasCauseOfDeath(player, info, disease);
if (killedByDisease) {
diseaseStats[disease.name].playersKilled.count += 1;
if (config.BroadcastDeathMessages) BasePlayer.activePlayerList.ToList().ForEach(x => x.ChatMessage(Lang("Death", player.UserIDString, player.displayName, Color(disease.name, config.DiseaseNameColor))));
}
if (debug) PLUGIN.Puts("Died: " + player.displayName + " removing of " + disease.name);
diseaseStats[disease.name].activeInfected.count -= 1;
diseaseManager.RemoveInfected(player, disease);
Interface.CallHook("OnInfectedDeath", player, disease, killedByDisease);
}
}
RefreshIndicator(player);
}
}
object OnPlayerSleep(BasePlayer player) {
if (player.IsValid()) {
foreach (string diseasename in diseaseManager.GetPlayerInfections(player)) {
diseaseStats[diseasename].sleepersInfected.count += 1;
diseaseStats[diseasename].activeInfected.count -= 1;
}
}
return null;
}
void OnPlayerSleepEnded(BasePlayer player) {
if (debug) PLUGIN.Puts("Sleep ended for " + player.displayName);
if (player.IsValid()) {
if (debug) PLUGIN.Puts("Is valid: " + player.displayName);
foreach (string diseasename in diseaseManager.GetPlayerInfections(player)) {
Disease disease = diseaseManager.GetDisease(diseasename);
if (debug) PLUGIN.Puts("Has active tick: " + diseaseManager.HasActiveTick(player, disease));
if (disease != null && diseaseManager.HasActiveTick(player, disease)) {
if (diseaseManager.HasImmunity(player, disease)) {
if (debug) PLUGIN.Puts("Resuming recovery for " + player.displayName + " with " + disease.name);
DoRecoverTick(player, disease);
}
else {
if (debug) PLUGIN.Puts("Resuming infection for " + player.displayName + " with " + disease.name);
DoInfectedTick(player, disease);
}
diseaseStats[disease.name].sleepersInfected.count -= 1;
diseaseStats[disease.name].activeInfected.count += 1;
}
}
RefreshIndicator(player);
}
}
void OnItemUse(Item item, int amountToUse) {
if (item == null) return;
BasePlayer player = item.GetOwnerPlayer();
if (!player.IsValid()) return;
foreach (Disease disease in diseaseManager.LoadedDiseases.Values) {
CureItem ci = diseaseManager.ItemsThatCure.Get(item.info.shortname, disease.name);
if (ci != null && ci.cureChanceOnConsumption >= UnityEngine.Random.Range(0, 100)) {
RecoverPlayer(player, disease, () => {
diseaseStats[disease.name].timesCuredFromItem.count += 1;
}
);
}
InfectionItem ii = diseaseManager.ItemsThatInfect.Get(item.info.shortname, disease.name);
if (ii != null && ii.infectionChanceOnConsumption >= UnityEngine.Random.Range(0, 100)) {
InfectPlayer(player, disease, () => {
diseaseStats[disease.name].infectedFromItem.count += 1;
}
);
}
TreatmentItem ti = diseaseManager.ItemsThatTreat.Get(item.info.shortname, disease.name);
if (ti != null) {
TreatPlayer(player, disease, ti.Value(), ti.treatmentDelay, () => {
diseaseStats[disease.name].timesTreatedFromItem.count += 1;
}
);
}
}
}
object OnEntityTakeDamage(BasePlayer player, HitInfo info) {
if (!player.IsValid() || info == null || info.Initiator == null) return null;
foreach (Disease disease in diseaseManager.LoadedDiseases.Values) {
if (debug) Puts(disease.name);
SpreaderEntity se = diseaseManager.EntitiesThatInfect.Get(info.Initiator.ShortPrefabName, disease.name);
if (se != null && (se.shortPrefabName != "player" || !info.IsProjectile()) && se.infectionChanceOnHit >= UnityEngine.Random.Range(0, 100)) {
if (debug) Puts("Should infect: " + player.displayName + " with " + disease.name);
InfectPlayer(player, disease, () => {
diseaseStats[disease.name].infectedFromEntity.count += 1;
}
);
}
}
return null;
}
#endregion
#region Helper Functions
private void LoadImages() {
if (ImageLibrary != null && ImageLibrary.IsLoaded) {
ImageLibrary.Call<bool>("AddImage", "https://i.imgur.com/BODiaSy.png", "DiseaseIndicatorIcon", 0UL);
}
}
private void DoDiseaseTick(Disease disease) {
if (disease.outbreakConditions.enabled) {
timer.In(disease.outbreakConditions.timeInterval, () => {
/* Attempt outbreak */
Outbreak(disease);
/* Continue */
DoDiseaseTick(disease);
}
);
}
}
private void DoInfectedTick(BasePlayer player, Disease disease) {
if (player.IsConnected && player.IsAlive() && player.IsValid() && !player.IsSleeping() && disease != null && diseaseManager.HasDisease(player, disease) && !diseaseManager.HasImmunity(player, disease)) {
/* Show symptoms */
if (debug) Puts("Showing symptom for: " + player.displayName);
DoSymptomDamage(player, disease);
DoSymptomEffects(player, disease);
DoSymptomScreenEffects(player, disease);
/* Spread to other players */
foreach (BasePlayer entity in NearbyValidPlayers(player.transform.position, disease)) {
if (GetSpreadChance(entity, disease) >= UnityEngine.Random.Range(1, 100)) {
if (debug) Puts("Spread " + disease.name + " from " + player.name + " to " + entity.displayName);
InfectPlayer(entity, disease, () => {
diseaseStats[disease.name].infectedFromSpreading.count += 1;
}
);
}
}
/* Continue timer */
int spreadTime = disease.SpreadTime();
timer.In(spreadTime, () => {
if (diseaseManager.UpdateInfectedTime(player, disease, spreadTime)) {
RecoverPlayer(player, disease);
}
else {
DoInfectedTick(player, disease);
}
}
);
}
else {
if (debug) Puts("Stopped infection tick for: " + player.displayName);
}
}
private void DoRecoverTick(BasePlayer player, Disease disease) {
if (player.IsConnected && player.IsAlive() && player.IsValid() && !player.IsSleeping()) {
diseaseManager.SetActiveTick(player, disease, true);
RefreshIndicator(player);
timer.In(disease.ImmunityTime(), () => {
if (player.IsConnected && player.IsAlive() && player.IsValid() && !player.IsSleeping()) {
CurePlayer(player, disease);
}
}
);
}
else {
if (debug) Puts("Stopped recovery tick for: " + player.displayName);
}
}
private List<BaseCombatEntity> NearbyValidPlayers(Vector3 position, Disease disease) {
List<BaseCombatEntity> valid = new List<BaseCombatEntity>();
List<BasePlayer> nearby = new List<BasePlayer>();
Vis.Entities(position, disease.spreadDistance, nearby);
foreach (BasePlayer entity in nearby) {
if (diseaseManager.CanInfect(entity, disease)) {
valid.Add(entity);
}
}
return valid;
}
private void InfectPlayer(BasePlayer player, Disease disease, Action callback = null) {
if (Interface.CallHook("OnPlayerInfected", player, disease) != null) return;
if (diseaseManager.AddInfected(player, disease)) {
if (debug) Puts("Started tick for: " + player.displayName);
diseaseManager.SetActiveTick(player, disease, true);
DoInfectedTick(player, disease);
RefreshIndicator(player);
StatusMessage(player, disease, Lang("StatusInfected", player.UserIDString, Color(disease.name, config.DiseaseNameColor)));
diseaseStats[disease.name].activeInfected.count += 1;
diseaseStats[disease.name].totalInfected.count += 1;
;
callback?.Invoke();
}
}
private void RecoverPlayer(BasePlayer player, Disease disease, Action callback = null) {
if (diseaseManager.HasDisease(player, disease) && diseaseManager.SetRecovery(player, disease)) {
if (debug) Puts("Started recovery for: " + player.displayName);
diseaseManager.SetActiveTick(player, disease, true);
DoRecoverTick(player, disease);
RefreshIndicator(player);
StatusMessage(player, disease, Lang("StatusRecovered", player.UserIDString, Color(disease.name, config.DiseaseNameColor)));
callback?.Invoke();
}
}
private int Outbreak(Disease disease, int tries = 1) {
if (Interface.CallHook("OnOutbreak", disease) != null) return 0;
int infected = 0;
if (debug) Puts("Attempting outbreak for " + disease.name);
List<BasePlayer> activePlayerList = BasePlayer.activePlayerList.ToList().Where(p => diseaseManager.CanInfect(p, disease)).ToList();
if (debug) Puts("Valid players: " + activePlayerList.Count);
if (activePlayerList.Count > 0) {
List<BasePlayer> filteredList = activePlayerList.ToArray().ToList();
// Make a copy
foreach (StatFilter filter in disease.outbreakConditions.statFilters) {
filteredList = filteredList.Where(p => CompareStat(p, filter.stat, filter.minValue, (x, y) => {
return x >= y;
}
) && CompareStat(p, filter.stat, filter.maxValue, (x, y) => {
return x <= y;
}
)).ToList();
}
if (debug) Puts("Filters players: " + filteredList.Count);
for (int i = 0; i < tries; i++) {
if (filteredList.Count > 0) {
BasePlayer randomPlayer = filteredList[UnityEngine.Random.Range(0, filteredList.Count - 1)];
if (disease.outbreakConditions.outbreakChance >= UnityEngine.Random.Range(0, 100)) {
InfectPlayer(randomPlayer, disease);
infected++;
filteredList.Remove(randomPlayer);
}
}
}
diseaseStats[disease.name].infectedFromOutbreak.count += infected;
}
diseaseStats[disease.name].numOutbreaks.count += 1;
;
if (infected > 0) {
if (config.BroadcastOutbreaks) BasePlayer.activePlayerList.ToList().ForEach(x => x.ChatMessage(Lang("StatusOutbreak", x.UserIDString, Color(disease.name, config.DiseaseNameColor))));
}
return infected;
}
private void CurePlayer(BasePlayer player, Disease disease, Action callback = null) {
if (Interface.CallHook("OnPlayerCured", player, disease) != null) return;
if (diseaseManager.RemoveInfected(player, disease)) {
if (debug) Puts("Cured: " + player.displayName + " of " + disease.name);
RefreshIndicator(player);
diseaseManager.SetActiveTick(player, disease, false);
StatusMessage(player, disease, Lang("StatusCured", player.UserIDString, Color(disease.name, config.DiseaseNameColor)));
diseaseStats[disease.name].activeInfected.count -= 1;
callback?.Invoke();
}
}
private void TreatPlayer(BasePlayer player, Disease disease, int amount, int delay, Action callback = null) {
if (Interface.CallHook("OnPlayerTreated", player, disease) != null) return;
if (diseaseManager.HasDisease(player, disease) && !diseaseManager.HasTreatment(player, disease) && !diseaseManager.HasImmunity(player, disease)) {
if (debug) Puts("Treated: " + player.displayName + " of " + disease.name + " for " + amount);
diseaseManager.SetTreatment(player, disease, true);
RefreshIndicator(player);
timer.In(delay, () => {
diseaseManager.SetTreatment(player, disease, false);
RefreshIndicator(player);
}
);
if (diseaseManager.UpdateInfectedTime(player, disease, amount)) {
diseaseManager.SetActiveTick(player, disease, true);
RecoverPlayer(player, disease);
}
StatusMessage(player, disease, Lang("StatusTreated", player.UserIDString, Color(disease.name, config.DiseaseNameColor)));
callback?.Invoke();
}
}
private void StatusMessage(BasePlayer player, Disease disease, string message) {
if (config.ShowStatusMessages) player.ChatMessage(message);
}
private void DoSymptomEffects(BasePlayer player, Disease disease) {
foreach (SymptomEffect effect in disease.symptomEffects) {
effectManager.PlayEffect(player, effect.shortPrefabName, effect.LocalOnly);
}
}
private void DoSymptomScreenEffects(BasePlayer player, Disease disease) {
foreach (SymptomScreenEffect effect in disease.symptomScreenEffects) {
effectManager.ShowScreenEffect(player, effect.shortPrefabName, effect.materialName, effect.opacity, effect.duration, effect.fadeOut);
}
}
private void DoSymptomDamage(BasePlayer player, Disease disease) {
foreach (SymptomDamage damage in disease.damageValues) {
float value = damage.Value();
switch (damage.stat) {
case RustStat.health : player.Hurt(value, Rust.DamageType.Poison);
break;
case RustStat.bleeding : player.metabolism.ApplyChange(MetabolismAttribute.Type.Bleeding, value, 1);
break;
case RustStat.calories : player.metabolism.ApplyChange(MetabolismAttribute.Type.Calories, -value, 1);
break;
case RustStat.hydration : player.metabolism.ApplyChange(MetabolismAttribute.Type.Hydration, -value, 1);
break;
case RustStat.oxygen : player.metabolism.oxygen.value -= value;
break;
case RustStat.radiation : player.metabolism.ApplyChange(MetabolismAttribute.Type.Radiation, value, 1);
break;
case RustStat.temperature : player.metabolism.temperature.value += value;
break;
case RustStat.poison : player.metabolism.ApplyChange(MetabolismAttribute.Type.Poison, value, 1);
break;
}
}
}
private bool WasCauseOfDeath(BasePlayer player, HitInfo info, Disease disease) {
if (!diseaseManager.HasDisease(player, disease)) return false;
Rust.DamageType cause = info.damageTypes.GetMajorityDamageType();
foreach (SymptomDamage damage in disease.damageValues) if (cause == damage.GetDamageType()) return true;
return false;
}
private float GetSpreadChance(BasePlayer player, Disease disease) {
foreach (Item item in player.inventory.containerWear.itemList) {
if (config.FaceCoveringItems.Contains(item.info.shortname)) return disease.spreadChanceCovered;
}
return disease.spreadChanceUncovered;
}
private bool CompareStat(BasePlayer player, string statname, float value, Func<float, float, bool> comparison) {
switch (statname) {
case RustStat.health : return comparison(player.health, value);
case RustStat.bleeding : return comparison(player.metabolism.bleeding.value, value);
case RustStat.calories : return comparison(player.metabolism.calories.value, value);
case RustStat.hydration : return comparison(player.metabolism.hydration.value, value);
case RustStat.oxygen : return comparison(player.metabolism.oxygen.value, value);
case RustStat.radiation : return comparison(player.metabolism.radiation_level.value, value);
case RustStat.temperature : return comparison(player.metabolism.temperature.value, value);
case RustStat.poison : return comparison(player.metabolism.poison.value, value);
}
return false;
}
private string AsTitle(string str) => str.ToLower().TitleCase();
#endregion
#region Data Structures
class DoubleDictionary<TkeyA, TkeyB, Tvalue> {
private Dictionary<TkeyA, Dictionary<TkeyB, Tvalue>> a2b;
private Dictionary<TkeyB, Dictionary<TkeyA, Tvalue>> b2a;
public DoubleDictionary() {
a2b = new Dictionary<TkeyA, Dictionary<TkeyB, Tvalue>>();
b2a = new Dictionary<TkeyB, Dictionary<TkeyA, Tvalue>>();
}
public void Set(TkeyA keyA, TkeyB keyB, Tvalue value) {
if (!a2b.ContainsKey(keyA)) a2b.Add(keyA, new Dictionary<TkeyB, Tvalue>());
if (!b2a.ContainsKey(keyB)) b2a.Add(keyB, new Dictionary<TkeyA, Tvalue>());
if (!a2b[keyA].ContainsKey(keyB)) a2b[keyA].Add(keyB, value);
else a2b[keyA][keyB] = value;
if (!b2a[keyB].ContainsKey(keyA)) b2a[keyB].Add(keyA, value);
else b2a[keyB][keyA] = value;
}
public Tvalue Get(TkeyA keyA, TkeyB keyB) {
if (a2b.ContainsKey(keyA) && a2b[keyA].ContainsKey(keyB)) return a2b[keyA][keyB];
return default(Tvalue);
}
public Dictionary<TkeyB, Tvalue> Get(TkeyA keyA) {
if (a2b.ContainsKey(keyA)) return a2b[keyA];
return new Dictionary<TkeyB, Tvalue>();
}
public Dictionary<TkeyA, Tvalue> Get(TkeyB keyB) {
if (b2a.ContainsKey(keyB)) return b2a[keyB];
return new Dictionary<TkeyA, Tvalue>();
}
public bool ContainsKey(TkeyA keyA, TkeyB keyB) {
return a2b.ContainsKey(keyA) && a2b[keyA].ContainsKey(keyB);
}
public void Delete(TkeyA keyA, TkeyB keyB) {
if (a2b.ContainsKey(keyA) && a2b[keyA].ContainsKey(keyB)) a2b[keyA].Remove(keyB);
if (b2a.ContainsKey(keyB) && b2a[keyB].ContainsKey(keyA)) b2a[keyB].Remove(keyA);
}
public void Delete(TkeyA keyA) {
foreach (TkeyB keyB in b2a.Keys) if (b2a[keyB].ContainsKey(keyA)) b2a[keyB].Remove(keyA);
if (a2b.ContainsKey(keyA)) a2b.Remove(keyA);
}
public void Delete(TkeyB keyB) {
foreach (TkeyA keyA in a2b.Keys) if (a2b[keyA].ContainsKey(keyB)) a2b[keyA].Remove(keyB);
if (b2a.ContainsKey(keyB)) b2a.Remove(keyB);
}
}
class LookupList<TOne, TTwo> {
private Dictionary<TOne, List<TTwo>> a2b;
private Dictionary<TTwo, List<TOne>> b2a;
public LookupList() {
a2b = new Dictionary<TOne, List<TTwo>>();
b2a = new Dictionary<TTwo, List<TOne>>();
}
public int Count() {
return a2b.Count();
}
public void Add(TOne a, TTwo b) {
if (!a2b.ContainsKey(a)) a2b.Add(a, new List<TTwo>());
if (!b2a.ContainsKey(b)) b2a.Add(b, new List<TOne>());
if (!a2b[a].Contains(b)) a2b[a].Add(b);
if (!b2a[b].Contains(a)) b2a[b].Add(a);
}
public void AddAll(TOne[] listA, TTwo b) {
foreach (TOne a in listA) Add(a, b);
}
public void AddAll(TTwo[] listB, TOne a) {
foreach (TTwo b in listB) Add(a, b);
}
public void Remove(TOne a) {
TTwo[] depends = Get(a).ToArray();
foreach (TTwo b in depends) Remove(a, b);
}
public void Remove(TTwo b) {
TOne[] depends = Get(b).ToArray();
foreach (TOne a in depends) Remove(a, b);
}
public void Remove(TOne a, TTwo b) {
if (a2b.ContainsKey(a) && a2b[a].Contains(b)) {
a2b[a].Remove(b);
if (a2b[a].Count() <= 0) a2b.Remove(a);
}
if (b2a.ContainsKey(b) && b2a[b].Contains(a)) {
b2a[b].Remove(a);
if (b2a[b].Count() <= 0) b2a.Remove(b);
}
}
public List<TTwo> Get(TOne a) {
if (a2b.ContainsKey(a)) return a2b[a];
return new List<TTwo>();
}
public List<TOne> Get(TTwo b) {
if (b2a.ContainsKey(b)) return b2a[b];
return new List<TOne>();
}
public bool ContainsKey(TOne a) {
return a2b.ContainsKey(a);
}
public bool ContainsKey(TTwo b) {
return b2a.ContainsKey(b);
}
public bool Contains(TOne a, TTwo b) {
return a2b.ContainsKey(a) && a2b[a].Contains(b);
}
public bool Contains(TTwo b, TOne a) {
return b2a.ContainsKey(b) && b2a[b].Contains(a);
}
}
#endregion
#region Models
class RustStat {
public const string health = "health";
public const string bleeding = "bleeding";
public const string calories = "calories";
public const string hydration = "hydration";
public const string oxygen = "oxygen";
public const string radiation = "radiation";
public const string temperature = "temperature";
public const string poison = "poison";
}
class SymptomEffect {
[JsonProperty(PropertyName = "Asset path")]
public string shortPrefabName;
[JsonProperty(PropertyName = "Local only")]
public bool LocalOnly = false;
}
class SymptomScreenEffect {
[JsonProperty(PropertyName = "Sprite asset path")]
public string shortPrefabName = "assets/content/textures/generic/fulltransparent.tga";
[JsonProperty(PropertyName = "Opacity (0-1.0)")]
public float opacity;
[JsonProperty(PropertyName = "Duration (seconds)")]
public float duration;
[JsonProperty(PropertyName = "Fade out (seconds)")]
public float fadeOut;
[JsonProperty(PropertyName = "Material asset path (optional)")]
public string materialName = "";
}
class SpreaderEntity {
[JsonProperty(PropertyName = "Short prefab name")]
public string shortPrefabName;
[JsonProperty(PropertyName = "Infection chance on hit (0-100)")]
public float infectionChanceOnHit;
}
class InfectionItem : ItemPrefab {
[JsonProperty(PropertyName = "Infection chance on consumption (0-100)")]
public float infectionChanceOnConsumption;
}
class TreatmentItem : ItemPrefab {
[JsonProperty(PropertyName = "Min infection time decreased (seconds)")]
public int minTimeDecrease;
[JsonProperty(PropertyName = "Max infection time decreased (seconds)")]
public int maxTimeDecrease;
[JsonProperty(PropertyName = "Delay between reusing treament (seconds)")]
public int treatmentDelay;
public int Value() => UnityEngine.Random.Range(minTimeDecrease, maxTimeDecrease);
}
class CureItem : ItemPrefab {
[JsonProperty(PropertyName = "Cure chance on consumption (0-100)")]
public float cureChanceOnConsumption;
}
abstract class ItemPrefab {
[JsonProperty(PropertyName = "Short prefab name")]
public string shortPrefabName;
}
class SymptomDamage {
[JsonProperty(PropertyName = "Stat name")]
public string stat;
[JsonProperty(PropertyName = "Min damage")]
public float minValue;
[JsonProperty(PropertyName = "Max damage")]
public float maxValue;
public float Value() => UnityEngine.Random.Range(minValue, maxValue);
public Rust.DamageType GetDamageType() {
switch (stat) {
case "health" : return Rust.DamageType.Poison;
case "bleeding" : return Rust.DamageType.Bleeding;
case "calories" : return Rust.DamageType.Hunger;
case "hydration" : return Rust.DamageType.Thirst;
case "oxygen" : return Rust.DamageType.Drowned;
case "radiation" : return Rust.DamageType.Radiation;
case "temperature" : return Rust.DamageType.Cold;
case "poison" : return Rust.DamageType.Poison;
default : return Rust.DamageType.LAST;
}
}
}
class StatFilter {
[JsonProperty(PropertyName = "Stat name")]
public string stat;
[JsonProperty(PropertyName = "Min value")]
public float minValue;
[JsonProperty(PropertyName = "Max value")]
public float maxValue;
}
class OutbreakCondition {
[JsonProperty(PropertyName = "Outbreaks enabled")]
public bool enabled;
[JsonProperty(PropertyName = "Outbreak chance (0-100)")]
public float outbreakChance;
[JsonProperty(PropertyName = "Time interval (seconds)")]
public int timeInterval;
[JsonProperty(PropertyName = "Only outbreak on players with the following stats")]
public StatFilter[] statFilters;
}
class DiseaseStats {
public class DiseaseStat {
public string name;
public int count;
}
public DiseaseStat activeInfected = new DiseaseStat {
name = "Active players infected", count = 0
}
;
public DiseaseStat sleepersInfected = new DiseaseStat {
name = "Sleeping players infected", count = 0
}
;
public DiseaseStat totalInfected = new DiseaseStat {
name = "Total players infected", count = 0
}
;
public DiseaseStat playersKilled = new DiseaseStat {
name = "Total players killed", count = 0
}
;
public DiseaseStat numOutbreaks = new DiseaseStat {
name = "Number of outbreaks", count = 0
}
;
public DiseaseStat infectedFromSpreading = new DiseaseStat {
name = "Players infected from spreading", count = 0
}
;
public DiseaseStat infectedFromEntity = new DiseaseStat {
name = "Players infected from entities", count = 0
}
;
public DiseaseStat infectedFromItem = new DiseaseStat {
name = "Players infected from items", count = 0
}
;
public DiseaseStat infectedFromOutbreak = new DiseaseStat {
name = "Players infected from outbreaks", count = 0
}
;
public DiseaseStat infectedFromCommand = new DiseaseStat {
name = "Players infected from commands", count = 0
}
;
public DiseaseStat timesTreatedFromItem = new DiseaseStat {
name = "Times treated by items", count = 0
}
;
public DiseaseStat timesCuredFromItem = new DiseaseStat {
name = "Times cured by items", count = 0
}
;
}
#endregion
#region Classes
class Disease {
[JsonProperty(PropertyName = "Disease name")]
public string name {
get;
set;
}
= "";
[JsonProperty(PropertyName = "Description")]
public string info {
get;
set;
}
= "No information on this disease.";
[JsonProperty(PropertyName = "Min time immune after recovery (seconds)")]
public int minImmunityTime {
get;
set;
}
= 0;
[JsonProperty(PropertyName = "Max time immune after recovery (seconds)")]
public int maxImmunityTime {
get;
set;
}
= 0;
[JsonProperty(PropertyName = "Min time infected (seconds)")]
public int minInfectionTime {
get;
set;
}
= 0;
[JsonProperty(PropertyName = "Max time infected (seconds)")]
public int maxInfectionTime {
get;
set;
}
= 0;
[JsonProperty(PropertyName = "Min time between symptoms (seconds)")]
public int minSpreadTime {
get;
set;
}
= 0;
[JsonProperty(PropertyName = "Max time between symptoms (seconds)")]
public int maxSpreadTime {
get;
set;
}
= 0;
[JsonProperty(PropertyName = "Spread distance (4.0 = 1 foundation)")]
public float spreadDistance {
get;
set;
}
= 0;
[JsonProperty(PropertyName = "Spread chance with mask (0-100)")]
public float spreadChanceCovered {
get;
set;
}
= 0;
[JsonProperty(PropertyName = "Spread chance without mask (0-100)")]
public float spreadChanceUncovered {
get;
set;
}
= 0;
[JsonProperty(PropertyName = "Symptom damage effects")]
public SymptomDamage[] damageValues {
get;
set;
}
= new SymptomDamage[] {
}
;
[JsonProperty(PropertyName = "Items that cause infection on consumption")]
public InfectionItem[] infectionItems {
get;
set;
}
= new InfectionItem[] {
}
;
[JsonProperty(PropertyName = "Items that cure on consumption")]
public CureItem[] cureItems {
get;
set;
}
= new CureItem[] {
}
;
[JsonProperty(PropertyName = "Items that reduce infection time on consumption")]
public TreatmentItem[] treatmentItems {
get;
set;
}
= new TreatmentItem[] {
}
;
[JsonProperty(PropertyName = "Entities that cause infection on hit")]
public SpreaderEntity[] spreaderEntities {
get;
set;
}
= new SpreaderEntity[] {
}
;
[JsonProperty(PropertyName = "Random outbreak settings")]
public OutbreakCondition outbreakConditions {
get;
set;
}
= new OutbreakCondition {
enabled = false, outbreakChance = 0, timeInterval = 300, statFilters = new StatFilter[] {
}
}
;
[JsonProperty(PropertyName = "Symptom effects")]
public SymptomEffect[] symptomEffects {
get;
set;
}
= new SymptomEffect[] {
}
;
[JsonProperty(PropertyName = "Symptom screen effects")]
public SymptomScreenEffect[] symptomScreenEffects {
get;
set;
}
= new SymptomScreenEffect[] {
}
;
[JsonProperty(PropertyName = "Version")]
public VersionNumber version {
get;
set;
}
= new VersionNumber(0, 0, 0);
public int SpreadTime() => UnityEngine.Random.Range(minSpreadTime, maxSpreadTime);
public int InfectedTime() => UnityEngine.Random.Range(minInfectionTime, maxInfectionTime);
public int ImmunityTime() => UnityEngine.Random.Range(minImmunityTime, maxImmunityTime);
#region File IO
public static Disease GenerateDefault() {
Disease disease = new Disease {
name = "Norovirus", info = "Highly contagious disease that causes vomitting and is caused by eating rotten food.Can be treated with tea and cured with pills.", minImmunityTime = 180, maxImmunityTime = 600, minInfectionTime = 180, maxInfectionTime = 600, minSpreadTime = 5, maxSpreadTime = 45, spreadDistance = 2f, spreadChanceCovered = 0, spreadChanceUncovered = 85, damageValues = new SymptomDamage[] {
new SymptomDamage {
stat=RustStat.health , minValue=1f, maxValue=5f
}
, new SymptomDamage {
stat=RustStat.calories , minValue=100f, maxValue=125f
}
, new SymptomDamage {
stat=RustStat.hydration , minValue=50f, maxValue=85f
}
}
, infectionItems = new InfectionItem[] {
new InfectionItem {
shortPrefabName="chicken.raw", infectionChanceOnConsumption=10f
}
, new InfectionItem {
shortPrefabName="chicken.spoiled", infectionChanceOnConsumption=85f
}
, new InfectionItem {
shortPrefabName="humanmeat.raw", infectionChanceOnConsumption=10f
}
, new InfectionItem {
shortPrefabName="humanmeat.spoiled", infectionChanceOnConsumption=85f
}
}
, cureItems = new CureItem[] {
new CureItem {
shortPrefabName="antiradpills", cureChanceOnConsumption=50f
}
}
, treatmentItems = new TreatmentItem[] {
new TreatmentItem {
shortPrefabName="healingtea", minTimeDecrease=5, maxTimeDecrease=10, treatmentDelay=10
}
, new TreatmentItem {
shortPrefabName="healingtea.advanced", minTimeDecrease=10, maxTimeDecrease=40, treatmentDelay=40
}
, new TreatmentItem {
shortPrefabName="healingtea.pure", minTimeDecrease=40, maxTimeDecrease=160, treatmentDelay=160
}
}
, spreaderEntities = new SpreaderEntity[] {
new SpreaderEntity {
shortPrefabName="boar", infectionChanceOnHit=5f
}
}
, outbreakConditions = new OutbreakCondition {
enabled = true, outbreakChance = 25f, timeInterval = 300, statFilters = new StatFilter[] {
new StatFilter {
stat=RustStat.health, minValue=0, maxValue=50
}
}
}
, symptomEffects = new SymptomEffect[] {
new SymptomEffect {
shortPrefabName="assets/bundled/prefabs/fx/gestures/drink_vomit.prefab"
}
, new SymptomEffect {
shortPrefabName="assets/bundled/prefabs/fx/water/midair_splash.prefab"
}
, new SymptomEffect {
shortPrefabName="assets/prefabs/weapons/cake/effects/strike_screenshake.prefab", LocalOnly=true
}
}
, symptomScreenEffects = new SymptomScreenEffect[] {
new SymptomScreenEffect {
shortPrefabName="assets/content/ui/overlay_poisoned.png", duration=1f, opacity=1f, fadeOut=1f
}
}
, version = PLUGIN.Version
}
;
return disease;
}
public static void SaveToFile(Disease disease) {
Interface.Oxide.DataFileSystem.WriteObject("Diseases/" + disease.name, disease);
}
public static Disease ReadFromFile(string diseaseName) {
try {
Disease disease = Interface.Oxide.DataFileSystem.ReadObject<Disease>("Diseases/" + diseaseName);
if (disease.name == null) disease.name = "null";
if (PLUGIN.AsTitle(diseaseName) != PLUGIN.AsTitle(disease.name)) PLUGIN.PrintWarning($"Disease file '{diseaseName}.json' does not match with disease name of '{disease.name}', the name '{diseaseName}' will be used instead.");
disease.name = PLUGIN.AsTitle(diseaseName);
if (disease.version.Major != PLUGIN.Version.Major || disease.version.Minor != PLUGIN.Version.Minor) {
PLUGIN.PrintError($"The disease file '{disease.name}.json' is version {disease.version} but the plugin version is {PLUGIN.Version}.This disease cannot be loaded until it is updated.");
return null;
}
else if (disease.version.Patch != PLUGIN.Version.Patch) {
PLUGIN.PrintWarning($"Consider updating the 'Version' property in '{diseaseName}.json' from {disease.version} to {PLUGIN.Version}.");
}
return disease;
}
catch (Newtonsoft.Json.JsonSerializationException) {
PLUGIN.PrintError($"The disease file '{diseaseName}.json' contains a syntax error.Please check to make sure it matches the syntax in 'Norovirus.json'.");
}
catch (Exception) {
PLUGIN.PrintError($"The disease file '{diseaseName}.json' failed to load properly.");
}
return null;
}
#endregion
}
#endregion
#region Manager
class EffectManager {
public readonly Dictionary<ulong, List<string>> ActiveEffects;
private ulong idCount;
public EffectManager() {
ActiveEffects = new Dictionary<ulong, List<string>>();
idCount = 0;
}
private void InitPlayer(ulong userid) {
if (!ActiveEffects.ContainsKey(userid)) ActiveEffects.Add(userid, new List<string>());
}
private void AddEffect(ulong userid, string effectString) {
InitPlayer(userid);
ActiveEffects[userid].Add(effectString + idCount);
idCount++;
}
private void RemoveEffect(ulong userid, string effectString) {
InitPlayer(userid);
if (ActiveEffects[userid].Contains(effectString)) ActiveEffects[userid].Remove(effectString);
}
public void PlayEffect(BasePlayer player, string effectString, bool local) {
var effect = new Effect(effectString, player, 0, Vector3.zero, Vector3.forward);
if (local) EffectNetwork.Send(effect, player.net.connection);
else EffectNetwork.Send(effect);
}
public void ClearAllEffects(BasePlayer player) {
InitPlayer(player.userID);
foreach (string activeEffect in ActiveEffects[player.userID]) {
CuiHelper.DestroyUi(player, activeEffect);
RemoveEffect(player.userID, activeEffect);
}
}
public void ShowScreenEffect(BasePlayer player, string assetStringPath, string materialStringPath, float opacity = 1, float duration = 1, float fadeOut = 1) {
CuiElementContainer container = new CuiElementContainer();
ulong thisId = idCount;
container.Add(new CuiElement {
Name = assetStringPath + thisId, Parent = "Hud", FadeOut = fadeOut, Components = {
new CuiRawImageComponent {
Sprite = assetStringPath, Material = materialStringPath, Color = "1 1 1 " + opacity
}
, new CuiRectTransformComponent {
AnchorMin = "0 0", AnchorMax = "1 1"
}
}
,
}
);
if (debug) PLUGIN.Puts("Started screen effect for " + assetStringPath + thisId);
CuiHelper.AddUi(player, container);
AddEffect(player.userID, assetStringPath);
PLUGIN.timer.In(duration, () => {
CuiHelper.DestroyUi(player, assetStringPath + thisId);
if (debug) PLUGIN.Puts("Cleared screen effect for " + assetStringPath + thisId);
RemoveEffect(player.userID, assetStringPath + thisId);
}
);
}
}
class DiseaseManager {
public readonly LookupLi
// ... [Code preview truncated due to length. Full source in attached file] ...