C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Oxide.Game.Rust.Libraries;
using Oxide.Plugins.DuelistExtensionMethods;
using Rust;
using Rust.Workshop;
using UnityEngine;
using Random = UnityEngine.Random;
namespace Oxide.Plugins {
[Info("Duelist", "nivex", "1.3.5")]
[Description("1v1 and team deathmatch event.")]
public class Duelist : RustPlugin {
[PluginReference]
Plugin Kits, ZoneManager, Economics, ServerRewards, Clans, AimTrain, LustyMap;
private static Duelist Instance;
public enum Team {
Good = 0, Evil = 1, None = 2
}
private const string hewwPrefab = "assets/prefabs/building/wall.external.high.wood/wall.external.high.wood.prefab";
private const string heswPrefab = "assets/prefabs/building/wall.external.high.stone/wall.external.high.stone.prefab";
private const bool debugMode = false;
private List<string> readyUiList = new List<string>();
private List<string> spectators = new List<string>();
private List<Rematch> rematches = new List<Rematch>();
private Dictionary<string, AttackerInfo> tdmAttackers = new Dictionary<string, AttackerInfo>();
private Dictionary<string, string> tdmKits = new Dictionary<string, string>();
private HashSet<GoodVersusEvilMatch> tdmMatches = new HashSet<GoodVersusEvilMatch>();
private List<DuelingZone> duelingZones = new List<DuelingZone>();
// where all the fun is at
private StoredData duelsData = new StoredData();
private Dictionary<string, string> dataDuelists = new Dictionary<string, string>();
// active duelers
private Dictionary<string, long> dataImmunity = new Dictionary<string, long>();
// players immune to damage
private Dictionary<string, Vector3> dataImmunitySpawns = new Dictionary<string, Vector3>();
// players spawn points
private int blockedMask = LayerMask.GetMask("Player (Server)", "Prevent Building", "Construction", "Deployed", "Trigger");
// layers we won't be setting a zone within 50 meters of
private int constructionMask = LayerMask.GetMask("Construction", "Deployed");
private bool matchUpdateRequired;
private int groundMask = LayerMask.GetMask("Terrain", "World", "Default");
// used to find dueling zone/set custom zone and create spawn points
private int wallMask = LayerMask.GetMask("Terrain", "World", "Default", "Construction", "Deployed");
private int waterMask = LayerMask.GetMask("Water");
// used to count water colliders when finding a random dueling zone on the map
private int worldMask = LayerMask.GetMask("World");
private Timer announceTimer;
private SortedDictionary<string, string> boneTags = new SortedDictionary<string, string> {
["r_"]
= "Right ", ["l_"] = "Left ", [".prefab"] = string.Empty, ["1"] = string.Empty, ["2"] = string.Empty, ["3"] = string.Empty, ["4"] = string.Empty, ["END"] = string.Empty, ["_"] = " ", ["."] = " "
}
;
private Dictionary<string, string> announcements = new Dictionary<string, string>();
// users id and announcement
private Dictionary<string, long> dataDeath = new Dictionary<string, long>();
// users id and timestamp of when they're to be executed
private Dictionary<string, string> dataRequests = new Dictionary<string, string>();
// users requesting a duel and to whom
private Dictionary<string, bool> deployables = new Dictionary<string, bool>();
private Dictionary<ulong, List<BaseEntity>> duelEntities = new Dictionary<ulong, List<BaseEntity>>();
private DynamicConfigFile duelsFile;
private Timer eventTimer;
// timer to check for immunity and auto death time of duelers
private Timer matchTimer;
// timer to check for updates to the match ui
private SpawnFilter filter = new SpawnFilter();
// RandomDropPosition()
private Dictionary<Vector3, float> managedZones = new Dictionary<Vector3, float>();
// blocked zones from zonemanager plugin
private List<Vector3> monuments = new List<Vector3>();
// positions of monuments on the server
private Dictionary<string, string> prefabs = new Dictionary<string, string>();
private bool resetDuelists;
// if wipe is detected then assign awards and wipe VictoriesSeed / LossesSeed
private Dictionary<string, List<ulong>> skinsCache = new Dictionary<string, List<ulong>>();
// used to randomize custom kit skins which skin id values are 0
private Dictionary<string, string> tdmRequests = new Dictionary<string, string>();
// users requesting a deathmatch and to whom
private Dictionary<string, List<ulong>> workshopskinsCache = new Dictionary<string, List<ulong>>();
private List<string> dcsBlock = new List<string>();
// users blocked from 1v1 for 60 seconds after suiciding or disconnecting
private Dictionary<string, string> playerZones = new Dictionary<string, string>();
// id, set zone name
public class StoredData {
public List<string> Allowed = new List<string>();
// list of users that allow duel requests
public Dictionary<string, List<string>> AutoGeneratedSpawns = new Dictionary<string, List<string>>();
public Dictionary<string, string> Bans = new Dictionary<string, string>();
// users banned from dueling
public Dictionary<string, BetInfo> Bets = new Dictionary<string, BetInfo>();
// active bets users have placed
public Dictionary<string, List<string>> BlockedUsers = new Dictionary<string, List<string>>();
// users and the list of players they blocked from requesting duels with
public List<string> Chat = new List<string>();
// user ids of those who opted out of seeing duel death messages
public List<string> ChatEx = new List<string>();
// user ids of those who opted to see duel death messages when the config blocks them for all players
public Dictionary<string, List<BetInfo>> ClaimBets = new Dictionary<string, List<BetInfo>>();
// active bets users need to claim after winning a bet
public Dictionary<string, string> CustomKits = new Dictionary<string, string>();
// userid and custom kit
public bool DuelsEnabled;
// enable/disable dueling for all players (not admins)
public Dictionary<string, string> Homes = new Dictionary<string, string>();
// user id and location of where they teleported from
public Dictionary<string, string> Kits = new Dictionary<string, string>();
// userid and kit.give kit when they wake up inside of the dueling zone
public Dictionary<string, int> Losses = new Dictionary<string, int>();
// user id / losses for lifetime
public Dictionary<string, int> LossesSeed = new Dictionary<string, int>();
// user id / losses for seed
public SortedDictionary<long, string> Queued = new SortedDictionary<long, string>();
// queued duelers sorted by timestamp and user id.first come first serve
public List<string> Restricted = new List<string>();
// list of users blocked from requesting a duel for 60 seconds
public List<string> Spawns = new List<string>();
// custom spawn points
public List<string> AutoReady = new List<string>();
public Dictionary<string, int> MatchVictories = new Dictionary<string, int>();
// player name & total wins
public Dictionary<string, int> MatchVictoriesSeed = new Dictionary<string, int>();
// player name & wins for current seed
public Dictionary<string, int> MatchLosses = new Dictionary<string, int>();
// player name & total losses
public Dictionary<string, int> MatchLossesSeed = new Dictionary<string, int>();
// player name & losses for current seed
public Dictionary<string, int> MatchDeaths = new Dictionary<string, int>();
// player name & total deaths
public Dictionary<string, int> MatchDeathsSeed = new Dictionary<string, int>();
// player name & deaths for the seed
public Dictionary<string, int> MatchKills = new Dictionary<string, int>();
// player name & total kills
public Dictionary<string, int> MatchKillsSeed = new Dictionary<string, int>();
// player name & kills for current seed
public Dictionary<string, Dictionary<string, int>> MatchSizesVictories = new Dictionary<string, Dictionary<string, int>>();
// size, id, wins
public Dictionary<string, Dictionary<string, int>> MatchSizesVictoriesSeed = new Dictionary<string, Dictionary<string, int>>();
// size, id, wins seed
public Dictionary<string, Dictionary<string, int>> MatchSizesLosses = new Dictionary<string, Dictionary<string, int>>();
// size, id, losses
public Dictionary<string, Dictionary<string, int>> MatchSizesLossesSeed = new Dictionary<string, Dictionary<string, int>>();
// size, id, losses seed
public int TotalDuels;
// the total amount of duels ever played on the server
public Dictionary<string, int> Victories = new Dictionary<string, int>();
// user id / wins for lifetime
public Dictionary<string, int> VictoriesSeed = new Dictionary<string, int>();
// user id / wins for seed
public List<string> ZoneIds = new List<string>();
// the locations of each dueling zone
public Dictionary<string, string> DuelZones = new Dictionary<string, string>();
// location, name
}
private class Tracker : FacepunchBehaviour {
public BasePlayer player;
private Duelist _;
private void Awake() {
player = GetComponent<BasePlayer>();
}
public void Init(Duelist _) {
this._ = _;
InvokeRepeating(Track, 0f, 0.5f);
}
private void Track() {
if (player == null || player.transform == null) {
Destroy(this);
return;
}
if (_.dataDuelists.ContainsKey(player.UserIDString) || _.tdmMatches.Exists(team => team.GetTeam(player) != Team.None)) {
if (!_.DuelTerritory(player.transform.position)) {
player.inventory.Strip();
Destroy(this);
}
}
}
private void OnDestroy() {
try {
CancelInvoke();
}
catch {
}
Destroy(this);
}
}
public class Rematch {
public Rematch(Duelist _) {
this._ = _;
}
private Duelist _;
public List<BasePlayer> Duelists = new List<BasePlayer>();
public List<BasePlayer> Ready = new List<BasePlayer>();
private List<BasePlayer> Evil = new List<BasePlayer>();
private List<BasePlayer> Good = new List<BasePlayer>();
public GoodVersusEvilMatch match;
private Timer _notify;
public List<BasePlayer> Players {
get {
Duelists.RemoveAll(IsNotConnected);
Good.RemoveAll(IsNotConnected);
Evil.RemoveAll(IsNotConnected);
Ready.RemoveAll(IsNotConnected);
if (match == null) return Duelists;
var _list = new List<BasePlayer>();
_list.AddRange(Good);
_list.AddRange(Evil);
return _list;
}
}
public bool HasPlayer(BasePlayer player) {
return Players.Contains(player);
}
public bool AddRange(List<BasePlayer> players, Team team) {
foreach (var player in players) {
if (IsNotConnected(player) || _.InEvent(player) || Good.Contains(player) || Evil.Contains(player)) break;
if (team == Team.Evil) Evil.Add(player);
else Good.Add(player);
}
return (team == Team.Evil ? Evil.Count : Good.Count) == players.Count;
}
public bool IsReady(BasePlayer player) {
if (_.InEvent(player) || !_.IsNewman(player) || _.duelsData.Bans.ContainsKey(player.UserIDString)) return false;
return true;
}
public bool IsReady() {
if (Players.Exists(player => !IsReady(player))) {
Reset("RematchFailed2");
return false;
}
return Ready.Count == (match == null ? 2 : match.TeamSize * 2);
}
private void Reset(string key) {
_.tdmMatches.Remove(match);
MessageAll(key);
Duelists.Clear();
Good.Clear();
Evil.Clear();
Ready.Clear();
_notify?.Destroy();
_.rematches.Remove(this);
_.matchUpdateRequired = true;
}
public void MessageAll(string key, params object[] args) {
foreach (var player in Players) {
Message(player, _.msg(key, player.UserIDString, args));
}
}
public void Notify() {
MessageAll("RematchNotify", 60f, match == null ? _.szDuelChatCommand : _.szMatchChatCommand);
foreach (var player in Players) if (_.duelsData.AutoReady.Contains(player.UserIDString)) Ready.Add(player);
if (IsReady()) {
Start();
_.rematches.Remove(this);
}
else if (match == null || !match.IsPublic) _notify = _.timer.Once(60f, Cancel);
}
private void Cancel() {
if (match != null && match.IsPublic) return;
if (_.rematches.Contains(this)) {
if (_.sendHomeSpectatorWhenRematchTimesOut) {
foreach (var player in Players) {
if (_.IsSpectator(player)) {
_.EndSpectate(player);
_.SendHome(player);
}
}
}
if (match != null) {
match.Reuse();
}
_.tdmMatches.Remove(match);
Reset("RematchTimedOut");
}
}
public void Start() {
if (match == null) {
var player = Ready[0];
var target = Ready[1];
if (!_.SelectZone(player, target)) {
Message(player, _.msg("AllZonesFull", player.UserIDString, _.duelingZones.Count, _.playersPerZone));
Message(target, _.msg("AllZonesFull", target.UserIDString, _.duelingZones.Count, _.playersPerZone));
}
}
else {
match.Reuse();
_.tdmMatches.Add(match);
if (!AddMatchPlayers(Good, Team.Good) || !AddMatchPlayers(Evil, Team.Evil)) {
Reset("RematchFailed");
match.Reuse();
}
}
_notify?.Destroy();
_.rematches.Remove(this);
}
private bool AddMatchPlayers(List<BasePlayer> players, Team team) {
foreach (var player in players) if (!match.AddMatchPlayer(player, team)) return false;
return true;
}
}
public class AttackerInfo {
public string AttackerName = "";
public string AttackerId = "";
public string BoneName = "";
public string Distance = "";
public string Weapon = "";
}
public class GoodVersusEvilMatch {
public GoodVersusEvilMatch(Duelist _) {
this._ = _;
}
private Duelist _;
private HashSet<ulong> _banned = new HashSet<ulong>();
private HashSet<BasePlayer> _evil = new HashSet<BasePlayer>();
private HashSet<ulong> _evilKIA = new HashSet<ulong>();
private List<BasePlayer> _evilRematch = new List<BasePlayer>();
private HashSet<BasePlayer> _good = new HashSet<BasePlayer>();
private HashSet<ulong> _goodKIA = new HashSet<ulong>();
private List<BasePlayer> _goodRematch = new List<BasePlayer>();
private string _goodHostName = "";
private string _evilHostName = "";
private string _goodHostId = "";
private string _evilHostId = "";
private string _goodCode = "";
private string _evilCode = "";
private int _teamSize = 2;
private bool _started;
private bool _ended;
private string _kit = "";
private DuelingZone _zone;
private Timer _queueTimer;
private bool _enteredQueue;
private bool _public;
public bool CanRematch = true;
public string Id {
get {
return _goodHostId + _evilHostId;
}
}
public string Versus {
get {
return string.Format("{0} / {1} {2}v{2}", _goodHostName, _evilHostName, _teamSize);
}
}
public bool IsPublic {
get {
return _public;
}
set {
_public = value;
_.matchUpdateRequired = true;
MessageAll(_public ? "MatchPublic" : "MatchPrivate");
}
}
public int TeamSize {
get {
return _teamSize;
}
set {
if (IsStarted) return;
_teamSize = value;
_.matchUpdateRequired = true;
MessageAll("MatchSizeChanged", _teamSize);
}
}
public DuelingZone Zone {
get {
return _zone;
}
}
public bool EitherEmpty {
get {
return _good.Count == 0 || _evil.Count == 0;
}
}
public bool IsStarted {
get {
return _started;
}
set {
_started = value;
_.matchUpdateRequired = true;
}
}
public bool IsOver {
get {
return _ended;
}
set {
_ended = value;
_.matchUpdateRequired = true;
}
}
public string Kit {
get {
return _kit;
}
set {
_kit = value;
if (!EitherEmpty) {
_good.RemoveWhere(IsNotConnected);
_evil.RemoveWhere(IsNotConnected);
_good.ToList().ForEach(player => _.duelsData.Kits[player.UserIDString] = _kit);
_evil.ToList().ForEach(player => _.duelsData.Kits[player.UserIDString] = _kit);
MessageAll("MatchKitSet", _kit);
}
}
}
public void Reuse() {
if (_zone != null) _zone.IsLocked = false;
_evilRematch.Clear();
_goodRematch.Clear();
_good.Clear();
_evil.Clear();
_goodKIA.Clear();
_evilKIA.Clear();
_started = false;
_ended = false;
_enteredQueue = false;
_kit = _.GetRandomKit();
_goodHostId = BasePlayer.activePlayerList.FirstOrDefault(x => x.displayName == _goodHostName)?.UserIDString ?? _goodHostId;
_evilHostId = BasePlayer.activePlayerList.FirstOrDefault(x => x.displayName == _evilHostName)?.UserIDString ?? _evilHostId;
_.matchUpdateRequired = true;
}
public void Setup(BasePlayer player, BasePlayer target) {
_.tdmMatches.Add(this);
_goodHostName = player.displayName;
_goodHostId = player.UserIDString;
_evilHostName = target.displayName;
_evilHostId = target.UserIDString;
_goodCode = Random.Range(10000, 99999).ToString();
_evilCode = Random.Range(10000, 99999).ToString();
if (_teamSize < _.minDeathmatchSize) _teamSize = _.minDeathmatchSize;
AddMatchPlayer(player, Team.Good);
AddMatchPlayer(target, Team.Evil);
if (_.tdmKits.ContainsKey(player.UserIDString)) {
Kit = _.tdmKits[player.UserIDString];
_.tdmKits.Remove(player.UserIDString);
}
else if (_.tdmKits.ContainsKey(target.UserIDString)) {
Kit = _.tdmKits[target.UserIDString];
_.tdmKits.Remove(target.UserIDString);
}
else Kit = _.GetRandomKit();
if (TeamSize > 1) {
Message(player, _.msg("MatchOpened", player.UserIDString, _.szMatchChatCommand, _goodCode));
Message(target, _.msg("MatchOpened", target.UserIDString, _.szMatchChatCommand, _evilCode));
}
_.matchUpdateRequired = true;
}
public bool IsFull() {
return _good.Count == _teamSize && _evil.Count == _teamSize;
}
public bool IsFull(Team team) {
return team == Team.Good ? _good.Count == _teamSize : _evil.Count == _teamSize;
}
public void MessageAll(string key, params object[] args) {
_.Message(_good, key, args);
_.Message(_evil, key, args);
}
public Team GetTeam(BasePlayer player) {
return _good.Contains(player) ? Team.Good : _evil.Contains(player) ? Team.Evil : Team.None;
}
public bool IsHost(BasePlayer player) {
return player.UserIDString == _goodHostId || player.UserIDString == _evilHostId;
}
public void SetCode(BasePlayer player, string code) {
if (GetTeam(player) == Team.Evil) _evilCode = code;
else if (GetTeam(player) == Team.Good) _goodCode = code;
}
public string Code(Team team) {
return team == Team.Good ? _goodCode : _evilCode;
}
public bool AlliedTo(BasePlayer player, Team team) {
return _.IsAllied(player.UserIDString, team == Team.Good ? _goodHostId : _evilHostId);
}
public bool IsBanned(ulong targetId) {
return _banned.Contains(targetId);
}
public bool Ban(BasePlayer target) {
if (target.UserIDString == _goodHostId || target.UserIDString == _evilHostId || IsBanned(target.userID)) return false;
_banned.Add(target.userID);
RemoveMatchPlayer(target);
return true;
}
public bool Equals(GoodVersusEvilMatch match) {
return match._good.Equals(_good) && match._evil.Equals(_evil);
}
public string GetNames(Team team) {
return string.Join(", ", team == Team.Good ? _good.ToList().Select(player => player.displayName) : _evil.ToList().Select(player => player.displayName));
}
public void GiveShirt(BasePlayer player) {
Item item = ItemManager.CreateByName(_.teamShirt, 1, GetTeam(player) == Team.Evil ? _.teamEvilShirt : _.teamGoodShirt);
if (item == null) return;
if (item.info.category != ItemCategory.Attire) {
item.Remove(0.01f);
return;
}
foreach (Item wear in player.inventory.containerWear.itemList) {
if (wear.info.shortname.Contains("shirt")) {
wear.RemoveFromContainer();
wear.Remove(0.01f);
break;
}
}
item.MoveToContainer(player.inventory.containerWear, -1, false);
if (!player.inventory.containerWear.HasFlag(ItemContainer.Flag.IsLocked)) player.inventory.containerWear.SetFlag(ItemContainer.Flag.IsLocked, true);
}
public bool AddMatchPlayer(BasePlayer player, Team team) {
if (_started) {
Message(player, _.msg("MatchStartedAlready", player.UserIDString));
return false;
}
_good.RemoveWhere(IsNotConnected);
_evil.RemoveWhere(IsNotConnected);
if (_banned.Contains(player.userID)) return false;
if (!_.IsNewman(player)) {
Message(player, _.msg("MustBeNaked", player.UserIDString));
return false;
}
switch (team) {
case Team.Good : if (_good.Count == _teamSize) {
Message(player, _.msg("MatchTeamFull", player.UserIDString, _teamSize));
return false;
}
_good.Add(player);
MessageAll("MatchJoinedTeam", player.displayName, _goodHostName, _good.Count, _teamSize, _evilHostName, _evil.Count);
break;
case Team.Evil : if (_evil.Count == _teamSize) {
Message(player, _.msg("MatchTeamFull", player.UserIDString, _teamSize));
return false;
}
_evil.Add(player);
MessageAll("MatchJoinedTeam", player.displayName, _evilHostName, _evil.Count, _teamSize, _goodHostName, _good.Count);
break;
}
if (_good.Count == _teamSize && _evil.Count == _teamSize) Queue();
return true;
}
public bool RemoveMatchPlayer(BasePlayer player) {
if (player == null) return false;
if (player.inventory.containerWear.HasFlag(ItemContainer.Flag.IsLocked)) player.inventory.containerWear.SetFlag(ItemContainer.Flag.IsLocked, false);
_.Metabolize(player, false);
_.Track(player, false);
_.RemoveEntities(player.userID);
Interface.Oxide.CallHook("DisableBypass", player.userID);
if (_.DuelTerritory(player.transform.position)) {
if (_.sendDefeatedHome) _.SendHome(player);
else _.StartSpectate(player);
}
if (IsOver) {
_good.Remove(player);
_evil.Remove(player);
return true;
}
if (_good.Remove(player)) {
if (_good.Count == 0) {
if (_started) {
_goodKIA.Add(player.userID);
_goodRematch.Add(player);
}
else MessageAll("MatchNoPlayersLeft");
EndMatch(Team.Evil);
return true;
}
if (_started) {
_goodKIA.Add(player.userID);
_goodRematch.Add(player);
}
if (player.UserIDString == _goodHostId) AssignGoodHostId();
return true;
}
if (_evil.Remove(player)) {
if (_evil.Count == 0) {
if (_started) {
_evilKIA.Add(player.userID);
_evilRematch.Add(player);
}
else MessageAll("MatchNoPlayersLeft");
EndMatch(Team.Good);
return true;
}
if (_started) {
_evilKIA.Add(player.userID);
_evilRematch.Add(player);
}
if (player.UserIDString == _evilHostId) AssignEvilHostId();
return true;
}
return false;
}
private void AssignGoodHostId() {
_good.RemoveWhere(IsNotConnected);
if (_good.Count > 0) {
_goodHostId = _good.FirstOrDefault().UserIDString;
_.matchUpdateRequired = true;
}
else EndMatch(Team.Evil);
}
private void AssignEvilHostId() {
_evil.RemoveWhere(IsNotConnected);
if (_evil.Count > 0) {
_evilHostId = _evil.FirstOrDefault().UserIDString;
_.matchUpdateRequired = true;
}
else EndMatch(Team.Good);
}
private void Finalize(Team team) {
Interface.CallHook("OnDuelistFinalized", team == Team.Good ? _goodKIA : _evilKIA);
switch (team) {
case Team.Evil : {
foreach (ulong playerId in _goodKIA) {
_.UpdateMatchStats(playerId.ToString(), false, true, false, false);
_.UpdateMatchSizeStats(playerId.ToString(), true, false, _teamSize);
}
foreach (ulong playerId in _evilKIA) {
_.AwardPlayer(playerId, _.teamEconomicsMoney, _.teamServerRewardsPoints);
_.UpdateMatchStats(playerId.ToString(), true, false, false, false);
_.UpdateMatchSizeStats(playerId.ToString(), false, true, _teamSize);
}
break;
}
case Team.Good : {
foreach (ulong playerId in _evilKIA) {
_.UpdateMatchStats(playerId.ToString(), false, true, false, false);
_.UpdateMatchSizeStats(playerId.ToString(), true, false, _teamSize);
}
foreach (ulong playerId in _goodKIA) {
_.AwardPlayer(playerId, _.teamEconomicsMoney, _.teamServerRewardsPoints);
_.UpdateMatchStats(playerId.ToString(), true, false, false, false);
_.UpdateMatchSizeStats(playerId.ToString(), false, true, _teamSize);
}
break;
}
}
_goodKIA.Clear();
_evilKIA.Clear();
}
private bool SetupRematch() {
if (!CanRematch || _goodRematch.Exists(IsNotConnected) || _evilRematch.Exists(IsNotConnected)) return false;
var rematch = new Rematch(_);
if (rematch.AddRange(_evilRematch, Team.Evil) && rematch.AddRange(_goodRematch, Team.Good)) {
rematch.match = this;
rematch.Notify();
_.rematches.Add(rematch);
return true;
}
return false;
}
private void EndMatch(Team team) {
if (!_ended && _started) {
Finalize(team);
_.Puts(_.msg("MatchDefeat", null, team == Team.Evil ? _evilHostName : _goodHostName, team == Team.Evil ? _goodHostName : _evilHostName, _teamSize));
IsOver = true;
IsStarted = false;
foreach (var player in _evil.ToList()) {
RemoveMatchPlayer(player);
if (player != null && player.IsConnected && !_evilRematch.Contains(player)) _evilRematch.Add(player);
}
foreach (var player in _good.ToList()) {
RemoveMatchPlayer(player);
if (player != null && player.IsConnected && !_goodRematch.Contains(player)) _goodRematch.Add(player);
}
foreach (var target in BasePlayer.activePlayerList.Where(p => p?.displayName != null)) {
if (_.guiAnnounceUITime > 0f && (_goodKIA.Contains(target.userID) || _evilKIA.Contains(target.userID))) _.CreateAnnouncementUI(target, _.msg("MatchDefeat", target.UserIDString, team == Team.Evil ? _evilHostName : _goodHostName, team == Team.Evil ? _goodHostName : _evilHostName, _teamSize));
if (_.duelsData.Chat.Contains(target.UserIDString) && !_goodKIA.Contains(target.userID) && !_evilKIA.Contains(target.userID)) continue;
Message(target, _.msg("MatchDefeat", target.UserIDString, team == Team.Evil ? _evilHostName : _goodHostName, team == Team.Evil ? _goodHostName : _evilHostName, _teamSize));
}
if (!SetupRematch()) {
var _list = new List<BasePlayer>();
_list.AddRange(_evilRematch);
_list.AddRange(_goodRematch);
foreach (var player in _list) {
if (IsNotConnected(player)) continue;
Message(player, _.msg("RematchFailed2", player.UserIDString));
}
Reuse();
}
}
End();
}
public void End(bool forced = false) {
if (_zone != null) _zone.IsLocked = false;
_queueTimer?.Destroy();
_good.RemoveWhere(IsNull);
_evil.RemoveWhere(IsNull);
var _list = new List<BasePlayer>();
_list.AddRange(_good);
_list.AddRange(_evil);
foreach (var player in _list) {
if (player.inventory.containerWear.HasFlag(ItemContainer.Flag.IsLocked)) player.inventory.containerWear.SetFlag(ItemContainer.Flag.IsLocked, false);
if (IsStarted || IsOver) {
if (_.DuelTerritory(player.transform.position)) {
player.inventory.Strip();
_.SendHome(player);
}
_.Metabolize(player, false);
_.Track(player, false);
}
}
_good.Clear();
_evil.Clear();
_.tdmMatches.Remove(this);
_.matchUpdateRequired = true;
if (_.dataDuelists.Count == 0 && _.tdmMatches.Count == 0) _.Unsubscribe(nameof(OnPlayerHealthChange));
}
private void Queue() {
DuelingZone zone = null;
var _list = new List<BasePlayer>();
_list.AddRange(_good);
_list.AddRange(_evil);
foreach (var player in _list) {
if (player == null) continue;
if (!_.IsNewman(player)) {
Message(player, _.msg("MustBeNaked", player.UserIDString));
MessageAll("MatchIsNotNaked", player.displayName);
_queueTimer = _.timer.Once(30f, Queue);
return;
}
if (zone == null) zone = _.GetPlayerZone(player, TeamSize);
}
var zones = _.duelingZones.Where(x => x.TotalPlayers == 0 && !x.IsLocked && x.Spawns.Count >= (_.requireTeamSize ? TeamSize * 2 : 2)).ToList();
if (zones == null || zones.Count == 0) {
if (!_enteredQueue) {
MessageAll("MatchQueued");
_enteredQueue = true;
}
_queueTimer = _.timer.Once(2f, Queue);
return;
}
_zone = zone ?? LastZone ?? zones.GetRandom();
_queueTimer?.Destroy();
Start();
}
public DuelingZone LastZone {
get {
DuelingZone zone = null;
if (_good.Exists(player => _.DuelTerritory(player.transform.position))) zone = _.GetDuelZone(_good.FirstOrDefault(player => _.DuelTerritory(player.transform.position)).transform.position);
if (_evil.Exists(player => _.DuelTerritory(player.transform.position))) zone = _.GetDuelZone(_evil.FirstOrDefault(player => _.DuelTerritory(player.transform.position)).transform.position);
return zone == null || zone.TotalPlayers > 0 || zone.IsLocked ? null : zone;
}
}
private void Start() {
_.SubscribeHooks(true);
var goodSpawn = _zone.Spawns.GetRandom();
var evilSpawn = goodSpawn;
float dist = -100f;
foreach (var spawn in _zone.Spawns) // get the furthest spawn point away from the good team and assign it to the evil team
{
float distance = Vector3.Distance(spawn, goodSpawn);
if (distance > dist) {
dist = distance;
evilSpawn = spawn;
}
}
_.Message(_good, "MatchStarted", GetNames(Team.Evil));
_.Message(_evil, "MatchStarted", GetNames(Team.Good));
_zone.IsLocked = true;
IsStarted = true;
Spawn(_good, goodSpawn);
Spawn(_evil, evilSpawn);
}
private void Spawn(HashSet<BasePlayer> players, Vector3 spawn) {
foreach (var player in players) {
_.duelsData.Kits[player.UserIDString] = _kit;
if (!_.DuelTerritory(player.transform.position) || !_.duelsData.Homes.ContainsKey(player.UserIDString)) {
var ppos = player.transform.position;
if (_.IsOnConstruction(ppos)) ppos.y += 1;
// prevent player from becoming stuck or dying when teleported home
_.duelsData.Homes[player.UserIDString] = ppos.ToString();
}
_.RemoveFromQueue(player.UserIDString);
_.Teleport(player, spawn);
if (_.immunityTime >= 1) {
_.dataImmunity[player.UserIDString] = TimeStamp() + _.immunityTime;
_.dataImmunitySpawns[player.UserIDString] = spawn;
}
}
}
}
public class DuelKitItem {
public string ammo;
public int amount;
public string container;
public List<string> mods;
public string shortname;
public ulong skin;
public int slot;
}
public class BetInfo {
public string trigger;
// the trigger used to request this as a bet
public int amount;
// amount the player bet
public int itemid;
// the unique identifier of the item
public int max;
// the maximum amount allowed to bet on this item
public bool Equals(BetInfo bet) {
return bet.amount == amount && bet.itemid == itemid;
}
}
public class DuelingZone : FacepunchBehaviour // Thanks @Jake_Rich for helping me get this started!
{
private Duelist _ => Instance;
private HashSet<BasePlayer> _players = new HashSet<BasePlayer>();
private HashSet<BasePlayer> _waiting = new HashSet<BasePlayer>();
private Vector3 _zonePos;
private List<Vector3> _duelSpawns = new List<Vector3>();
// spawn points generated on the fly
private List<SphereEntity> spheres = new List<SphereEntity>();
public bool IsLocked;
public int Kills;
public int TotalPlayers {
get {
return _players.Count;
}
}
public List<BasePlayer> Players {
get {
return _players.ToList();
}
}
public List<Vector3> Spawns {
get {
var spawns = _.GetSpawnPoints(this);
// get custom spawn points if any exist
return spawns == null || spawns.Count < 2 ? _duelSpawns : spawns;
}
}
public bool IsFull {
get {
return TotalPlayers + _waiting.Count + 2 > _.playersPerZone || IsLocked;
}
}
public Vector3 Position {
get {
return _zonePos;
}
}
private void OnDestroy() {
Destroy(gameObject);
Destroy(this);
}
private void OnTriggerEnter(Collider col) {
if (col == null) return;
var entity = col.ToBaseEntity();
if (entity is BaseMountable) {
var m = entity as BaseMountable;
if (m.GetParentEntity() is TrainCar) {
return;
}
RemoveMountable(m, GetMountedPlayers(m));
}
else if (entity is BasePlayer) {
var p = entity as BasePlayer;
if (_.InEvent(p) || _.IsSpectator(p)) {
return;
}
RemovePlayer(p);
}
}
public Vector3 GetEjectLocation(Vector3 a, float distance) {
var position = ((a.XZ3D() - _zonePos.XZ3D()).normalized * (_.zoneRadius + distance)) + _zonePos;
// credits ZoneManager
float y = TerrainMeta.HighestPoint.y + 250f;
RaycastHit hit;
if (Physics.Raycast(position + new Vector3(0f, y, 0f), Vector3.down, out hit, Mathf.Infinity, targetLayer, QueryTriggerInteraction.Ignore)) {
position.y = hit.point.y + 0.75f;
}
else position.y = Mathf.Max(TerrainMeta.HeightMap.GetHeight(position), TerrainMeta.WaterMap.GetHeight(position)) + 0.75f;
return position;
}
public bool RemovePlayer(BasePlayer player) {
if (!player.IsHuman() || player.IsAdmin) {
return false;
}
var m = player.GetMounted();
if (m.IsValid()) {
var players = GetMountedPlayers(m);
players.RemoveAll(x => x == null || !x.IsHuman());
if (RemoveMountable(m, players)) {
return true;
}
}
var position = GetEjectLocation(player.transform.position, 10f);
if (player.IsFlying) {
position.y = player.transform.position.y;
}
player.Teleport(position);
player.SendNetworkUpdateImmediate();
return true;
}
public void DismountAllPlayers(BaseMountable m) {
foreach (var target in GetMountedPlayers(m)) {
if (target == null) continue;
m.DismountPlayer(target, false);
target.EnsureDismounted();
}
}
private List<BasePlayer> GetMountedPlayers(BaseMountable m) {
BaseVehicle vehicle = m.HasParent() ? m.VehicleParent() : m as BaseVehicle;
if (vehicle.IsValid()) {
return GetMountedPlayers(vehicle);
}
List<BasePlayer> players = new List<BasePlayer>();
var player = m.GetMounted();
if (player.IsValid() && player.IsHuman()) {
players.Add(player);
}
return players;
}
private List<BasePlayer> GetMountedPlayers(BaseVehicle vehicle) {
List<BasePlayer> players = new List<BasePlayer>();
if (!vehicle.HasMountPoints()) {
var player = vehicle.GetMounted();
if (player.IsValid() && player.IsHuman()) {
players.Add(player);
}
return players;
}
for (int i = 0; i < vehicle.mountPoints.Count; i++) {
var mountPoint = vehicle.mountPoints[i];
if (mountPoint.mountable == null) {
continue;
}
var player = mountPoint.mountable.GetMounted();
if (player.IsValid() && player.IsHuman()) {
players.Add(player);
}
}
return players;
}
private bool RemoveMountable(BaseMountable m, List<BasePlayer> players) {
if (players.Count == 0) {
return Ej
// ... [Code preview truncated due to length. Full source in attached file] ...