C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Plugins.TruePVEExtensionMethods;
using Rust;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
/*
Fix damage to patrol helicopter when crashing into terrain
Added `heliturrets cannot hurt players` to default config - you MUST add this to prevent damage if you have `anything can hurt players` rule
{
"name": "heliturrets",
"members": "turret_attackheli",
"exclusions": ""
},
heliturrets cannot hurt players
*/
namespace Oxide.Plugins {
[Info("TruePVE", "nivex", "2.1.2")]
[Description("Improvement of the default Rust PVE behavior")]
internal // Thanks to the original author, ignignokt84.class TruePVE : RustPlugin
{
#region Variables
private static TruePVE Instance;
// config/data container
private Configuration config = new Configuration();
[PluginReference]
Plugin ZoneManager, LiteZones, Clans, Friends, AbandonedBases, RaidableBases;
public string usageString;
private enum Command {
def, sched, trace, usage, enable, sleepers
}
;
//[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
private class RuleFlags : FlagsAttribute {
public const long None = 0;
public const long AdminsHurtSleepers = 1L << 1;
public const long AnimalsIgnoreSleepers = 1L << 2;
public const long AuthorizedDamage = 1L << 3;
public const long AuthorizedDamageRequiresOwnership = 1L << 4;
public const long CupboardOwnership = 1L << 5;
public const long FriendlyFire = 1L << 6;
public const long HeliDamageLocked = 1L << 7;
public const long HumanNPCDamage = 1L << 8;
public const long LockedBoxesImmortal = 1L << 9;
public const long LockedDoorsImmortal = 1L << 10;
public const long NoPlayerDamageToCar = 1L << 11;
public const long NoPlayerDamageToMini = 1L << 12;
public const long NoPlayerDamageToScrap = 1L << 13;
public const long NoHeliDamage = 1L << 14;
public const long NoHeliDamagePlayer = 1L << 15;
public const long NoHeliDamageQuarry = 1L << 16;
public const long NoHeliDamageRidableHorses = 1L << 17;
public const long NoHeliDamageSleepers = 1L << 18;
public const long NoMLRSDamage = 1L << 19;
public const long NpcsCanHurtAnything = 1L << 20;
public const long PlayerSamSitesIgnorePlayers = 1L << 21;
public const long ProtectedSleepers = 1L << 22;
public const long TrapsIgnorePlayers = 1L << 23;
public const long TrapsIgnoreScientist = 1L << 24;
public const long TurretsIgnorePlayers = 1L << 25;
public const long TurretsIgnoreScientist = 1L << 26;
public const long TwigDamage = 1L << 27;
public const long TwigDamageRequiresOwnership = 1L << 28;
public const long VehiclesTakeCollisionDamageWithoutDriver = 1L << 29;
public const long SamSitesIgnoreMLRS = 1L << 30;
public const long SelfDamage = 1L << 31;
public const long StaticSamSitesIgnorePlayers = 1L << 32;
public const long StaticTurretsIgnorePlayers = 1L << 33;
public const long SafeZoneTurretsIgnorePlayers = 1L << 34;
public const long SuicideBlocked = 1L << 35;
public const long NoHeliDamageBuildings = 1L << 36;
public const long WoodenDamage = 1L << 37;
public const long WoodenDamageRequiresOwnership = 1L << 38;
public static long Get(string value) {
switch (value) {
case "None" : default : return RuleFlags.None;
case "AdminsHurtSleepers" : return RuleFlags.AdminsHurtSleepers;
case "AnimalsIgnoreSleepers" : return RuleFlags.AnimalsIgnoreSleepers;
case "AuthorizedDamage" : return RuleFlags.AuthorizedDamage;
case "AuthorizedDamageRequiresOwnership" : return RuleFlags.AuthorizedDamageRequiresOwnership;
case "CupboardOwnership" : return RuleFlags.CupboardOwnership;
case "FriendlyFire" : return RuleFlags.FriendlyFire;
case "HeliDamageLocked" : return RuleFlags.HeliDamageLocked;
case "HumanNPCDamage" : return RuleFlags.HumanNPCDamage;
case "LockedBoxesImmortal" : return RuleFlags.LockedBoxesImmortal;
case "LockedDoorsImmortal" : return RuleFlags.LockedDoorsImmortal;
case "NoPlayerDamageToCar" : return RuleFlags.NoPlayerDamageToCar;
case "NoPlayerDamageToMini" : return RuleFlags.NoPlayerDamageToMini;
case "NoPlayerDamageToScrap" : return RuleFlags.NoPlayerDamageToScrap;
case "NoHeliDamage" : return RuleFlags.NoHeliDamage;
case "NoHeliDamagePlayer" : return RuleFlags.NoHeliDamagePlayer;
case "NoHeliDamageQuarry" : return RuleFlags.NoHeliDamageQuarry;
case "NoHeliDamageRidableHorses" : return RuleFlags.NoHeliDamageRidableHorses;
case "NoHeliDamageSleepers" : return RuleFlags.NoHeliDamageSleepers;
case "NoMLRSDamage" : return RuleFlags.NoMLRSDamage;
case "NpcsCanHurtAnything" : return RuleFlags.NpcsCanHurtAnything;
case "PlayerSamSitesIgnorePlayers" : return RuleFlags.PlayerSamSitesIgnorePlayers;
case "ProtectedSleepers" : return RuleFlags.ProtectedSleepers;
case "TrapsIgnorePlayers" : return RuleFlags.TrapsIgnorePlayers;
case "TrapsIgnoreScientist" : return RuleFlags.TrapsIgnoreScientist;
case "TurretsIgnorePlayers" : return RuleFlags.TurretsIgnorePlayers;
case "TurretsIgnoreScientist" : return RuleFlags.TurretsIgnoreScientist;
case "TwigDamage" : return RuleFlags.TwigDamage;
case "TwigDamageRequiresOwnership" : return RuleFlags.TwigDamageRequiresOwnership;
case "VehiclesTakeCollisionDamageWithoutDriver" : return RuleFlags.VehiclesTakeCollisionDamageWithoutDriver;
case "SamSitesIgnoreMLRS" : return RuleFlags.SamSitesIgnoreMLRS;
case "SelfDamage" : return RuleFlags.SelfDamage;
case "StaticSamSitesIgnorePlayers" : return RuleFlags.StaticSamSitesIgnorePlayers;
case "StaticTurretsIgnorePlayers" : return RuleFlags.StaticTurretsIgnorePlayers;
case "SafeZoneTurretsIgnorePlayers" : return RuleFlags.SafeZoneTurretsIgnorePlayers;
case "SuicideBlocked" : return RuleFlags.SuicideBlocked;
case "WoodenDamage" : return RuleFlags.WoodenDamage;
case "WoodenDamageRequiresOwnership" : return RuleFlags.WoodenDamageRequiresOwnership;
}
}
public static long Get(int value) {
switch (value) {
case 0 : default : return RuleFlags.None;
case 1 : return RuleFlags.AdminsHurtSleepers;
case 2 : return RuleFlags.AnimalsIgnoreSleepers;
case 3 : return RuleFlags.AuthorizedDamage;
case 4 : return RuleFlags.AuthorizedDamageRequiresOwnership;
case 5 : return RuleFlags.CupboardOwnership;
case 6 : return RuleFlags.FriendlyFire;
case 7 : return RuleFlags.HeliDamageLocked;
case 8 : return RuleFlags.HumanNPCDamage;
case 9 : return RuleFlags.LockedBoxesImmortal;
case 10 : return RuleFlags.LockedDoorsImmortal;
case 11 : return RuleFlags.NoPlayerDamageToCar;
case 12 : return RuleFlags.NoPlayerDamageToMini;
case 13 : return RuleFlags.NoPlayerDamageToScrap;
case 14 : return RuleFlags.NoHeliDamage;
case 15 : return RuleFlags.NoHeliDamagePlayer;
case 16 : return RuleFlags.NoHeliDamageQuarry;
case 17 : return RuleFlags.NoHeliDamageRidableHorses;
case 18 : return RuleFlags.NoHeliDamageSleepers;
case 19 : return RuleFlags.NoMLRSDamage;
case 20 : return RuleFlags.NpcsCanHurtAnything;
case 21 : return RuleFlags.PlayerSamSitesIgnorePlayers;
case 22 : return RuleFlags.ProtectedSleepers;
case 23 : return RuleFlags.TrapsIgnorePlayers;
case 24 : return RuleFlags.TrapsIgnoreScientist;
case 25 : return RuleFlags.TurretsIgnorePlayers;
case 26 : return RuleFlags.TurretsIgnoreScientist;
case 27 : return RuleFlags.TwigDamage;
case 28 : return RuleFlags.TwigDamageRequiresOwnership;
case 29 : return RuleFlags.VehiclesTakeCollisionDamageWithoutDriver;
case 30 : return RuleFlags.SamSitesIgnoreMLRS;
case 31 : return RuleFlags.SelfDamage;
case 32 : return RuleFlags.StaticSamSitesIgnorePlayers;
case 33 : return RuleFlags.StaticTurretsIgnorePlayers;
case 34 : return RuleFlags.SafeZoneTurretsIgnorePlayers;
case 35 : return RuleFlags.SuicideBlocked;
case 36 : return RuleFlags.WoodenDamage;
case 37 : return RuleFlags.WoodenDamageRequiresOwnership;
}
}
}
private Timer scheduleUpdateTimer;
// timer to check for schedule updates
private bool shareRedirectEnabled;
// undocumented.UAYOR.private RuleSet dudRuleSet; // dud ruleset when no locations are shared
private RuleSet currentRuleSet;
// current ruleset
private string currentBroadcastMessage;
// current broadcast message
private bool useZones;
// internal useZones flag
private const string Any = "any";
// constant "any" string for rules
private const string AllZones = "allzones";
// constant "allzones" string for mappings
private const string PermCanMap = "truepve.canmap";
// permission for mapping command
private bool animalsIgnoreSleepers;
// toggle flag to protect sleepers
private bool trace = false;
// trace flag
private const string traceFile = "ruletrace";
// tracefile name
private const float traceTimeout = 300f;
// auto-disable trace after 300s (5m)
private Timer traceTimer;
// trace timeout timer
private bool tpveEnabled = true;
// toggle flag for damage handling
private List<DamageType> damageTypes = new List<DamageType> {
DamageType.Arrow, DamageType.Blunt, DamageType.Bullet, DamageType.Explosion, DamageType.Heat, DamageType.Generic, DamageType.Slash, DamageType.Stab,
}
;
#endregion
#region Loading/Unloading
private void Unload() {
Instance = null;
scheduleUpdateTimer?.Destroy();
}
private void OnPluginLoaded(Plugin plugin) {
if (plugin.Name == "ZoneManager") ZoneManager = plugin;
if (plugin.Name == "LiteZones") LiteZones = plugin;
if (ZoneManager != null || LiteZones != null) useZones = config?.options.useZones ?? true;
}
private void OnPluginUnloaded(Plugin plugin) {
if (plugin.Name == "ZoneManager") ZoneManager = null;
if (plugin.Name == "LiteZones") LiteZones = null;
if (ZoneManager == null && LiteZones == null) useZones = false;
traceTimer?.Destroy();
}
private void Init() {
Unsubscribe(nameof(OnEntityMarkHostile));
Unsubscribe(nameof(OnEntitySpawned));
Unsubscribe(nameof(OnEntityEnter));
Unsubscribe(nameof(OnTurretTarget));
Unsubscribe(nameof(OnEntityTakeDamage));
Unsubscribe(nameof(OnPlayerConnected));
Unsubscribe(nameof(OnSamSiteTarget));
Unsubscribe(nameof(OnTrapTrigger));
Unsubscribe(nameof(OnNpcTarget));
Unsubscribe(nameof(OnMlrsFire));
Instance = this;
// register console commands automagically
foreach (Command command in Enum.GetValues(typeof(Command))) {
AddCovalenceCommand($"tpve.{command}", nameof(CommandDelegator));
}
// register chat commands
AddCovalenceCommand("tpve_prod", nameof(CommandDelegator));
AddCovalenceCommand("tpve_enable", nameof(CommandDelegator));
AddCovalenceCommand("tpve", nameof(CommandDelegator));
permission.RegisterPermission(PermCanMap, this);
// build usage string for console (without sizing)
usageString = WrapColor("orange", GetMessage("Header_Usage")) + $" - {Version}{Environment.NewLine}" + WrapColor("cyan", $"tpve.{Command.def}") + $" - {GetMessage("Cmd_Usage_def")}{Environment.NewLine}" + WrapColor("cyan", $"tpve.{Command.trace}") + $" - {GetMessage("Cmd_Usage_trace")}{Environment.NewLine}" + WrapColor("cyan", $"tpve.{Command.sched} [enable|disable]") + $" - {GetMessage("Cmd_Usage_sched")}{Environment.NewLine}" + WrapColor("cyan", $"/tpve_prod") + $" - {GetMessage("Cmd_Usage_prod")}{Environment.NewLine}" + WrapColor("cyan", $"/tpve map") + $" - {GetMessage("Cmd_Usage_map")}";
}
private void OnServerInitialized(bool isStartup) {
// check for server pve setting
if (ConVar.Server.pve) WarnPve();
// load configuration
config.Init();
currentRuleSet = config.GetDefaultRuleSet();
dudRuleSet = config.GetDudRuleSet();
if (currentRuleSet == null) PrintWarning(GetMessage("Warning_NoRuleSet"), config.defaultRuleSet);
useZones = config.options.useZones && (LiteZones != null || ZoneManager != null);
if (useZones && config.mappings.Count == 1 && config.mappings.FirstOrDefault().Key.Equals(config.defaultRuleSet)) useZones = false;
if (config.schedule.enabled) TimerLoop(true);
if (config.ruleSets.Exists(ruleSet => ruleSet.HasFlag(RuleFlags.AnimalsIgnoreSleepers))) Subscribe(nameof(OnNpcTarget));
if (currentRuleSet == null) return;
if (config.ruleSets.Exists(ruleSet => (ruleSet._flags & (RuleFlags.SafeZoneTurretsIgnorePlayers | RuleFlags.StaticTurretsIgnorePlayers | RuleFlags.TrapsIgnorePlayers | RuleFlags.TrapsIgnoreScientist | RuleFlags.TurretsIgnorePlayers | RuleFlags.TurretsIgnoreScientist)) != 0)) {
Subscribe(nameof(OnEntityEnter));
Subscribe(nameof(OnTurretTarget));
}
if (config.ruleSets.Exists(ruleSet => (ruleSet._flags & (RuleFlags.SamSitesIgnoreMLRS | RuleFlags.PlayerSamSitesIgnorePlayers | RuleFlags.StaticSamSitesIgnorePlayers)) != 0)) {
Subscribe(nameof(OnSamSiteTarget));
}
if (config.ruleSets.Exists(ruleSet => (ruleSet._flags & (RuleFlags.TrapsIgnorePlayers | RuleFlags.TrapsIgnoreScientist)) != 0)) {
Subscribe(nameof(OnTrapTrigger));
}
if (config.schedule.enabled && config.schedule.broadcast && currentBroadcastMessage != null) {
Subscribe(nameof(OnPlayerConnected));
}
if (config.options.disableBaseOvenSplash) {
BaseNetworkable.serverEntities.OfType<BaseOven>().ForEach(oven => oven.disabledBySplash = false);
}
if (config.options.disableHostility) {
Subscribe(nameof(OnEntityMarkHostile));
}
Subscribe(nameof(OnEntitySpawned));
Subscribe(nameof(OnEntityTakeDamage));
Subscribe(nameof(OnMlrsFire));
}
#endregion
#region Command Handling
// delegation method for commands
private void CommandDelegator(IPlayer user, string command, string[] args) {
// return if user doesn't have access to run console command
if (!user.IsServer && !(user.Object as BasePlayer).IsAdmin) return;
if (args.Length > 0 && args[0] == "map" && user.HasPermission(PermCanMap)) {
CommandMap(user, command, args);
return;
}
if (args.Contains("pvp")) {
if (!currentRuleSet.rules.Remove("players cannot hurt players")) {
currentRuleSet.rules.Add("players cannot hurt players");
}
Puts("PVP toggled {0}", currentRuleSet.rules.Contains("players cannot hurt players") ? "on" : "off");
SaveConfig();
return;
}
if (command == "tpve_prod") {
HandleProd(user);
return;
}
if (command == "tpve_enable") {
tpveEnabled = !tpveEnabled;
Message(user, "Enable", tpveEnabled);
return;
}
if (command == "tpve" && args.Length != 0) command = args[0];
else command = command.Replace("tpve.", string.Empty);
TruePVE.Command @enum;
if (!Enum.TryParse(command, out @enum)) {
user.Reply($"Invalid argument: {command}");
return;
}
switch (@enum) {
case Command.sleepers : HandleSleepers(user);
return;
case Command.def : HandleDef(user);
return;
case Command.sched : HandleScheduleSet(user, args);
return;
case Command.trace : _tracesToServerConsole = 0;
trace = !trace;
if (!trace) {
tracePlayer = null;
traceEntity = null;
}
else tracePlayer = user.Object as BasePlayer;
Message(user, "Notify_TraceToggle", new object[] {
trace ? "on" : "off"
}
);
if (trace) {
traceTimer = timer.In(traceTimeout, () => trace = false);
}
else traceTimer?.Destroy();
return;
case Command.enable : tpveEnabled = !tpveEnabled;
Message(user, "Enable", tpveEnabled);
return;
case Command.usage : default : ShowUsage(user);
return;
}
}
private void HandleSleepers(IPlayer user) {
if (animalsIgnoreSleepers) {
animalsIgnoreSleepers = false;
if (!config.ruleSets.Exists(ruleSet => ruleSet.HasFlag(RuleFlags.AnimalsIgnoreSleepers))) Unsubscribe(nameof(OnNpcTarget));
user.Reply("Sleepers are no longer protected from animals.");
}
else {
animalsIgnoreSleepers = true;
Subscribe(nameof(OnNpcTarget));
user.Reply("Sleepers are now protected from animals.");
}
}
// handle setting defaults
private void HandleDef(IPlayer user) {
config.options = new ConfigurationOptions();
Message(user, "Notify_DefConfigLoad");
LoadDefaultData();
Message(user, "Notify_DefDataLoad");
SaveConfig();
}
// handle prod command (raycast to determine what player is looking at)
private void HandleProd(IPlayer user) {
var player = user.Object as BasePlayer;
if (player == null || !player.IsAdmin) {
Message(user, "Error_NoPermission");
return;
}
object entity;
if (!GetRaycastTarget(player, out entity) || entity.IsNull()) {
SendReply(player, WrapSize(12, WrapColor("red", GetMessage("Error_NoEntityFound", player.UserIDString))));
return;
}
Message(player, "Notify_ProdResult", new object[] {
entity.GetType(), (entity as BaseEntity).ShortPrefabName
}
);
}
private void CommandMap(IPlayer user, string command, string[] args) {
// assume args[0] is the command (beyond /tpve)
if (args.Length > 0) command = args[0];
// shift arguments
args = args.Length > 1 ? args.Skip(1) : new string[0];
if (command != "map") {
Message(user, "Error_InvalidCommand");
}
else if (args.Length == 0) {
Message(user, "Error_InvalidParamForCmd", command);
}
else {
string from = args[0];
// mapping name
string to = args.Length == 2 ? args[1] : null;
// target ruleSet/exclude, otherwise delete mapping
if (to != null) {
if (to != "exclude" && !config.ruleSets.Exists(r => r.name == to)) {
// target ruleset must exist, or be "exclude"
Message(user, "Error_InvalidMapping", from, to);
return;
}
if (config.HasMapping(from)) {
string old = config.mappings[from];
Message(user, "Notify_MappingUpdated", from, old, to);
// update existing mapping
}
else Message(user, "Notify_MappingCreated", from, to);
// add new mapping
config.mappings[from] = to;
SaveConfig();
}
else {
if (config.HasMapping(from)) {
Message(user, "Notify_MappingDeleted", from, config.mappings[from]);
config.mappings.Remove(from);
// remove mapping
SaveConfig();
}
else Message(user, "Error_NoMappingToDelete", from);
}
}
}
// handles schedule enable/disable
private void HandleScheduleSet(IPlayer user, string[] args) {
if (args.Length == 0) {
Message(user, "Error_InvalidParamForCmd");
return;
}
if (!config.schedule.valid) {
Message(user, "Notify_InvalidSchedule");
}
else if (args[0] == "enable") {
if (config.schedule.enabled) return;
config.schedule.enabled = true;
TimerLoop();
Message(user, "Notify_SchedSetEnabled");
}
else if (args[0] == "disable") {
if (!config.schedule.enabled) return;
config.schedule.enabled = false;
if (scheduleUpdateTimer != null) scheduleUpdateTimer.Destroy();
Message(user, "Notify_SchedSetDisabled");
}
else {
Message(user, "Error_InvalidParameter", args[0]);
}
}
#endregion
#region Configuration/Data
// load config
protected override void LoadConfig() {
base.LoadConfig();
try {
config = Config.ReadObject<Configuration>();
if (config == null) LoadDefaultConfig();
CheckData();
SaveConfig();
}
catch (Exception ex) {
Puts("{0}", ex);
LoadDefaultConfig();
return;
}
// check config version, update version to current version
if (config.configVersion != Version.ToString()) {
config.configVersion = Version.ToString();
SaveConfig();
}
}
protected override void LoadDefaultConfig() {
config = new Configuration {
configVersion = Version.ToString(), options = new ConfigurationOptions()
}
;
LoadDefaultData();
}
// save data
protected override void SaveConfig() {
Config.WriteObject(config);
}
// check rulesets and groups
private void CheckData() {
if (config.ruleSets.IsNullOrEmpty() || config.groups.IsNullOrEmpty()) {
LoadDefaultData();
}
if (config.schedule == null) {
config.schedule = new Schedule();
}
CheckMappings();
}
// rebuild mappings
private bool CheckMappings() {
bool dirty = false;
foreach (RuleSet rs in config.ruleSets) {
if (!config.mappings.ContainsValue(rs.name)) {
config.mappings[rs.name] = rs.name;
dirty = true;
}
}
return dirty;
}
// load default data to mappings, rulesets, and groups
private bool LoadDefaultData() {
config.mappings.Clear();
config.ruleSets.Clear();
config.groups.Clear();
config.schedule = new Schedule();
config.defaultRuleSet = "default";
// build groups first
config.groups.Add(new EntityGroup("barricades") {
members = "Barricade, icewall, GraveYardFence", // "barricade.cover.wood, door_barricade_a, door_barricade_a_large, door_barricade_b, door_barricade_dbl_a, door_barricade_dbl_a_large, door_barricade_dbl_b, door_barricade_dbl_b_large",
exclusions = "barricade.concrete, barricade.sandbags, barricade.metal, barricade.stone, barricade.wood, barricade.woodwire"
}
);
config.groups.Add(new EntityGroup("dispensers") {
members = "BaseCorpse, HelicopterDebris, PlayerCorpse, NPCPlayerCorpse, HorseCorpse, SkyLantern"
}
);
config.groups.Add(new EntityGroup("fire") {
members = "FireBall, FlameExplosive, FlameThrower, BaseOven, FlameTurret, rocket_heli_napalm, napalm, oilfireball2"
}
);
config.groups.Add(new EntityGroup("guards") {
members = "bandit_guard, scientistpeacekeeper, sentry.scientist.static"
}
);
config.groups.Add(new EntityGroup("heli") {
members = "PatrolHelicopter"
}
);
config.groups.Add(new EntityGroup("highwalls") {
members = "SimpleBuildingBlock, wall.external.high.ice, gates.external.high.stone, gates.external.high.wood"
}
);
config.groups.Add(new EntityGroup("ridablehorses") {
members = "RidableHorse"
}
);
config.groups.Add(new EntityGroup("cars") {
members = "BasicCar, ModularCar, BaseModularVehicle, BaseVehicleModule, VehicleModuleEngine, VehicleModuleSeating, VehicleModuleStorage, VehicleModuleTaxi, ModularCarSeat"
}
);
config.groups.Add(new EntityGroup("mini") {
members = "minicopter.entity"
}
);
config.groups.Add(new EntityGroup("scrapheli") {
members = "ScrapTransportHelicopter"
}
);
config.groups.Add(new EntityGroup("ch47") {
members = "ch47.entity"
}
);
config.groups.Add(new EntityGroup("npcs") {
members = "ch47scientists.entity, BradleyAPC, CustomScientistNpc, ScarecrowNPC, HumanNPC, NPCPlayer, ScientistNPC, TunnelDweller, SimpleShark, UnderwaterDweller, ZombieNPC"
}
);
config.groups.Add(new EntityGroup("players") {
members = "BasePlayer, FrankensteinPet"
}
);
config.groups.Add(new EntityGroup("resources") {
members = "ResourceEntity, TreeEntity, OreResourceEntity, LootContainer", exclusions = "hobobarrel.deployed"
}
);
config.groups.Add(new EntityGroup("snowmobiles") {
members = "snowmobile, tomahasnowmobile"
}
);
config.groups.Add(new EntityGroup("traps") {
members = "AutoTurret, BearTrap, FlameTurret, Landmine, GunTrap, ReactiveTarget, TeslaCoil, spikes.floor"
}
);
config.groups.Add(new EntityGroup("junkyard") {
members = "magnetcrane.entity, carshredder.entity"
}
);
config.groups.Add(new EntityGroup("tugboats") {
members = "Tugboat"
}
);
config.groups.Add(new EntityGroup("heliturrets") {
members = "turret_attackheli"
}
);
// create default ruleset
RuleSet defaultRuleSet = new RuleSet(config.defaultRuleSet) {
_flags = RuleFlags.HumanNPCDamage | RuleFlags.LockedBoxesImmortal | RuleFlags.LockedDoorsImmortal | RuleFlags.PlayerSamSitesIgnorePlayers | RuleFlags.TrapsIgnorePlayers | RuleFlags.TurretsIgnorePlayers, flags = "HumanNPCDamage, LockedBoxesImmortal, LockedDoorsImmortal, PlayerSamSitesIgnorePlayers, TrapsIgnorePlayers, TurretsIgnorePlayers"
}
;
// create rules and add to ruleset
defaultRuleSet.AddRule("anything can hurt dispensers");
defaultRuleSet.AddRule("anything can hurt resources");
defaultRuleSet.AddRule("anything can hurt barricades");
defaultRuleSet.AddRule("anything can hurt traps");
defaultRuleSet.AddRule("anything can hurt heli");
defaultRuleSet.AddRule("anything can hurt npcs");
defaultRuleSet.AddRule("anything can hurt players");
defaultRuleSet.AddRule("nothing can hurt ch47");
defaultRuleSet.AddRule("nothing can hurt cars");
defaultRuleSet.AddRule("nothing can hurt mini");
defaultRuleSet.AddRule("nothing can hurt snowmobiles");
//defaultRuleSet.AddRule("nothing can hurt guards");
defaultRuleSet.AddRule("nothing can hurt ridablehorses");
defaultRuleSet.AddRule("cars cannot hurt anything");
defaultRuleSet.AddRule("mini cannot hurt anything");
defaultRuleSet.AddRule("ch47 cannot hurt anything");
defaultRuleSet.AddRule("scrapheli cannot hurt anything");
defaultRuleSet.AddRule("players cannot hurt players");
defaultRuleSet.AddRule("players cannot hurt traps");
defaultRuleSet.AddRule("guards cannot hurt players");
defaultRuleSet.AddRule("fire cannot hurt players");
defaultRuleSet.AddRule("traps cannot hurt players");
defaultRuleSet.AddRule("highwalls cannot hurt players");
defaultRuleSet.AddRule("barricades cannot hurt players");
defaultRuleSet.AddRule("mini cannot hurt mini");
defaultRuleSet.AddRule("npcs can hurt players");
defaultRuleSet.AddRule("junkyard cannot hurt anything");
defaultRuleSet.AddRule("junkyard can hurt cars");
defaultRuleSet.AddRule("players cannot hurt tugboats");
defaultRuleSet.AddRule("heliturrets cannot hurt players");
config.ruleSets.Add(defaultRuleSet);
// add ruleset to rulesets list
config.mappings[config.defaultRuleSet] = config.defaultRuleSet;
// create mapping for ruleset
return true;
}
private bool ResetRules(string key) {
if (string.IsNullOrEmpty(key) || config == null) {
return false;
}
string old = config.defaultRuleSet;
config.defaultRuleSet = key;
currentRuleSet = config.GetDefaultRuleSet();
if (currentRuleSet == null) {
config.defaultRuleSet = old;
currentRuleSet = config.GetDefaultRuleSet();
return false;
}
return true;
}
#endregion
#region Trace
private StringBuilder _tsb = new StringBuilder();
private BaseEntity traceEntity;
private BasePlayer tracePlayer;
private const int _tracesToServerConsoleLimit = 50;
private int _tracesToServerConsole = 0;
private void Trace(string message, int indentation = 0) {
if (config.options.PlayerConsole || config.options.ServerConsole) {
if (tracePlayer.IsReallyConnected() && traceEntity != null) {
if (config.options.MaxTraceDistance == 0 || tracePlayer.Distance(traceEntity) <= config.options.MaxTraceDistance) {
if (config.options.PlayerConsole) {
tracePlayer.ConsoleMessage(message);
}
if (config.options.ServerConsole && ++_tracesToServerConsole <= _tracesToServerConsoleLimit) {
Puts(message);
}
_tsb.AppendLine(string.Empty.PadLeft(indentation, ' ') + message);
}
}
}
else _tsb.AppendLine(string.Empty.PadLeft(indentation, ' ') + message);
}
private void LogTrace() {
var text = _tsb.ToString();
traceEntity = null;
try {
if (!string.IsNullOrEmpty(text)) {
LogToFile(traceFile, text, this);
}
}
catch (IOException) {
timer.Once(1f, () => LogToFile(traceFile, text, this));
return;
}
_tsb.Length = 0;
}
#endregion Trace
#region Hooks/Handler Procedures
private void OnPlayerConnected(BasePlayer player) {
SendReply(player, GetMessage("Prefix") + currentBroadcastMessage);
}
private string CurrentRuleSetName() => currentRuleSet?.name;
private bool IsEnabled() => tpveEnabled;
// handle damage - if another mod must override TruePVE damages or take priority,
// set handleDamage to false and reference HandleDamage from the other mod(s)
private object OnEntityTakeDamage(ResourceEntity entity, HitInfo hitInfo) {
// if default global is not enabled, return true (allow all damage)
if (hitInfo == null || currentRuleSet == null || currentRuleSet.IsEmpty() || !currentRuleSet.enabled) {
return null;
}
// get entity and initiator locations (zones)
List<string> entityLocations = GetLocationKeys(entity);
List<string> initiatorLocations = GetLocationKeys(hitInfo.Initiator);
// check for exclusion zones (zones with no rules mapped)
if (CheckExclusion(entityLocations, initiatorLocations, trace)) {
if (trace) Trace("Exclusion found; allow and return", 1);
return null;
}
if (trace) Trace("No exclusion found - looking up RuleSet...", 1);
// process location rules
RuleSet ruleSet = GetRuleSet(entityLocations, initiatorLocations);
return EvaluateRules(entity, hitInfo, ruleSet) ? (object)null : true;
}
private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo) {
if (entity.IsNull() || entity.IsDestroyed || hitInfo == null || AllowKillingSleepers(entity, hitInfo)) {
return null;
}
object extCanTakeDamage = Interface.CallHook("CanEntityTakeDamage", new object[] {
entity, hitInfo
}
);
if (extCanTakeDamage is bool) {
if ((bool)extCanTakeDamage) {
return null;
}
CancelHit(hitInfo);
return true;
}
if (!config.options.handleDamage || hitInfo.damageTypes.Total() <= 0f) {
return null;
}
var majority = hitInfo.damageTypes.GetMajorityDamageType();
if (majority == DamageType.Decay || majority == DamageType.Fall || majority == DamageType.Radiation) {
return null;
}
if (!AllowDamage(entity, hitInfo)) {
if (trace) LogTrace();
CancelHit(hitInfo);
return true;
}
if (trace) LogTrace();
return null;
}
private void CancelHit(HitInfo hitInfo) {
hitInfo.damageTypes = new DamageTypeList();
hitInfo.DidHit = false;
hitInfo.DoHitEffects = false;
}
private bool AllowKillingSleepers(BaseCombatEntity entity, HitInfo hitInfo) {
if (entity.ShortPrefabName == "player" && (config.AllowKillingSleepersAlly || config.AllowKillingSleepers || config.AllowKillingSleepersAuthorization)) {
var victim = entity.ToPlayer();
if (!victim.IsSleeping()) {
return false;
}
if (config.AllowKillingSleepersAuthorization && hitInfo.Initiator is BasePlayer && AllowAuthorizationDamage(victim, hitInfo.InitiatorPlayer)) {
return true;
}
if (config.AllowKillingSleepersAlly && hitInfo.Initiator is BasePlayer) {
return victim.IsSleeping() && IsAlly(victim.userID, hitInfo.InitiatorPlayer.userID);
}
if (config.AllowKillingSleepers) {
return victim.IsSleeping();
}
}
return false;
}
private bool AllowAuthorizationDamage(BasePlayer victim, BasePlayer attacker) {
if (!attacker.userID.IsSteamId()) {
return false;
}
var tugboat = victim.GetParentEntity() as Tugboat;
if (tugboat != null && tugboat.IsAuthed(attacker)) {
return true;
}
var priv = victim.GetBuildingPrivilege();
if (priv != null && priv.IsAuthed(attacker)) {
return true;
}
return false;
}
// determines if an entity is "allowed" to take damage
private bool AllowDamage(BaseCombatEntity entity, HitInfo hitInfo) {
if (trace) {
traceEntity = entity;
_tsb.Length = 0;
}
// if default global is not enabled or entity is npc, allow all damage
if (currentRuleSet == null || currentRuleSet.IsEmpty() || !currentRuleSet.enabled || entity is BaseNpc) {
return true;
}
// allow damage to door barricades and covers
if (entity is TrainBarricade || entity is Barricade && (entity.ShortPrefabName.Contains("door_barricade") || entity.ShortPrefabName.Contains("cover"))) {
return true;
}
// if entity is a barrel, trash can, or giftbox, allow damage (exclude water and hobo barrels)
if (!entity.ShortPrefabName.Equals("waterbarrel") && entity.prefabID != 1748062128 && (entity.ShortPrefabName.Contains("barrel") || entity.ShortPrefabName.Equals("loot_trash") || entity.ShortPrefabName.Equals("giftbox_loot"))) {
return true;
}
var weapon = hitInfo.Initiator ?? hitInfo.Weapon ?? hitInfo.WeaponPrefab;
TrySetInitiator(hitInfo, weapon);
if (trace) {
// Sometimes the initiator is not the attacker (turrets)
Trace("======================" + Environment.NewLine + "== STARTING TRACE ==" + Environment.NewLine + "== " + DateTime.Now.ToString("HH:mm:ss.fffff") + " ==" + Environment.NewLine + "======================");
Trace($"From: {weapon?.GetType().Name ?? "Unknown_Weapon"}, {weapon?.ShortPrefabName ?? "Unknown_Prefab"}", 1);
Trace($"To: {entity.GetType().Name}, {entity.ShortPrefabName}", 1);
}
// get entity and initiator locations (zones)
List<string> entityLocations = GetLocationKeys(entity);
List<string> initiatorLocations = GetLocationKeys(weapon);
// check for exclusion zones (zones with no rules mapped)
if (CheckExclusion(entityLocations, initiatorLocations, trace)) {
if (trace) Trace("Exclusion found; allow and return", 1);
return true;
}
if (trace) Trace("No exclusion found - looking up RuleSet...", 1);
// process location rules
RuleSet ruleSet = GetRuleSet(entityLocations, initiatorLocations);
if (trace) Trace($"Using RuleSet \"{ruleSet.name}\"", 1);
if (entity is PatrolHelicopter) {
if (weapon is BasePlayer) {
bool isBlocked = !EvaluateRules(entity, weapon, ruleSet, false);
if (trace) Trace($"Target is PatrolHelicopter; {(isBlocked ? "block and return" : "allow and return")}", 1);
return !isBlocked;
}
return true;
}
if (weapon?.ShortPrefabName == "maincannonshell" || weapon is BradleyAPC) {
if (trace) Trace("Initiator is BradleyAPC; evaluating RuleSet rules...", 1);
return EvaluateRules(entity, weapon, ruleSet);
}
if (ruleSet.HasFlag(RuleFlags.VehiclesTakeCollisionDamageWithoutDriver) && entity is BaseVehicle && weapon == entity && (entity as BaseVehicle).GetDriver() == null) {
if (trace) Trace($"Vehicle collision: No driver; allow and return", 1);
return true;
}
// Check storage containers and doors for locks
if (ruleSet.HasFlag(RuleFlags.LockedBoxesImmortal) && entity is StorageContainer && !(entity is LootContainer) || ruleSet.HasFlag(RuleFlags.LockedDoorsImmortal) && entity is Door) {
object hurt = CheckLock(ruleSet, entity, hitInfo);
// check for lock
if (trace) Trace($"Door/StorageContainer detected with immortal flag; lock check results: {(hurt == null ? "null (no lock or unlocked);
continue checks" : (bool)hurt ? "allow and return" : "block and return")}", 1);
if (hurt is bool) return (bool)hurt;
}
// check heli and turret
var heli = CheckHeliInitiator(ruleSet, hitInfo);
if (config.Firework && entity is BaseFirework) {
if (heli is bool) return false;
return true;
}
if (heli is bool) {
return HandleHelicopter(ruleSet, entity, weapon, (bool)heli);
}
// after heli check, return true if initiator is null
if (hitInfo.Initiator.IsRealNull()) {
if (damageTypes.Exists(x => hitInfo.damageTypes.Get(x) > 0f)) {
if (trace) Trace($"Initiator empty for player damage; block and return", 1);
return false;
}
if (weapon is MLRSRocket) {
if (trace) Trace($"Initiator empty for MLRS Rocket; block and return", 1);
return false;
}
if (trace) Trace($"Initiator empty; allow and return", 1);
return true;
}
if (hitInfo.Initiator is SamSite && (entity is BasePlayer || entity is BaseMountable)) {
if (CheckExclusion(hitInfo.Initiator)) {
if (trace) Trace($"Initiator is samsite, and target is player; exclusion found; allow and return", 1);
return true;
}
bool isAllowed = (hitInfo.Initiator as SamSite).staticRespawn ? !ruleSet.HasFlag(RuleFlags.StaticSamSitesIgnorePlayers) : !ruleSet.HasFlag(RuleFlags.PlayerSamSitesIgnorePlayers);
if (trace) Trace($"Initiator is samsite, and target is player; {(isAllowed ? "flag not set;
allow and return" : "flag set;
block and return")}", 1);
return isAllowed;
}
var isVictim = entity is BasePlayer;
var isAttacker = hitInfo.Initiator is BasePlayer;
var attacker = isAttacker ? hitInfo.Initiator as BasePlayer : null;
if (isAttacker && !attacker.userID.IsSteamId() || hitInfo.Initiator is BaseNpc) {
if (isVictim && ruleSet.HasFlag(RuleFlags.ProtectedSleepers) && entity.ToPlayer().IsSleeping()) {
if (trace) Trace("Target is sleeping player, with ProtectedSleepers flag set; block and return", 1);
return false;
}
if (ruleSet.HasFlag(RuleFlags.NpcsCanHurtAnything)) {
if (trace) Trace("Initiator is NPC; flag set; allow damage and return", 1);
return true;
}
}
if (isVictim) {
var victim = entity as BasePlayer;
if (ruleSet.HasFlag(RuleFlags.SafeZoneTurretsIgnorePlayers) && hitInfo.Initiator is NPCAutoTurret && hitInfo.Initiator.OwnerID == 0) {
return true;
}
if (!ruleSet.HasFlag(RuleFlags.StaticTurretsIgnorePlayers) && hitInfo.Initiator is AutoTurret && !hitInfo.Initiator.OwnerID.IsSteamId()) {
return true;
}
var damageType = hitInfo.damageTypes.GetMajorityDamageType();
if ((damageType == DamageType.Slash || damageType == DamageType.Stab) && hitInfo.Initiator == null) {
var lastAttacker = entity.lastAttacker as BasePlayer;
if (lastAttacker == null || !lastAttacker.userID.IsSteamId()) {
if (trace) Trace("Initiator is hurt trigger; allow damage and return", 1);
return true;
}
}
if (entity == attacker && damageType == DamageType.Bullet && hitInfo.damageTypes.Total() < 0f) {
return true;
// ... [Code preview truncated due to length. Full source in attached file] ...