C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.RegularExpressions;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Core.Configuration;
using Oxide.Core.Libraries.Covalence;
using Oxide.Game.Rust.Libraries.Covalence;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using UnityEngine;
using Rust;
using Facepunch;
namespace Oxide.Plugins
{
[Info("RaidNotes", "Calytic", "1.0.0", ResourceId = 2117)]
[Description("Broadcasts raid activity to chat & more")]
public class RaidNotes : RustPlugin
{
#region Variables
[PluginReference]
Plugin Discord, Slack, Clans, LustyMap;
private DynamicConfigFile data;
public static JsonSerializer SERIALIZER = new JsonSerializer();
public static JsonConverter[] CONVERTERS = new JsonConverter[] { new UnityVector3Converter(), new DateTimeConverter()};
Dictionary<string, bool> _raidableCache = new Dictionary<string, bool>();
private int blockLayer = UnityEngine.LayerMask.GetMask(new string[] { "Player (Server)" });
private static Dictionary<string, int> reverseItems = new Dictionary<string, int>();
private static List<string> explosionRadiusPrefabs = new List<string>();
private Dictionary<Raid, Timer> timers = new Dictionary<Raid, Timer>();
private Dictionary<string, DateTime> detectionCooldowns = new Dictionary<string, DateTime>();
static Regex _htmlRegex = new Regex("<.*?>", RegexOptions.Compiled);
Color[] colors = new Color[7] { Color.blue, Color.cyan, Color.gray, Color.green, Color.magenta, Color.red, Color.yellow };
private float raidDuration = 300f;
private float raidDistance = 50f;
private float shoulderHeight = 0.45f;
private bool isNewSave = false;
private float detectionDuration = 60f;
private string slackType = "FancyMessage";
private int logHours = 12;
private bool trackExplosives = true;
private bool checkEntityDamage, checkEntityDeath, announceGlobal, printToLog = true;
private bool announceRaidStart, announceRaidEnd, announceToVictims, announceToSlack, announceToDiscord, announceToLustyMap, announceClan = false;
private string announcePrefixColor = "orange";
private string announceIcon;
private string announceNameColor = "lightblue";
private string announceClanColor = "#00eaff";
private string lustyMapIcon = "special";
private float lustyMapDuration = 10f;
private int detectionDistance = 50;
private float detectionCountdown = 1f;
private Dictionary<string, object> weaponColors = new Dictionary<string, object>() {
{"0", "#666333"},
{"498591726", "#666666"}
};
private Dictionary<string, object> gradeColors = new Dictionary<string, object>() {
{"Wood", "#a68b44"},
{"Stone", "#a4a4a4"},
{"Metal", "#9b5050"},
{"TopTier", "#473131"}
};
private float announceDelay, announceRadius = 0f;
internal int announceMinParticipants, announceMinWeapons, announceMinKills, announceMinDestroyed, announceMinMinutes = 0;
List<string> prefabs = new List<string>()
{
"door.hinged",
"door.double.hinged",
"window.bars",
"floor.ladder.hatch",
"floor.frame",
"wall.frame",
"shutter"
};
Dictionary<long, Raid> raids = new Dictionary<long, Raid>();
#endregion
#region Classes
public class AttackVector {
public Vector3 start;
public Vector3 end;
public int weapon;
public ulong victim;
public ulong initiator;
[JsonConstructor]
public AttackVector(Vector3 start, Vector3 end, int weapon, ulong victim = 0, ulong initiator = 0) {
this.start = start;
this.end = end;
this.weapon = weapon;
this.victim = victim;
this.initiator = initiator;
}
}
public class Raid
{
public long start = DateTime.Now.ToBinary();
public long end = 0;
public Vector3 firstDamage;
public Vector3 lastDamage;
public List<AttackVector> attackVector = new List<AttackVector>();
public List<AttackVector> killMap = new List<AttackVector>();
public ulong initiator;
public ulong victim;
public List<ulong> blockOwners = new List<ulong>();
public List<ulong> participants = new List<ulong>();
public int lastWeapon;
public Dictionary<int, int> weapons = new Dictionary<int, int>();
public Dictionary<BuildingGrade.Enum, int> blocksDestroyed = new Dictionary<BuildingGrade.Enum,int>();
public Dictionary<string, int> entitiesDestroyed = new Dictionary<string,int>();
[JsonConstructor]
public Raid(
long start,
long end,
Vector3 firstDamage,
Vector3 lastDamage,
List<AttackVector> attackVector,
ulong initiator,
ulong victim,
List<ulong> blockOwners,
List<ulong> participants,
int lastWeapon,
Dictionary<int, int> weapons,
List<AttackVector> killMap = null,
Dictionary<BuildingGrade.Enum, int> blocksDestroyed = null,
Dictionary<string, int> entitiesDestroyed = null
) {
this.start = start;
this.end = end;
this.firstDamage = firstDamage;
this.lastDamage = lastDamage;
this.attackVector = attackVector;
this.initiator = initiator;
this.victim = victim;
this.blockOwners = blockOwners;
this.participants = participants;
this.lastWeapon = lastWeapon;
this.weapons = weapons;
if(killMap != null) {
this.killMap = killMap;
}
if(blocksDestroyed != null) {
this.blocksDestroyed = blocksDestroyed;
}
if(entitiesDestroyed != null) {
this.entitiesDestroyed = entitiesDestroyed;
}
}
[JsonIgnore]
internal AttackVector lastAttackVector = null;
[JsonIgnore]
RaidNotes plugin;
[JsonIgnore]
public bool Completed
{
get
{
return end != 0;
}
}
[JsonIgnore]
public DateTime lastRefresh;
[JsonIgnore]
public IPlayer Initiator
{
get
{
return plugin.covalence.Players.FindPlayerById(initiator.ToString());
}
}
[JsonIgnore]
public IPlayer Victim
{
get
{
return plugin.covalence.Players.FindPlayerById(victim.ToString());
}
}
[JsonIgnore]
[JsonConverter(typeof(IsoDateTimeConverter))]
public DateTime Start {
get {
return DateTime.FromBinary(start);
}
}
[JsonIgnore]
[JsonConverter(typeof(IsoDateTimeConverter))]
public DateTime End {
get {
return DateTime.FromBinary(end);
}
}
public Raid(RaidNotes plugin, ulong initiator, ulong victim, Vector3 firstDamage)
{
this.plugin = plugin;
this.initiator = initiator;
this.victim = victim;
this.firstDamage = firstDamage;
}
[JsonIgnore]
public double Hours
{
get
{
if(!Completed) {
return 0;
}
var ts = DateTime.Now - End;
return ts.TotalHours;
}
}
public bool HasHours(int hours)
{
if (Hours >= hours)
{
return true;
}
return false;
}
public void Participate(BasePlayer player) {
var behavior = player.gameObject.AddComponent<RaidBehavior>();
behavior.raid = this;
if (!participants.Contains(player.userID))
{
participants.Add(player.userID);
}
}
public bool IsAnnounced()
{
if (participants.Count < plugin.announceMinParticipants) return false;
if (killMap.Count < plugin.announceMinKills) return false;
if (weapons.Count < plugin.announceMinWeapons) return false;
if ((entitiesDestroyed.Count + blocksDestroyed.Count) < plugin.announceMinDestroyed) return false;
var ts = End - Start;
if (ts.TotalMinutes < plugin.announceMinMinutes) return false;
return true;
}
internal JObject Vector2JObject(Vector3 vector)
{
var obj = new JObject();
obj.Add("x", vector.x);
obj.Add("y", vector.y);
obj.Add("z", vector.z);
return obj;
}
public override string ToString()
{
return ToJObject().ToString();
}
internal JObject ToJObject()
{
var obj = new JObject();
obj["start"] = Start.ToString();
obj["end"] = End.ToString();
var explosions = new JObject();
explosions.Add("first", Vector2JObject(firstDamage));
explosions.Add("last", Vector2JObject(lastDamage));
obj["explosions"] = explosions;
obj["initiator"] = initiator;
obj["victim"] = victim;
JArray owners = new JArray();
foreach (var owner in blockOwners)
owners.Add(owner);
obj["owners"] = owners;
JArray participantsData = new JArray();
foreach (var participant in participants)
participantsData.Add(participant);
obj["participants"] = participantsData;
JObject weaponsData = new JObject();
foreach (var kvp in weapons)
weaponsData.Add(kvp.Key.ToString(), kvp.Value);
obj["weapons"] = weaponsData;
obj["attackvector"] = JArray.FromObject(attackVector, SERIALIZER);
obj["kills"] = JArray.FromObject(killMap, SERIALIZER);
return obj;
}
internal void OnEnded()
{
end = DateTime.Now.ToBinary();
Interface.CallHook("OnRaidEnded", ToJObject());
}
internal void OnStarted()
{
Interface.CallHook("OnRaidStarted", ToJObject());
}
internal void Attack(AttackVector vector) {
lastAttackVector = vector;
attackVector.Add(vector);
}
internal void Kill(AttackVector vector) {
killMap.Add(vector);
}
}
public class RaidBehavior : MonoBehaviour
{
public BasePlayer player;
internal Raid raid;
void Awake()
{
player = GetComponent<BasePlayer>();
}
void OnDestroy()
{
GameObject.Destroy(this);
}
}
public class ExplosiveTracker : MonoBehaviour {
public BaseEntity entity;
public Vector3 lastValidPosition;
public BasePlayer thrownBy;
public Vector3 thrownFrom;
void Awake() {
entity = GetComponent<BaseEntity>();
lastValidPosition = entity.transform.position;
}
void Update() {
if(Vector3.Distance(entity.transform.position, Vector3.zero) > 3) {
lastValidPosition = entity.transform.position;
}
}
void OnDestroy()
{
if(thrownBy == null) return;
var behavior = thrownBy.GetComponent<RaidBehavior>();
if(behavior != null && behavior.raid != null) {
int itemid;
if(reverseItems.TryGetValue(entity.PrefabName, out itemid))
behavior.raid.Attack(new AttackVector(thrownFrom, lastValidPosition, itemid, 0, thrownBy.userID));
}
GameObject.Destroy(this);
}
}
enum AnnouncementType {
Start = 1,
End = 2,
Slack_Start = 3,
Slack_End = 4,
Discord_Start = 5,
Discord_End = 6
}
#endregion
#region Initialization
protected override void LoadDefaultConfig()
{
PrintToConsole("Creating new configuration");
Config.Clear();
Config["Settings", "trackEntityDamage"] = true;
Config["Settings", "trackEntityDeath"] = true;
Config["Settings","trackExplosives"] = true;
Config["Raid", "distance"] = 50f;
Config["Raid", "duration"] = 300f;
Config["Raid", "logUpToHours"] = 3;
Config["Raid", "detectionDistance"] = 50;
Config["Raid", "detectionDuration"] = 60f;
Config["Raid", "detectionCountdownMinutes"] = 1f;
Config["AnnounceWhen", "raidEnds"] = false;
Config["AnnounceWhen", "raidStarts"] = false;
Config["AnnounceWhen", "minParticipants"] = 0;
Config["AnnounceWhen", "minWeapons"] = 0;
Config["AnnounceWhen", "minKills"] = 0;
Config["AnnounceWhen", "minMinutes"] = 0;
Config["AnnounceTo", "global"] = false;
Config["AnnounceTo", "clan"] = true;
Config["AnnounceTo", "victims"] = true;
Config["AnnounceTo", "slack"] = false;
Config["AnnounceTo", "discord"] = false;
Config["AnnounceTo", "lustymap"] = false;
Config["AnnounceTo", "log"] = true;
Config["Announcements","icon"] = 0;
Config["Announcements","prefixColor"] = "orange";
Config["Announcements","nameColor"] = "lightblue";
Config["Announcements","clanColor"] = "#00eaff";
Config["Announcements","weaponColors"] = weaponColors;
Config["Announcements","delay"] = 0f;
Config["Announcements","radius"] = 0f;
Config["LustyMap","icon"] = "special";
Config["LustyMap","duration"] = 10f;
Config["Slack","messageType"] = "FancyMessage";
}
void LoadMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
{"Announce: Prefix", "Raid"},
{"Announce: Start", "{initiatorClan} {initiator} ({initiatorClanMates}) is raiding {victimClan} {victim} ({victimClanMates})"},
{"Announce: End", "{initiatorClan} {initiator} ({initiatorClanMates}) raided {victimClan} {victim} ({victimClanMates}) using {weaponList} destroying {destroyedList}"},
{"Announce: Slack Start", "{initiatorClan} {initiator} ({initiatorClanMates}) is raiding {victimClan} {victim} ({victimClanMates})"},
{"Announce: Slack End", "{initiatorClan} {initiator} ({initiatorClanMates}) raided {victimClan} {victim} ({victimClanMates}) with {weaponList} destroying {destroyedList}"},
{"Announce: Discord Start", "{initiatorClan} {initiator} ({initiatorClanMates}) is raiding {victimClan} {victim} ({victimClanMates})"},
{"Announce: Discord End", "{initiatorClan} {initiator} ({initiatorClanMates}) raided {victimClan} {victim} ({victimClanMates}) with {weaponList} destroying {destroyedList}"},
{"Denied: Permission", "You lack permission to do that"},
{"Raid: Found", "<size=15>Raid(s) found: {raidCount}</size>"},
{"Raid: Started", "Started: <b>{date}</b>"},
{"Raid: Ended", "Ended: <b>{date}</b>"},
{"Raid: Duration", "Duration: <b>{duration}</b> minutes"},
{"Raid: Initiator", "Initiator: <b>{initiatorName}</b> ({initiatorID})"},
{"Raid: PlayerList", "{listName}: <b>{list}</b>"},
{"Raid: Activity", "Raid"},
{"Target: Nothing", "Nothing"},
{"Cooldown: Seconds", "You are doing that too often, try again in a {0} seconds(s)."},
{"Cooldown: Minutes", "You are doing that too often, try again in a {0} minute(s)."},
}, this);
}
void OnServerInitialized()
{
LoadData();
LoadMessages();
foreach(JsonConverter converter in CONVERTERS) {
SERIALIZER.Converters.Add(converter);
}
permission.RegisterPermission("raidnotes.inspect", this);
raidDistance = GetConfig("Raid","distance", 50f);
raidDuration = GetConfig("Raid","duration", 300f);
logHours = GetConfig("Raid", "logUpToHours", 3);
detectionDistance = GetConfig("Raid", "detectionDistance", 50);
detectionDuration = GetConfig("Raid", "detectionDuration", 60f);
detectionDuration = GetConfig("Raid", "detectionCountdownMinutes", 1f);
checkEntityDamage = GetConfig("Settings","hookEntityDamage", true);
checkEntityDeath = GetConfig("Settings","hookEntityDeath", true);
trackExplosives = GetConfig("Settings","trackExplosives", true);
announceGlobal = GetConfig("AnnounceTo","global", false);
announceClan = GetConfig("AnnounceTo","clan", true);
announceToVictims = GetConfig("AnnounceTo","victims", true);
announceToSlack = GetConfig("AnnounceTo","slack", false);
announceToLustyMap = GetConfig("AnnounceTo","lustymap", false);
announceToDiscord = GetConfig("AnnounceTo","discord", false);
printToLog = GetConfig("AnnounceTo","log", true);
announceRaidEnd = GetConfig("AnnounceWhen","raidEnds", false);
announceRaidStart = GetConfig("AnnounceWhen","raidStarts", false);
announceMinParticipants = GetConfig("AnnounceWhen","minParticipants", 0);
announceMinWeapons = GetConfig("AnnounceWhen","minWeapons", 0);
announceMinDestroyed = GetConfig("AnnounceWhen","minDestroyed", 0);
announceMinKills = GetConfig("AnnounceWhen","minKills", 0);
announceMinMinutes = GetConfig("AnnounceWhen","minMinutes", 0);
announceIcon = GetConfig("Announcements","icon", "0");
announcePrefixColor = GetConfig("Announcements","prefixColor", "orange");
announceNameColor = GetConfig("Announcements","nameColor", "lightblue");
announceClanColor = GetConfig("Announcements","clanColor", "#00eaff");
weaponColors = GetConfig("Announcements","weaponColors", weaponColors);
announceDelay = GetConfig("Announcements","delay", 0f);
announceRadius = GetConfig("Announcements","radius", 0f);
lustyMapIcon = GetConfig("LustyMap","icon", "special");
lustyMapDuration = GetConfig("LustyMap","duration", 10f);
slackType = GetConfig("Slack","messageType", "FancyMessage");
if (announceToSlack && !Slack)
{
PrintWarning("Slack plugin not found, please install http://oxidemod.org/plugins/slack.1952/");
announceToSlack = false;
}
if (announceToDiscord && !Discord)
{
PrintWarning("Discord plugin not found, please install http://oxidemod.org/plugins/discord.2149/");
announceToDiscord = false;
}
if(announceToLustyMap && !LustyMap) {
PrintWarning("LustyMap plugin not found, please install http://oxidemod.org/plugins/lustymap.1333/");
}
if (logHours == 0)
{
Unsubscribe(nameof(OnServerSave));
}
if(!trackExplosives) {
Unsubscribe(nameof(OnExplosiveThrown));
Unsubscribe(nameof(OnRocketLaunched));
}
foreach (ItemDefinition def in ItemManager.GetItemDefinitions())
{
var modEntity = def.GetComponent<ItemModEntity>();
if (modEntity != null && modEntity.entityPrefab != null)
{
var prefab = modEntity.entityPrefab.Get();
var thrownWeapon = prefab.GetComponent<ThrownWeapon>();
if (thrownWeapon != null && !string.IsNullOrEmpty(thrownWeapon.prefabToThrow.guid) && !reverseItems.ContainsKey(thrownWeapon.prefabToThrow.resourcePath))
{
reverseItems.Add(thrownWeapon.prefabToThrow.resourcePath, def.itemid);
continue;
}
}
var baseProjectile = def.GetComponent<ItemModProjectile>();
if (baseProjectile != null && !string.IsNullOrEmpty(baseProjectile.projectileObject.guid) && !reverseItems.ContainsKey(baseProjectile.projectileObject.resourcePath))
{
if (baseProjectile.projectileObject.resourcePath.Contains("rocket") && !baseProjectile.projectileObject.resourcePath.Contains("smoke"))
{
explosionRadiusPrefabs.Add(baseProjectile.projectileObject.resourcePath);
reverseItems.Add(baseProjectile.projectileObject.resourcePath, def.itemid);
}
}
}
if (announceClan)
{
if (!plugins.Exists("Clans"))
{
announceClan = false;
PrintWarning("Clans plugin not found, please install http://oxidemod.org/plugins/clans.2087/");
}
}
}
protected void ReloadConfig()
{
Config["VERSION"] = Version.ToString();
ClearData();
PrintToConsole("Upgrading configuration file");
SaveConfig();
}
void LoadData()
{
if (logHours > 0)
{
data = Interface.Oxide.DataFileSystem.GetFile(nameof(RaidNotes));
data.Settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
data.Settings.Converters = CONVERTERS;
raids = data.ReadObject<Dictionary<long, Raid>>();
if(isNewSave) {
ClearData();
isNewSave = false;
}
}
if (Config["VERSION"] == null)
{
// FOR COMPATIBILITY WITH INITIAL VERSIONS WITHOUT VERSIONED CONFIG
ReloadConfig();
}
else if (GetConfig<string>("VERSION", "") != Version.ToString())
{
// ADDS NEW, IF ANY, CONFIGURATION OPTIONS
ReloadConfig();
}
}
void SaveData(bool force = false)
{
if(raids.Count > 0 && !force) {
var toRemove = raids.Where(pair => pair.Value.Hours > logHours).Select(pair => pair.Key).ToList();
foreach (var key in toRemove)
raids.Remove(key);
}
data.WriteObject<Dictionary<long, Raid>>(raids);
}
void ClearData()
{
raids.Clear();
SaveData();
}
void OnNewSave(string filename)
{
isNewSave = true;
}
void OnServerSave()
{
SaveData();
}
void Unload()
{
var objects = GameObject.FindObjectsOfType(typeof(RaidBehavior));
if (objects != null)
foreach (var gameObj in objects)
GameObject.Destroy(gameObj);
objects = GameObject.FindObjectsOfType(typeof(ExplosiveTracker));
if (objects != null)
foreach (var gameObj in objects)
GameObject.Destroy(gameObj);
if(logHours > 0) {
SaveData(true);
}
}
#endregion
#region Oxide Hooks
void OnExplosiveThrown(BasePlayer player, BaseEntity entity)
{
if (player == null || entity.net == null) return;
AddTracker(player, entity);
}
void OnRocketLaunched(BasePlayer player, BaseEntity entity)
{
if(player == null || entity.net == null || !reverseItems.ContainsKey(entity.PrefabName)) return;
AddTracker(player, entity);
}
List<uint> recentAttacks = new List<uint>();
private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo hitInfo)
{
if (!checkEntityDamage) return;
if (hitInfo == null ||
hitInfo.Initiator == null ||
hitInfo.WeaponPrefab == null ||
!IsEntityRaidable(entity))
return;
var prefabName = hitInfo.WeaponPrefab.PrefabName;
if(explosionRadiusPrefabs.Contains(prefabName)) {
if(recentAttacks.Contains(hitInfo.Initiator.net.ID)) {
return;
} else {
recentAttacks.Add(hitInfo.Initiator.net.ID);
}
Interface.Oxide.NextTick(delegate() {
if(recentAttacks.Contains(hitInfo.Initiator.net.ID)) {
recentAttacks.Remove(hitInfo.Initiator.net.ID);
}
});
}
int itemUsed;
if (!reverseItems.TryGetValue(prefabName, out itemUsed))
return;
var majorityDamageType = hitInfo.damageTypes.GetMajorityDamageType();
switch (majorityDamageType)
{
case DamageType.Explosion:
case DamageType.Heat:
StructureAttack(entity, hitInfo.Initiator, itemUsed);
break;
}
}
private void OnEntityDeath(BaseCombatEntity entity, HitInfo hitInfo)
{
if (!checkEntityDeath || hitInfo == null || hitInfo.WeaponPrefab == null || hitInfo.Initiator == null || !(hitInfo.Initiator is BasePlayer)) return;
if (IsEntityRaidable(entity))
{
var majorityDamageType = hitInfo.damageTypes.GetMajorityDamageType();
var prefabName = hitInfo.WeaponPrefab.PrefabName;
int itemUsed;
if (reverseItems.TryGetValue(prefabName, out itemUsed))
{
switch (majorityDamageType)
{
case DamageType.Explosion:
case DamageType.Heat:
StructureAttack(entity, hitInfo.Initiator as BasePlayer, itemUsed, true);
break;
}
}
}
else if (entity is BasePlayer)
RegisterKill(entity as BasePlayer, hitInfo.Initiator as BasePlayer);
}
#endregion
#region Commands
[ConsoleCommand("raids.wipe")]
private void ccRaidsWipe(ConsoleSystem.Arg arg)
{
if (arg.Connection == null || (arg.Connection != null && arg.Connection.authLevel > 0)) {
ClearData();
SendReply(arg, "Data wiped");
return;
}
SendReply(arg, GetMsg("Denied: Permission"));
}
[ChatCommand("inspect")]
private void cmdInspect(BasePlayer player, string command, string[] args)
{
var permission = HasPerm(player, "raidnotes.inspect");
if (permission || (!permission && player.net.connection.authLevel > 0))
{
if (!CheckCooldown(player))
return;
else
PlayerCooldown(player);
SendRaids(player, args);
return;
}
SendReply(player, GetMsg("Denied: Permission", player));
}
#endregion
#region Core Methods
void PlayerCooldown(BasePlayer player)
{
if (player.IsAdmin)
return;
if (detectionCooldowns.ContainsKey(player.UserIDString))
detectionCooldowns.Remove(player.UserIDString);
detectionCooldowns.Add(player.UserIDString, DateTime.Now);
}
bool CheckCooldown(BasePlayer player)
{
if (detectionCountdown > 0)
{
DateTime startTime;
if (detectionCooldowns.TryGetValue(player.UserIDString, out startTime))
{
var endTime = DateTime.Now;
var span = endTime.Subtract(startTime);
if (span.TotalMinutes > 0 && span.TotalMinutes < Convert.ToDouble(detectionCountdown))
{
var timeleft = System.Math.Round(Convert.ToDouble(detectionCountdown) - span.TotalMinutes, 2);
if (timeleft < 1)
{
var timelefts = System.Math.Round((Convert.ToDouble(detectionCountdown) * 60) - span.TotalSeconds);
SendReply(player, string.Format(GetMsg("Cooldown: Seconds", player), timelefts.ToString()));
}
else
SendReply(player, string.Format(GetMsg("Cooldown: Minutes", player), System.Math.Round(timeleft).ToString()));
return false;
}
else
detectionCooldowns.Remove(player.UserIDString);
}
}
return true;
}
void AddTracker(BasePlayer player, BaseEntity entity)
{
var tracker = entity.gameObject.AddComponent<ExplosiveTracker>();
tracker.thrownBy = player;
tracker.thrownFrom = player.GetEstimatedWorldPosition();
tracker.thrownFrom.y += player.GetHeight() * shoulderHeight;
}
Vector3 Track(BaseEntity initiator, BaseEntity entity) {
var fromPos = Vector3.zero;
var tracker = trackExplosives ? initiator.gameObject.GetComponent<ExplosiveTracker>() : null;
if(tracker != null)
fromPos = tracker.thrownFrom;
else
fromPos = initiator.GetEstimatedWorldPosition();
return fromPos;
}
List<Raid> GetRaids(BasePlayer player, string[] args) {
var defaultDistance = detectionDistance;
if(args != null && args.Length == 1)
int.TryParse(args[0], out defaultDistance);
if(defaultDistance > detectionDistance)
defaultDistance = detectionDistance;
return raids.Where(pair => Vector3.Distance(pair.Value.firstDamage, player.transform.position) <= defaultDistance).Select(pair => pair.Value).ToList();
}
void SendRaids(BasePlayer player, string[] args)
{
if(player.net.connection == null)
return;
var nearbyRaids = GetRaids(player, args);
if(nearbyRaids.Count > 0) {
int found = 0;
var sbs = new List<StringBuilder>();
foreach (var raid in nearbyRaids) {
var sb = new StringBuilder();
if(SendRaid(player, raid, sb, found)) {
found++;
sbs.Add(sb);
}
}
if(found > 0) {
SendReply(player, Format(GetMsg("Raid: Found", player), raidCount => found));
foreach(var sb in sbs) {
SendReply(player, sb.ToString());
}
return;
}
}
SendReply(player, "No raids found");
}
public string ToHex(Color c)
{
return string.Format("#{0:X2}{1:X2}{2:X2}", ToByte(c.r), ToByte(c.g), ToByte(c.b));
}
private byte ToByte(float f)
{
f = Mathf.Clamp01(f);
return (byte)(f * 255);
}
bool SendRaid(BasePlayer player, Raid raid, StringBuilder sb, int found = 0) {
var uiduration = detectionDuration * 60;
var admin = false;
if(player.net.connection.authLevel > 0)
admin = true;
var randomColor = colors[UnityEngine.Random.Range(0, colors.Length - 1)];
var validAttack = false;
foreach (AttackVector attack in raid.attackVector)
{
if(!admin && attack.victim != player.userID)
continue;
validAttack = true;
var weapName = string.Empty;
var def = ItemManager.FindItemDefinition(attack.weapon);
if(def is ItemDefinition)
weapName = def.displayName.english;
player.SendConsoleCommand("ddraw.arrow", uiduration, randomColor, attack.start, attack.end, 0.2);
if(!string.IsNullOrEmpty(weapName))
player.SendConsoleCommand("ddraw.text", uiduration, GetWeaponColor(attack.weapon), attack.start, weapName);
}
foreach(AttackVector kill in raid.killMap) {
if(!admin && kill.victim != player.userID)
continue;
validAttack = true;
player.SendConsoleCommand("ddraw.arrow", uiduration, randomColor, kill.start, kill.end, 0.2);
player.SendConsoleCommand("ddraw.sphere", uiduration, Color.red, kill.end, 0.5f);
var victimPlayer = covalence.Players.FindPlayerById(kill.victim.ToString());
var initiatorPlayer = covalence.Players.FindPlayerById(kill.initiator.ToString());
if(victimPlayer is IPlayer) {
player.SendConsoleCommand("ddraw.text", uiduration, Color.red, kill.end + Vector3.up, victimPlayer.Name);
}
if(initiatorPlayer is IPlayer) {
player.SendConsoleCommand("ddraw.text", uiduration, Color.red, kill.start + Vector3.up, initiatorPlayer.Name);
}
}
if(validAttack) {
player.SendConsoleCommand("ddraw.arrow", uiduration, Color.green, raid.firstDamage + new Vector3(0,5,0), raid.firstDamage, 0.2);
if(raid.lastDamage != Vector3.zero) {
player.SendConsoleCommand("ddraw.arrow", uiduration, Color.red, raid.lastDamage + new Vector3(0,5,0), raid.lastDamage, 0.2);
}
sb.Append(Format("<color={color}><size=17>#{count}</size></color> + ", count => (found+1), color => ToHex(randomColor)));
var start = (raid.Start != null) ? raid.Start.ToString() : "N/A";
sb.AppendLine(Format(GetMsg("Raid: Started"), date => start));
if(raid.Completed) {
TimeSpan ts = raid.End - raid.Start;
var end = (raid.End != null) ? raid.End.ToString() : "N/A";
sb.Append(" |- ").AppendLine(Format(GetMsg("Raid: Duration"), duration => Math.Round(ts.TotalMinutes,2)));
sb.Append(" |- ").AppendLine(Format(GetMsg("Raid: Ended"), date => end));
}
var initiator = covalence.Players.FindPlayerById(raid.initiator.ToString());
if(initiator != null) {
sb.Append(" |- ").AppendLine(Format(GetMsg("Raid: Initiator"), initiatorName => initiator.Name, initiatorID => initiator.Id));
}
if(raid.blockOwners.Count > 0) {
var victimList = string.Join(", ", raid.blockOwners.Select(x => covalence.Players.FindPlayerById(x.ToString()).Name).ToArray());
sb.Append(" |- ").AppendLine(Format(GetMsg("Raid: PlayerList"), listName => "Property Of", list => victimList));
}
if(raid.participants.Count > 0) {
var participantList = string.Join(", ", raid.blockOwners.Where(x => !raid.blockOwners.Contains(x)).Select(x => covalence.Players.FindPlayerById(x.ToString()).Name).ToArray());
if(!string.IsNullOrEmpty(participantList.Trim())) {
sb.Append(" |- ").AppendLine(Format(GetMsg("Raid: PlayerList"), listName => "Perpetrators", list => participantList));
}
}
if(raid.weapons.Count > 0)
sb.Append(" |- ").AppendLine(GetWeaponList(raid));
if(raid.blocksDestroyed.Count > 0 || raid.entitiesDestroyed.Count > 0)
sb.Append(" |- ").AppendLine(GetDestroyedList(raid));
return true;
}
return false;
}
void RegisterKill(BasePlayer player, BasePlayer attacker)
{
var behavior = player.GetComponent<RaidBehavior>();
if (behavior != null && behavior.raid != null)
{
var activeItem = attacker.GetActiveItem();
var itemid = 0;
if(activeItem != null)
itemid = activeItem.info.itemid;
behavior.raid.Kill(new AttackVector(attacker.transform.position, player.transform.position, itemid, player.userID, attacker.userID));
GameObject.Destroy(behavior);
}
}
void StructureAttack(BaseEntity targetEntity, BaseEntity sourceEntity,int weapon, bool destroy = false)
{
BasePlayer source;
if (sourceEntity.ToPlayer() is BasePlayer)
source = sourceEntity.ToPlayer();
else
{
var ownerID = (sourceEntity.OwnerID == 0) ? sourceEntity.OwnerID.ToString() : string.Empty;
if (!string.IsNullOrEmpty(ownerID))
source = BasePlayer.Find(ownerID);
else
return;
}
if (source == null)
return;
var targetID = targetEntity.OwnerID.IsSteamId() ? targetEntity.OwnerID.ToString() : string.Empty;
if (!string.IsNullOrEmpty(targetID) && targetID != source.UserIDString)
{
var targetIDUint = Convert.ToUInt64(targetID);
var target = covalence.Players.FindPlayerById(targetID);
Raid raid;
var raidFound = TryGetRaid(source, targetIDUint, targetEntity.transform.position, out raid);
raid.lastWeapon = weapon;
if (raid.blockOwners.Count == 0)
raid.victim = targetIDUint;
if (!raid.blockOwners.Contains(targetIDUint))
raid.blockOwners.Add(targetIDUint);
if(destroy) {
if(targetEntity is BuildingBlock) {
var grade = ((BuildingBlock)targetEntity).grade;
if(raid.blocksDestroyed.ContainsKey(grade))
raid.blocksDestroyed[grade]++;
else
raid.blocksDestroyed.Add(grade, 1);
} else if(targetEntity is BaseCombatEntity) {
var name = targetEntity.ShortPrefabName;
if(raid.entitiesDestroyed.ContainsKey(name))
raid.entitiesDestroyed[name]++;
else
raid.entitiesDestroyed.Add(name, 1);
}
} else {
if (raid.weapons.ContainsKey(weapon))
raid.weapons[weapon]++;
else
raid.weapons.Add(weapon, 1);
}
if(raid.lastAttackVector != null)
raid.lastAttackVector.victim = targetIDUint;
raid.lastDamage = targetEntity.transform.position;
if (!raidFound && announceRaidStart)
{
AnnounceRaidMsg(raid, AnnouncementType.Start);
if(announceToSlack)
AnnounceRaidMsg(raid, AnnouncementType.Slack_Start);
if(announceToDiscord)
AnnounceRaidMsg(raid, AnnouncementType.Discord_Start);
}
}
}
Raid FindRaid(Vector3 position, out List<BasePlayer> nearbyTargets) {
Raid existingRaid = null;
nearbyTargets = GetNearbyPlayers(position);
if (existingRaid == null && nearbyTargets.Count > 0)
{
foreach (var nearbyTarget in nearbyTargets)
{
var behavior = nearbyTarget.GetComponent<RaidBehavior>();
if (behavior != null && behavior.raid != null && existingRaid != behavior.raid && !behavior.raid.Completed)
{
existingRaid = behavior.raid;
break;
}
}
}
return existingRaid;
}
List<BasePlayer> GetNearbyPlayers(Vector3 position) {
var nearbyTargets = Pool.GetList<BasePlayer>();
Vis.Entities<BasePlayer>(position, raidDistance, nearbyTargets, blockLayer);
nearbyTargets = Sort(position, nearbyTargets);
return nearbyTargets;
}
bool TryGetRaid(BasePlayer source, ulong victim, Vector3 position, out Raid raid)
{
Raid existingRaid = null;
List<BasePlayer> nearbyTargets = null;
var sourceBehavior = source.GetComponent<RaidBehavior>();
if (sourceBehavior != null && sourceBehavior.raid != null && !sourceBehavior.raid.Completed)
existingRaid = sourceBehavior.raid;
else
existingRaid = FindRaid(position, out nearbyTargets);
bool found = true;
if (existingRaid == null || (existingRaid != null && existingRaid.Completed)) {
found = false;
var newRaid = StartRaid(source, victim, position);
existingRaid = newRaid;
if(nearbyTargets == null)
nearbyTargets = GetNearbyPlayers(position);
foreach (var nearbyTarget in nearbyTargets) {
var behavior = nearbyTarget.GetComponent<RaidBehavior>();
if (behavior == null || (behavior != null && behavior.raid == null)) {
existingRaid.Participate(nearbyTarget);
}
}
} else if (sourceBehavior == null || (sourceBehavior != null && sourceBehavior.raid == null))
existingRaid.Participate(source);
if(nearbyTargets != null)
Pool.FreeList<BasePlayer>(ref nearbyTargets);
RefreshRaid(existingRaid);
raid = existingRaid;
return found;
}
public Raid StartRaid(BasePlayer source, ulong victim, Vector3 position)
{
var raid = new Raid(this, source.userID, victim, position);
RefreshRaid(raid);
raid.Participate(source);
raid.OnStarted();
return raid;
}
private string GetAnnouncementMsg(AnnouncementType type) {
var msgName = string.Empty;
switch(type) {
case AnnouncementType.Start:
msgName = "Announce: Start";
break;
case AnnouncementType.End:
msgName = "Announce: End";
break;
case AnnouncementType.Slack_Start:
msgName = "Announce: Slack Start";
break;
case AnnouncementType.Slack_End:
msgName = "Announce: Slack End";
break;
case AnnouncementType.Discord_Start:
msgName = "Announce: Discord Start";
break;
case AnnouncementType.Discord_End:
msgName = "Announce: Discord End";
break;
}
if(!string.IsNullOrEmpty(msgName))
return GetMsg(msgName);
return msgName;
}
public void CheckRaid(Raid raid)
{
var ts = DateTime.Now - raid.lastRefresh;
if (ts.TotalSeconds > raidDuration)
{
if(announceToLustyMap && lustyMapDuration > 0)
LustyMap?.Call("RemoveMarker", raid.start.ToString());
StopRaid(raid);
if (announceRaidEnd)
{
AnnounceRaidMsg(raid, AnnouncementType.End);
if(announceToSlack)
AnnounceRaidMsg(raid, AnnouncementType.Slack_End);
if(announceToDiscord)
AnnounceRaidMsg(raid, AnnouncementType.Discord_End);
}
}
}
void AnnounceRaidMsg(Raid raid, AnnouncementType type) {
if (announceDelay > 0)
timer.In(announceDelay, delegate()
{
AnnounceRaid(raid, type);
});
else
AnnounceRaid(raid, type);
}
public void RefreshRaid(Raid raid)
{
raid.lastRefresh = DateTime.Now;
Timer t;
if (timers.TryGetValue(raid, out t)) {
if(t.Destroyed) {
timers.Add(raid, t = timer.Repeat(raidDuration, 0, () => CheckRaid(raid)));
}
}
else {
timers.Add(raid, t = timer.Repeat(raidDuration, 0, () => CheckRaid(raid)));
// ... [Code preview truncated due to length. Full source in attached file] ...