C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
//Requires: ArenaStatistics
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Globalization;
using System.Reflection;
using Oxide.Core;
using Oxide.Core.Configuration;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.AI;
namespace Oxide.Plugins {
[Info("Arena Events", "k1lly0u", "0.1.65")]
[Description("Logic controllers from Arena events")]
class ArenaEvents : RustPlugin {
#region Fields
StoredData storedData;
private DynamicConfigFile data;
public static Arena Arena;
public static ArenaStatistics Statistics;
public static ArenaEvents ins;
private static NavMeshHit navHit;
private static FieldInfo _brain = typeof(global : : HumanNPC).GetField("_brain", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
private static FieldInfo _memory = typeof(HumanNPC).GetField("myMemory", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
private static FrameBudgeter frameBudgeter;
private const string FLAG_PREFAB = "assets/prefabs/deployable/signs/sign.post.double.prefab";
private const string SCIENTIST_PREFAB = "assets/rust.ai/agents/npcplayer/humannpc/scientist/scientistnpc.prefab";
static bool isUnloading;
#endregion
#region Oxide Hooks
private void Loaded() {
data = Interface.Oxide.DataFileSystem.GetFile("Arena/event_specific_data");
ToggleHookSubscription(false);
isUnloading = false;
}
private void OnServerInitialized() {
ins = this;
LoadData();
frameBudgeter = new GameObject("ArenaEvents.NPCFrameBudgeter").AddComponent<FrameBudgeter>();
}
private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info) {
if (entity != null && info != null) {
CTF.CTFFlag ctfFlag = entity.GetComponent<CTF.CTFFlag>() ?? entity.GetComponentInParent<CTF.CTFFlag>();
if (ctfFlag != null) Arena.NullifyDamage(info);
}
}
private void OnEntityDeath(ScientistNPC entity, HitInfo info) {
if (entity == null) return;
NPCSurvival.NPCController npcController = entity.GetComponent<NPCSurvival.NPCController>();
if (npcController != null) npcController.eventGame.OnNpcDeath(entity, info);
}
private void OnEntitySpawned(BaseNetworkable entity) {
if (Arena == null || entity == null) return;
if (entity.GetComponent<LootableCorpse>()) {
foreach (KeyValuePair<string, Arena.EventManager> eventGame in Arena.events) {
if (eventGame.Value is NPCSurvival && eventGame.Value.IsEventNPC((entity as LootableCorpse).playerSteamID)) {
eventGame.Value.OnNpcCorpseSpawned(entity as LootableCorpse);
break;
}
}
}
}
private object OnPlayerDropActiveItem(BasePlayer player, Item item) {
NPCPlayer npcPlayer = player?.GetComponent<NPCPlayer>();
if (npcPlayer != null) {
foreach (KeyValuePair<string, Arena.EventManager> eventGame in Arena.events) {
if (eventGame.Value is NPCSurvival) {
if (eventGame.Value.IsEventNPC(player.userID)) return false;
}
}
}
return null;
}
private object OnNpcTarget(global : : HumanNPC npcPlayer, BaseEntity target) {
NPCSurvival.NPCController monitor = npcPlayer?.GetComponent<NPCSurvival.NPCController>();
if (monitor != null) {
return monitor.CanTargetEntity(target);
}
return null;
}
// Used for Slasher game mode to make it night time, disabled until spectate has been fixed
//private object CanNetworkTo(BaseNetworkable entity, BasePlayer player)
//{
// if (!(entity is EnvSync))
// return null;
// Arena.EventPlayer eventPlayer = Arena.GetUser(player);
// if (eventPlayer == null)
// return null;
// Slasher manager = Arena.GetEvent(eventPlayer.currentEvent) as Slasher;
// if (manager == null)
// return null;
// if (manager.matchStatus == Slasher.MatchStatus.Pending)
// return null;
// EnvSync env = entity as EnvSync;
// if (Net.sv.write.Start())
// {
// Connection connection = player.net.connection;
// connection.validate.entityUpdates = connection.validate.entityUpdates + 1;
// BaseNetworkable.SaveInfo saveInfo = new BaseNetworkable.SaveInfo
// {
// forConnection = player.net.connection,
// forDisk = false
// };
// Net.sv.write.PacketID(Message.Type.Entities);
// Net.sv.write.UInt32(player.net.connection.validate.entityUpdates);
// using (saveInfo.msg = Pool.Get<Entity>())
// {
// env.Save(saveInfo);
// saveInfo.msg.environment.dateTime = manager.timeSync;
// saveInfo.msg.ToProto(Net.sv.write);
// env.PostSave(saveInfo);
// Net.sv.write.Send(new SendInfo(player.net.connection));
// }
// }
// return false;
//}
private void Unload() {
if (Arena != null) Arena.isUnloading = true;
UnityEngine.Object.Destroy(frameBudgeter.gameObject);
isUnloading = true;
ins = null;
Statistics = null;
Arena = null;
}
#endregion
#region NPC Logic Limiter
private class FrameBudgeter : MonoBehaviour {
private double maxMilliseconds = 1f;
private Stopwatch sw = Stopwatch.StartNew();
private int lastIndex = 0;
private void Update() {
sw.Reset();
sw.Start();
int count = NPCSurvival.NPCController.allNpcs?.Count ?? 0;
if (lastIndex >= count) lastIndex = 0;
for (int i = lastIndex; i < count; i++) {
if (sw.Elapsed.TotalMilliseconds > maxMilliseconds) {
lastIndex = i;
return;
}
NPCSurvival.NPCController.allNpcs[i]?.DoUpdate();
}
lastIndex = 0;
}
}
#endregion
#region Functions
private void ToggleHookSubscription(bool enabled) {
if (enabled) {
Subscribe(nameof(OnEntityTakeDamage));
Subscribe(nameof(OnEntityDeath));
Subscribe(nameof(OnEntitySpawned));
Subscribe(nameof(OnPlayerDropActiveItem));
}
else {
Unsubscribe(nameof(OnEntityTakeDamage));
Unsubscribe(nameof(OnEntityDeath));
Unsubscribe(nameof(OnEntitySpawned));
Unsubscribe(nameof(OnPlayerDropActiveItem));
}
}
public void RegisterEvents(Arena arena) {
Arena = arena;
Statistics = Arena.Statistics;
ToggleHookSubscription(true);
Arena.eventTypes.Add("Free For All");
Arena.eventTypes.Add("Team Deathmatch");
Arena.eventTypes.Add("Survival");
Arena.eventTypes.Add("Team Survival");
Arena.eventTypes.Add("GunGame");
Arena.eventTypes.Add("One in the Chamber");
Arena.eventTypes.Add("Capture the Flag");
Arena.eventTypes.Add("NPC Survival");
//Arena.eventTypes.Add("Slasher"); Disabled until spectate is fixed...//Arena.eventTypes.Add("Infected");
Arena.additionalParameters.Add("GunGame", new List<Arena.AdditionalParamData> {
new Arena.AdditionalParamData {
field = "Weapon Set", isRequired = true, type = "string", useSelector = true, selectorHook = "GetWeaponSets", selectorDesc = "Select a weapon set", useInput = false
}
, new Arena.AdditionalParamData {
field = "Use Downgrade Weapon", isRequired = true, type = "bool", useToggle = true
}
, new Arena.AdditionalParamData {
field = "Player Gear", isRequired = true, type = "string", useSelector = true, selectorDesc = "Select a kit for the players gear (No Weapons)", selectorHook = "GetAllKits"
}
}
);
Arena.additionalParameters.Add("One in the Chamber", new List<Arena.AdditionalParamData> {
new Arena.AdditionalParamData {
field = "Player Lives", isRequired = true, requireValue = true, type = "int", useInput = true
}
, new Arena.AdditionalParamData {
field = "Player Gear", isRequired = true, type = "string", useSelector = true, selectorDesc = "Select a kit for the players gear (No Weapons)", selectorHook = "GetAllKits"
}
, new Arena.AdditionalParamData {
field = "Primary Weapon", isRequired = true, type = "string", useSelector = true, selectorHook = "GetAllWeapons", selectorDesc = "Select a primary weapon.This will be the weapon that receives bullets for kills",
}
, new Arena.AdditionalParamData {
field = "Secondary Weapon", isRequired = true, type = "string", useSelector = true, selectorHook = "GetAllWeapons", selectorDesc = "Select a secondary weapon.This will be the weapon that you use when you run out of bullets",
}
,
}
);
Arena.additionalParameters.Add("NPC Survival", new List<Arena.AdditionalParamData> {
new Arena.AdditionalParamData {
field = "Player Lives", isRequired = true, requireValue = true, type = "int", useInput = true
}
, new Arena.AdditionalParamData {
field = "Rounds to Play", isRequired = true, requireValue = true, type = "int", useInput = true
}
, new Arena.AdditionalParamData {
field = "NPCs to Spawn", isRequired = true, requireValue = true, type = "int", useInput = true
}
, new Arena.AdditionalParamData {
field = "Additional NPCs (per round)", isRequired = true, type = "int", useInput = true
}
, new Arena.AdditionalParamData {
field = "NPC Spawnfile", isRequired = true, type = "string", useSelector = true, selectorHook = "GetSpawnfileNames", selectorDesc = "Select a spawnfile for NPCs",
}
, new Arena.AdditionalParamData {
field = "Use Scientists", isRequired = true, type = "bool", useToggle = true
}
, new Arena.AdditionalParamData {
field = "Use Murderers", isRequired = true, type = "bool", useToggle = true
}
}
);
Arena.additionalParameters.Add("Infected", new List<Arena.AdditionalParamData>() {
new Arena.AdditionalParamData {
field = "Infected Kit", isRequired = true, type = "string", useSelector = true, selectorDesc = "Select a kit for the infected", selectorHook = "GetAllKits"
}
, new Arena.AdditionalParamData {
field = "Infected Spawnfile", isRequired = true, type = "string", useSelector = true, selectorHook = "GetSpawnfileNames", selectorDesc = "Select a spawnfile for the infected",
}
, new Arena.AdditionalParamData {
field = "Survivors start with random weapon", isRequired = true, type = "bool", useToggle = true
}
, new Arena.AdditionalParamData {
field = "Round Timer", isRequired = true, requireValue = true, type = "int", useInput = true
}
,
}
);
//Arena.additionalParameters.Add("Slasher", new List<Arena.AdditionalParamData>
// {
// new Arena.AdditionalParamData
// {
// field = "Rounds to Play",
// isRequired = true,
// requireValue = true,
// type = "int",
// useInput = true
// },
// new Arena.AdditionalParamData
// {
// field = "Slasher Kill Timer (seconds)",
// isRequired = true,
// requireValue = true,
// type = "int",
// useInput = true
// },
// new Arena.AdditionalParamData
// {
// field = "Player Kill Timer (seconds)",
// isRequired = true,
// requireValue = true,
// type = "int",
// useInput = true
// },
// new Arena.AdditionalParamData
// {
// field = "Slasher Weapon",
// isRequired = true,
// type = "string",
// useSelector = true,
// selectorHook = "GetAllWeapons",
// selectorDesc = "Select the weapon the slasher will use.This weapon will be automatically equipped with a flashlight",
// },
// new Arena.AdditionalParamData
// {
// field = "Slasher Kit",
// isRequired = true,
// type = "string",
// useSelector = true,
// selectorDesc = "Select a kit for the slasher (no weapons)",
// selectorHook = "GetAllKits"
// },
// new Arena.AdditionalParamData
// {
// field = "Slasher Spawnfile",
// isRequired = true,
// type = "string",
// useSelector = true,
// selectorHook = "GetSpawnfileNames",
// selectorDesc = "Select a spawnfile for the slasher",
// },
// new Arena.AdditionalParamData
// {
// field = "Player Kit",
// isRequired = true,
// type = "string",
// useSelector = true,
// selectorDesc = "Select a kit for the hunted (no weapons)",
// selectorHook = "GetAllKits"
// },
// new Arena.AdditionalParamData
// {
// field = "Player Damage Modifier",
// isRequired = true,
// requireValue = true,
// type = "float",
// useInput = true
// },
// });
}
public object SetEventMode(string eventName, GameObject eventManager) {
switch (eventName) {
case "Free For All" : eventManager.gameObject.AddComponent<FreeForAll>();
return true;
case "Team Deathmatch" : eventManager.gameObject.AddComponent<TDM>();
return true;
case "Survival" : eventManager.gameObject.AddComponent<Survival>();
return true;
case "Team Survival" : eventManager.gameObject.AddComponent<TeamSurvival>();
return true;
case "One in the Chamber" : eventManager.gameObject.AddComponent<OITC>();
return true;
case "GunGame" : eventManager.gameObject.AddComponent<GunGame>();
return true;
case "Capture the Flag" : eventManager.gameObject.AddComponent<CTF>();
return true;
case "NPC Survival" : eventManager.gameObject.AddComponent<NPCSurvival>();
return true;
//case "Slasher":
// eventManager.gameObject.AddComponent<Slasher>();
// return true;
case "Infected" : eventManager.gameObject.AddComponent<Infected>();
return true;
default : break;
}
return null;
}
private string[] GetWeaponSets() => storedData.gungameSets.Keys.ToArray();
private string[] GetAllWeapons() => ItemManager.itemList.Where(x => x.category == ItemCategory.Weapon).Select(x => x.shortname).ToArray();
#endregion
#region Event Require Checks
public bool IsTeamEvent(string eventType) {
if (eventType == "Team Deathmatch" || eventType == "Team Survival" || eventType == "Capture the Flag") return true;
return false;
}
public bool IsSurvivalEvent(string eventType) {
if (eventType == "Survival" || eventType == "Team Survival" || eventType == "One in the Chamber" || eventType == "NPC Survival") return true;
return false;
}
public bool IgnoreKillLimit(string eventType) {
if (eventType == "Survival" || eventType == "Team Survival" || eventType == "One in the Chamber" || eventType == "GunGame" || eventType == "NPC Survival" || eventType == "Slasher" || eventType == "Infected") return true;
return false;
}
public bool IgnoreTimeLimit(string eventType) {
if (eventType == "Slasher" || eventType == "Infected") return true;
return false;
}
public bool IgnoreKitSelection(string eventType) {
if (eventType == "One in the Chamber" || eventType == "GunGame" || eventType == "Slasher") return true;
return false;
}
public bool IgnoreClassSelector(string eventType) {
if (eventType == "One in the Chamber" || eventType == "GunGame" || eventType == "NPC Survival" || eventType == "Slasher" || eventType == "Infected") return true;
return false;
}
public bool RequiresTimeOrKillLimit(string eventType) {
if (eventType == "Team Deathmatch" || eventType == "Free For All" || eventType == "Capture the Flag") return true;
return false;
}
#endregion
#region Game Modes
#region Free For All
private class FreeForAll : Arena.EventManager {
private Arena.EventPlayer winner = null;
public override void Prestart() {
winner = null;
base.Prestart();
}
public override void JoinEvent(Arena.EventPlayer eventPlayer, Arena.Team team = Arena.Team.None) {
if (eventPlayers.Contains(eventPlayer)) return;
eventPlayer.ResetStats();
eventPlayer.team = team;
eventPlayers.Add(eventPlayer);
BroadcastToPlayers("joinevent", new string[] {
eventPlayer.player.displayName
}
);
base.JoinEvent(eventPlayer, team);
switch (status) {
case Arena.EventStatus.Pending : if (Arena.configData.EventSettings.SendToArenaOnJoin) SpawnPlayer(eventPlayer, false, true);
else BroadcastToPlayer(eventPlayer, Arena.msg("waitingForPlayers", eventPlayer.player.userID));
return;
case Arena.EventStatus.Finished : if (eventPlayers.Count >= config.minimumPlayers && status == Arena.EventStatus.Finished) {
BroadcastToPlayers("playersreached", new string[] {
Arena.configData.GameTimers.MatchPrestart[config.eventType].ToString()
}
, true);
Prestart();
}
else {
if (Arena.configData.EventSettings.SendToArenaOnJoin) SpawnPlayer(eventPlayer, false, true);
else BroadcastToPlayer(eventPlayer, Arena.msg("waitingForPlayers", eventPlayer.player.userID));
}
return;
case Arena.EventStatus.Started : SpawnPlayer(eventPlayer, true, true);
return;
}
}
public override void LeaveEvent(Arena.EventPlayer eventPlayer) {
if (status != Arena.EventStatus.Finished) BroadcastToPlayers("leftevent", new string[] {
eventPlayer.player.displayName
}
);
base.LeaveEvent(eventPlayer);
}
public override void OnPlayerDeath(Arena.EventPlayer victim, BasePlayer attacker = null, HitInfo info = null) {
if (victim == null) return;
victim.AddDeath();
if (attacker != null) {
Arena.EventPlayer attackerPlayer = Arena.GetUser(attacker);
if (attackerPlayer != null && victim != attackerPlayer) {
attackerPlayer.AddKill();
if (config.killLimit > 0 && attackerPlayer.kills >= config.killLimit) {
winner = attackerPlayer;
EndMatch();
return;
}
}
}
UpdateScoreboard();
base.OnPlayerDeath(victim, attacker);
}
public override void EndMatch() {
if (isUnloading || (Arena != null && Arena.isUnloading)) return;
if (winner == null) {
if (eventPlayers.Count > 0) {
int kills = 0;
int deaths = 0;
foreach (Arena.EventPlayer eventPlayer in eventPlayers) {
if (eventPlayer.kills > kills) {
winner = eventPlayer;
kills = eventPlayer.kills;
deaths = eventPlayer.deaths;
}
else if (eventPlayer.kills == kills) {
if (eventPlayer.deaths < deaths) {
winner = eventPlayer;
kills = eventPlayer.kills;
deaths = eventPlayer.deaths;
}
}
}
}
}
List<ulong> losers = eventPlayers.Select(x => x.player.userID).ToList();
if (winner != null) {
Statistics.OnEventWin(new List<ulong> {
winner.player.userID
}
, config);
BroadcastWinners("globalwin", new string[] {
winner.player.displayName, config.eventName
}
);
losers.Remove(winner.player.userID);
}
Statistics.OnEventLose(losers, config);
foreach (Arena.EventPlayer eventPlayer in eventPlayers) Statistics.OnGamePlayed(eventPlayer.player.userID, config);
base.EndMatch();
}
}
#endregion
#region Team Deathmatch
private class TDM : Arena.EventManager {
public int teamAKills;
public int teamBKills;
public override void Prestart() {
teamAKills = 0;
teamBKills = 0;
base.Prestart();
}
public override void StartMatch() {
BalanceTeams();
base.StartMatch();
}
public override void JoinEvent(Arena.EventPlayer eventPlayer, Arena.Team team = Arena.Team.None) {
if (eventPlayers.Contains(eventPlayer)) return;
eventPlayer.ResetStats();
Arena.LockInventory(eventPlayer.player);
eventPlayer.team = team;
eventPlayers.Add(eventPlayer);
BroadcastToPlayers("joinevent", new string[] {
eventPlayer.player.displayName
}
);
base.JoinEvent(eventPlayer, team);
switch (status) {
case Arena.EventStatus.Pending : if (Arena.configData.EventSettings.SendToArenaOnJoin) SpawnPlayer(eventPlayer, false, true);
else BroadcastToPlayer(eventPlayer, Arena.msg("waitingForPlayers", eventPlayer.player.userID));
return;
case Arena.EventStatus.Finished : if (eventPlayers.Count >= config.minimumPlayers && status == Arena.EventStatus.Finished) {
BroadcastToPlayers("playersreached", new string[] {
Arena.configData.GameTimers.MatchPrestart[config.eventType].ToString()
}
, true);
Prestart();
}
else {
if (Arena.configData.EventSettings.SendToArenaOnJoin) SpawnPlayer(eventPlayer, false, true);
else BroadcastToPlayer(eventPlayer, Arena.msg("waitingForPlayers", eventPlayer.player.userID));
}
return;
case Arena.EventStatus.Started : SpawnPlayer(eventPlayer, true, true);
return;
}
}
public override void LeaveEvent(Arena.EventPlayer eventPlayer) {
Arena.UnlockInventory(eventPlayer.player);
if (status != Arena.EventStatus.Finished) BroadcastToPlayers("leftevent", new string[] {
eventPlayer.player.displayName
}
);
base.LeaveEvent(eventPlayer);
if (ins.configData.BalanceOnLeave) BalanceTeams();
}
public override void OnPlayerTakeDamage(Arena.EventPlayer eventPlayer, HitInfo info) {
BasePlayer attacker = info.InitiatorPlayer;
if (attacker != null) {
Arena.EventPlayer eventAttacker = Arena.GetUser(attacker);
if (eventAttacker != null) {
if (eventAttacker.team == eventPlayer.team) {
BroadcastToPlayer(eventAttacker, Arena.msg("friendlyFire", attacker.userID));
if (!config.ffEnabled) {
Arena.NullifyDamage(info);
return;
}
}
}
}
base.OnPlayerTakeDamage(eventPlayer, info);
}
public override void OnPlayerDeath(Arena.EventPlayer victim, BasePlayer attacker = null, HitInfo info = null) {
if (victim == null) return;
victim.AddDeath();
if (attacker != null) {
Arena.EventPlayer attackerPlayer = Arena.GetUser(attacker);
if (attackerPlayer != null && victim != attackerPlayer) {
attackerPlayer.AddKill();
if (attackerPlayer.team == Arena.Team.A) teamAKills++;
if (attackerPlayer.team == Arena.Team.B) teamBKills++;
if (config.killLimit > 0 && (teamAKills >= config.killLimit || teamBKills >= config.killLimit)) {
EndMatch();
return;
}
}
}
UpdateScoreboard();
base.OnPlayerDeath(victim, attacker);
}
public override void GiveTeamGear(Arena.EventPlayer eventPlayer) {
string kitName = eventPlayer.team == Arena.Team.A ? config.teamA.kit : config.teamB.kit;
Arena.GiveKit(eventPlayer.player, kitName);
}
public override void EndMatch() {
if (isUnloading || (Arena != null && Arena.isUnloading)) return;
Arena.Team team = teamAKills > teamBKills ? Arena.Team.A : teamBKills > teamAKills ? Arena.Team.B : Arena.Team.None;
List<ulong> losers = eventPlayers.Where(x => x.team != team).Select(y => y.player.userID).ToList();
Statistics.OnEventLose(losers, config);
if (team != Arena.Team.None) {
List<ulong> winners = eventPlayers.Where(x => x.team == team).Select(y => y.player.userID).ToList();
Statistics.OnEventWin(winners, config);
BroadcastWinners("globalteamwin", new string[] {
team == Arena.Team.A ? (string.IsNullOrEmpty(config.teamA.name) ? Arena.msg("Team A") : config.teamA.name) : (string.IsNullOrEmpty(config.teamB.name) ? Arena.msg("Team B") : config.teamB.name), eventPlayers.Where(x => x.team == team).Select(y => y.player.displayName).ToSentence(), config.eventName
}
);
}
foreach (Arena.EventPlayer eventPlayer in eventPlayers) Statistics.OnGamePlayed(eventPlayer.player.userID, config);
base.EndMatch();
}
}
#endregion
#region Survival
private class Survival : Arena.EventManager {
private List<Arena.DeadPlayer> deadPlayers = new List<Arena.DeadPlayer>();
private Arena.EventPlayer winner;
public override void Prestart() {
winner = null;
deadPlayers.Clear();
base.Prestart();
}
public override bool CanJoinEvent() {
if (status == Arena.EventStatus.Started) return false;
return base.CanJoinEvent();
}
public override void JoinEvent(Arena.EventPlayer eventPlayer, Arena.Team team = Arena.Team.None) {
if (eventPlayers.Contains(eventPlayer)) return;
eventPlayer.ResetStats();
eventPlayer.team = team;
eventPlayers.Add(eventPlayer);
BroadcastToPlayers("joinevent", new string[] {
eventPlayer.player.displayName
}
);
base.JoinEvent(eventPlayer, team);
switch (status) {
case Arena.EventStatus.Pending : if (Arena.configData.EventSettings.SendToArenaOnJoin) SpawnPlayer(eventPlayer, false, true);
else BroadcastToPlayer(eventPlayer, Arena.msg("waitingForPlayers", eventPlayer.player.userID));
break;
case Arena.EventStatus.Finished : if (eventPlayers.Count >= config.minimumPlayers && status == Arena.EventStatus.Finished) {
BroadcastToPlayers("playersreached", new string[] {
Arena.configData.GameTimers.MatchPrestart[config.eventType].ToString()
}
, true);
Prestart();
}
else {
if (Arena.configData.EventSettings.SendToArenaOnJoin) SpawnPlayer(eventPlayer, false, true);
else BroadcastToPlayer(eventPlayer, Arena.msg("waitingForPlayers", eventPlayer.player.userID));
}
break;
case Arena.EventStatus.Started : return;
}
}
public override void LeaveEvent(Arena.EventPlayer eventPlayer) {
deadPlayers.Add(new Arena.DeadPlayer(eventPlayer, eventPlayer.kills, eventPlayer.deaths));
if (status != Arena.EventStatus.Finished) BroadcastToPlayers("survleftevent", new string[] {
eventPlayer.player.displayName, (eventPlayers.Count - 1).ToString()
}
);
base.LeaveEvent(eventPlayer);
}
public override void OnPlayerDeath(Arena.EventPlayer victim, BasePlayer attacker = null, HitInfo info = null) {
if (victim == null) return;
victim.isDead = true;
victim.isEliminated = true;
victim.AddDeath();
if (attacker != null) {
Arena.EventPlayer attackerPlayer = Arena.GetUser(attacker);
if (attackerPlayer != null && victim != attackerPlayer) attackerPlayer.AddKill();
}
base.OnPlayerDeath(victim, attacker, info);
if (!deathKick) {
deadPlayers.Add(new Arena.DeadPlayer(victim, victim.kills, victim.deaths));
foreach (Arena.EventPlayer spectator in eventPlayers.Where(x => x.player.IsSpectating())) spectator.UpdateSpectateTarget();
BroadcastToPlayer(victim, Arena.msg("youareout", victim.player.userID));
victim.BeginSpectating();
}
else LeaveEvent(victim);
UpdateScoreboard();
bool allDead = true;
foreach (Arena.EventPlayer eventPlayer in eventPlayers) {
if (!eventPlayer.isEliminated) {
allDead = false;
break;
}
}
if (allDead || eventPlayers.Count <= 1) EndMatch();
if (CountAlive() == 1) {
Arena.EventPlayer[] winners = eventPlayers.Where(x => !x.isDead && !x.player.IsSpectating()).ToArray();
if (winners.Length == 1) winner = winners[0];
EndMatch();
}
}
public override void EndMatch() {
if (isUnloading || (Arena != null && Arena.isUnloading)) return;
if (winner == null) {
if (eventPlayers.Count > 0) {
int kills = 0;
int deaths = 0;
foreach (Arena.EventPlayer eventPlayer in eventPlayers.Where(x => !x.isDead)) {
if (eventPlayer.kills > kills) {
winner = eventPlayer;
kills = eventPlayer.kills;
deaths = eventPlayer.deaths;
}
else if (eventPlayer.kills == kills) {
if (eventPlayer.deaths < deaths) {
winner = eventPlayer;
kills = eventPlayer.kills;
deaths = eventPlayer.deaths;
}
}
}
}
}
if (winner != null) {
Statistics.OnGamePlayed(winner.player.userID, config);
Statistics.OnEventWin(new List<ulong> {
winner.player.userID
}
, config);
BroadcastToPlayer(winner, Arena.msg("youarewinner", winner.player.userID));
BroadcastWinners("globalwin", new string[] {
winner.player.displayName, config.eventName
}
);
}
List<ulong> losers = deadPlayers.Select(x => x.playerId).ToList();
losers.AddRange(eventPlayers.Where(x => x.isDead).Select(x => x.player.userID));
foreach (ulong loser in losers) Statistics.OnGamePlayed(loser, config);
Statistics.OnEventLose(losers, config);
base.EndMatch();
}
public override List<Arena.ScoreEntry> GetGameScores(Arena.Team team = Arena.Team.A) {
List<Arena.ScoreEntry> gameScores = new List<Arena.ScoreEntry>();
ulong[] deadPlayerIds = deadPlayers.Select(x => x.playerId).ToArray();
int i = 1;
foreach (Arena.EventPlayer eventPlayer in eventPlayers.OrderByDescending(x => x.kills)) {
if (deadPlayerIds.Contains(eventPlayer.player.userID)) continue;
gameScores.Add(new Arena.ScoreEntry(eventPlayer, i, eventPlayer.kills, eventPlayer.deaths));
i++;
}
List<Arena.DeadPlayer> reversedDead = new List<Arena.DeadPlayer>(deadPlayers);
reversedDead.Reverse();
foreach (Arena.DeadPlayer deadPlayer in reversedDead) {
if (gameScores.Find(x => x.displayName == deadPlayer.playerName) != null) continue;
gameScores.Add(new Arena.ScoreEntry() {
position = i, value2 = deadPlayer.value2, displayName = $"{Arena.msg("deadplayer")} {deadPlayer.playerName}", value1 = deadPlayer.value1
}
);
i++;
}
return gameScores;
}
public override string GetScoreString() => Arena.msg("Survivors");
}
#endregion
#region Team Survival
private class TeamSurvival : Arena.EventManager {
private List<Arena.DeadPlayer> deadPlayers = new List<Arena.DeadPlayer>();
private Arena.Team winners;
public override void Prestart() {
winners = Arena.Team.None;
deadPlayers.Clear();
base.Prestart();
}
public override void StartMatch() {
BalanceTeams();
base.StartMatch();
}
public override bool CanJoinEvent() {
if (status == Arena.EventStatus.Started) return false;
return base.CanJoinEvent();
}
public override void JoinEvent(Arena.EventPlayer eventPlayer, Arena.Team team = Arena.Team.None) {
if (eventPlayers.Contains(eventPlayer)) return;
eventPlayer.ResetStats();
Arena.LockInventory(eventPlayer.player);
eventPlayer.team = team;
eventPlayers.Add(eventPlayer);
BroadcastToPlayers("joinevent", new string[] {
eventPlayer.player.displayName
}
);
base.JoinEvent(eventPlayer, team);
switch (status) {
case Arena.EventStatus.Pending : if (Arena.configData.EventSettings.SendToArenaOnJoin) SpawnPlayer(eventPlayer, false, true);
else BroadcastToPlayer(eventPlayer, Arena.msg("waitingForPlayers", eventPlayer.player.userID));
return;
case Arena.EventStatus.Finished : if (eventPlayers.Count >= config.minimumPlayers && status == Arena.EventStatus.Finished) {
BroadcastToPlayers("playersreached", new string[] {
Arena.configData.GameTimers.MatchPrestart[config.eventType].ToString()
}
, true);
Prestart();
}
else {
if (Arena.configData.EventSettings.SendToArenaOnJoin) SpawnPlayer(eventPlayer, false, true);
else BroadcastToPlayer(eventPlayer, Arena.msg("waitingForPlayers", eventPlayer.player.userID));
}
return;
case Arena.EventStatus.Started : SpawnPlayer(eventPlayer, true, true);
return;
}
}
public override void LeaveEvent(Arena.EventPlayer eventPlayer) {
deadPlayers.Add(new Arena.DeadPlayer(eventPlayer, eventPlayer.kills, eventPlayer.deaths));
Arena.UnlockInventory(eventPlayer.player);
if (status != Arena.EventStatus.Finished) BroadcastToPlayers("leftevent", new string[] {
eventPlayer.player.displayName
}
);
base.LeaveEvent(eventPlayer);
if (ins.configData.BalanceOnLeave) BalanceTeams();
}
public override void OnPlayerTakeDamage(Arena.EventPlayer eventPlayer, HitInfo info) {
BasePlayer attacker = info.InitiatorPlayer;
if (attacker != null) {
Arena.EventPlayer eventAttacker = Arena.GetUser(attacker);
if (eventAttacker != null) {
if (eventAttacker.team == eventPlayer.team) {
BroadcastToPlayer(eventAttacker, Arena.msg("friendlyFire", attacker.userID));
if (!config.ffEnabled) {
Arena.NullifyDamage(info);
return;
}
}
}
}
base.OnPlayerTakeDamage(eventPlayer, info);
}
public override void OnPlayerDeath(Arena.EventPlayer victim, BasePlayer attacker = null, HitInfo info = null) {
if (victim == null) return;
victim.isDead = true;
victim.isEliminated = true;
victim.AddDeath();
if (attacker != null) {
Arena.EventPlayer attackerPlayer = Arena.GetUser(attacker);
if (attackerPlayer != null && victim != attackerPlayer) attackerPlayer.AddKill();
}
base.OnPlayerDeath(victim, attacker);
if (!deathKick) {
deadPlayers.Add(new Arena.DeadPlayer(victim, victim.kills, victim.deaths));
foreach (Arena.EventPlayer spectator in eventPlayers.Where(x => x.player.IsSpectating())) spectator.UpdateSpectateTarget();
BroadcastToPlayer(victim, Arena.msg("youareout", victim.player.userID));
victim.BeginSpectating();
}
else LeaveEvent(victim);
UpdateScoreboard();
int teamACount = GetTeamAlive(Arena.Team.A);
int teamBCount = GetTeamAlive(Arena.Team.B);
if (teamACount == 0) winners = Arena.Team.A;
if (teamBCount == 0) winners = Arena.Team.B;
if (winners != Arena.Team.None) {
BroadcastWinners("globalteamwin", new string[] {
winners == Arena.Team.A ? (string.IsNullOrEmpty(config.teamA.name) ? Arena.msg("Team A") : config.teamA.name) : (string.IsNullOrEmpty(config.teamB.name) ? Arena.msg("Team B") : config.teamB.name), eventPlayers.Where(x => x.team == winners).Select(y => y.player.displayName).ToSentence(), config.eventName
}
);
BroadcastToPlayers("teamwin", new string[] {
winners == Arena.Team.A ? (string.IsNullOrEmpty(config.teamA.name) ? Arena.msg("Team A") : config.teamA.name) : (string.IsNullOrEmpty(config.teamB.name) ? Arena.msg("Team B") : config.teamB.name)
}
);
InvokeHandler.Invoke(this, EndMatch, 2f);
return;
}
bool allDead = true;
foreach (Arena.EventPlayer eventPlayer in eventPlayers) {
if (!eventPlayer.isEliminated) {
allDead = false;
break;
}
}
if (allDead || eventPlayers.Count <= 1) EndMatch();
}
public override void GiveTeamGear(Arena.EventPlayer eventPlayer) {
string kitName = eventPlayer.team == Arena.Team.A ? config.teamA.kit : config.teamB.kit;
Arena.GiveKit(eventPlayer.player, kitName);
}
public override void EndMatch() {
if (isUnloading || (Arena != null && Arena.isUnloading)) return;
int teamACount = GetTeamCount(Arena.Team.A);
int teamBCount = GetTeamCount(Arena.Team.B);
winners = teamACount > teamBCount ? Arena.Team.A : teamBCount > teamACount ? Arena.Team.B : Arena.Team.None;
List<ulong> winnerPlayers = eventPlayers.Where(x => x.team == winners).Select(y => y.player.userID).ToList();
Statistics.OnEventWin(winnerPlayers, config);
List<ulong> loserPlayers = eventPlayers.Where(x => x.team != winners).Select(y => y.player.userID).ToList();
loserPlayers.AddRange(deadPlayers.Where(x => x.team != winners).Select(x => x.playerId));
Statistics.OnEventLose(loserPlayers, config);
foreach (ulong winner in winnerPlayers) Statistics.OnGamePlayed(winner, config);
foreach (ulong loser in loserPlayers) Statistics.OnGamePlayed(loser, config);
base.EndMatch();
}
public override List<Arena.ScoreEntry> GetGameScores(Arena.Team team = Arena.Team.A) {
List<Arena.ScoreEntry> gameScores = new List<Arena.ScoreEntry>();
ulong[] deadPlayerIds = deadPlayers.Select(x => x.playerId).ToArray();
int i = 1;
foreach (Arena.EventPlayer eventPlayer in eventPlayers.Where(x => x.team == team).OrderByDescending(x => x.kills)) {
if (deadPlayerIds.Contains(eventPlayer.player.userID)) continue;
gameScores.Add(new Arena.ScoreEntry(eventPlayer, i, eventPlayer.kills, eventPlayer.deaths));
i++;
}
List<Arena.DeadPlayer> reversedDead = new List<Arena.DeadPlayer>(deadPlayers.Where(x => x.team == team));
reversedDead.Reverse();
foreach (Arena.DeadPlayer deadPlayer in reversedDead) {
if (gameScores.Find(x => x.displayName == deadPlayer.playerName) != null) continue;
gameScores.Add(new Arena.ScoreEntry() {
position = i, value2 = deadPlayer.value2, displayNam
// ... [Code preview truncated due to length. Full source in attached file] ...