C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
// Requires: EventManager
using Newtonsoft.Json;
using Oxide.Plugins.EventManagerEx;
using System;
using System.Collections.Generic;
using System.Globalization;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("CaptureTheFlag", "k1lly0u", "0.4.0"), Description("Capture the flag event mode for EventManager")]
class CaptureTheFlag : RustPlugin, IEventPlugin
{
#region Oxide Hooks
private void OnServerInitialized()
{
EventManager.RegisterEvent(Title, this);
GetMessage = Message;
}
protected override void LoadDefaultMessages() => lang.RegisterMessages(Messages, this);
private object CanUpdateSign(BasePlayer player, Signage sign) => sign.GetComponentInParent<FlagController>() != null ? (object)true : null;
private void Unload()
{
if (!EventManager.IsUnloading)
EventManager.UnregisterEvent(Title);
Configuration = null;
}
#endregion
#region Event Checks
public bool InitializeEvent(EventManager.EventConfig config) => EventManager.InitializeEvent<CaptureTheFlagEvent>(this, config);
public bool CanUseClassSelector => true;
public bool RequireTimeLimit => true;
public bool RequireScoreLimit => false;
public bool UseScoreLimit => true;
public bool UseTimeLimit => true;
public bool IsTeamEvent => true;
public void FormatScoreEntry(EventManager.ScoreEntry scoreEntry, ulong langUserId, out string score1, out string score2)
{
score1 = string.Format(Message("Score.FlagCaptures", langUserId), scoreEntry.value1);
score2 = string.Format(Message("Score.Kills", langUserId), scoreEntry.value2);
}
public List<EventManager.EventParameter> AdditionalParameters { get; } = new List<EventManager.EventParameter>
{
new EventManager.EventParameter
{
DataType = "int",
Field = "flagRespawnTimer",
Input = EventManager.EventParameter.InputType.InputField,
IsRequired = true,
Name = "Flag Reset Time",
DefaultValue = 30
},
};
public string ParameterIsValid(string fieldName, object value) => null;
#endregion
#region Event Classes
public class CaptureTheFlagEvent : EventManager.BaseEventGame
{
public EventManager.Team winningTeam;
internal int flagRespawnTime;
private int teamAScore;
private int teamBScore;
private EventManager.Team lastTeam = EventManager.Team.B;
internal FlagController TeamAFlag { get; private set; }
internal FlagController TeamBFlag { get; private set; }
internal override void InitializeEvent(IEventPlugin plugin, EventManager.EventConfig config)
{
flagRespawnTime = config.GetParameter<int>("flagRespawnTimer");
base.InitializeEvent(plugin, config);
TeamAFlag = FlagController.Create(this, EventManager.Team.A, _spawnSelectorA.ReserveSpawnPoint(0));
TeamBFlag = FlagController.Create(this, EventManager.Team.B, _spawnSelectorB.ReserveSpawnPoint(0));
}
protected override void StartEvent()
{
BalanceTeams();
base.StartEvent();
}
internal override void EndEvent()
{
TeamAFlag.DropFlag(false);
TeamBFlag.DropFlag(false);
base.EndEvent();
}
protected override void OnDestroy()
{
Destroy(TeamAFlag);
Destroy(TeamBFlag);
base.OnDestroy();
}
protected override EventManager.BaseEventPlayer AddPlayerComponent(BasePlayer player) => player.gameObject.AddComponent<CaptureTheFlagPlayer>();
protected override EventManager.Team GetPlayerTeam(BasePlayer player) => lastTeam = lastTeam == EventManager.Team.B ? EventManager.Team.A : EventManager.Team.B;
internal override int GetTeamScore(EventManager.Team team) => team == EventManager.Team.B ? teamBScore : teamAScore;
internal override void OnEventPlayerDeath(EventManager.BaseEventPlayer victim, EventManager.BaseEventPlayer attacker = null, HitInfo info = null)
{
if (victim == null)
return;
if ((victim as CaptureTheFlagPlayer).IsCarryingFlag)
{
FlagController flagController = victim.Team == EventManager.Team.B ? TeamAFlag : TeamBFlag;
if (flagController.FlagHolder == victim)
{
flagController.DropFlag(true);
BroadcastToPlayers(GetMessage, "Notification.FlagDropped", victim.Player.displayName, flagController.Team, GetTeamColor(victim.Team), GetTeamColor(flagController.Team));
}
}
victim.OnPlayerDeath(attacker, Configuration.RespawnTime);
if (attacker != null && victim != attacker && victim.Team != attacker.Team)
attacker.OnKilledPlayer(info);
UpdateScoreboard();
base.OnEventPlayerDeath(victim, attacker);
}
protected override void GetWinningPlayers(ref List<EventManager.BaseEventPlayer> winners)
{
if (winningTeam != EventManager.Team.None)
{
if (eventPlayers.Count > 0)
{
for (int i = 0; i < eventPlayers.Count; i++)
{
EventManager.BaseEventPlayer eventPlayer = eventPlayers[i];
if (eventPlayer == null)
continue;
if (eventPlayer.Team == winningTeam)
winners.Add(eventPlayer);
}
}
}
}
internal void OnFlagCaptured(CaptureTheFlagPlayer eventPlayer, EventManager.Team team)
{
int flagCaptures;
if (eventPlayer.Team == EventManager.Team.B)
flagCaptures = teamBScore += 1;
else flagCaptures = teamAScore += 1;
eventPlayer.FlagCaptures += 1;
BroadcastToPlayers(GetMessage, "Notification.FlagCaptured", eventPlayer.Player.displayName, team, GetTeamColor(eventPlayer.Team), GetTeamColor(team));
UpdateScoreboard();
if (flagCaptures >= Config.ScoreLimit)
{
winningTeam = eventPlayer.Team;
InvokeHandler.Invoke(this, EndEvent, 0.1f);
}
}
internal string GetTeamColor(EventManager.Team team) => team == EventManager.Team.B ? TeamBColor : TeamAColor;
#region Scoreboards
protected override void BuildScoreboard()
{
scoreContainer = EMInterface.CreateScoreboardBase(this);
int index = -1;
EMInterface.CreatePanelEntry(scoreContainer, string.Format(GetMessage("Score.Team", 0UL), teamAScore, TeamAColor, TeamBColor, teamBScore), index += 1);
if (Config.ScoreLimit > 0)
EMInterface.CreatePanelEntry(scoreContainer, string.Format(GetMessage("Score.Limit", 0UL), Config.ScoreLimit), index += 1);
EMInterface.CreateScoreEntry(scoreContainer, string.Empty, "C", "K", index += 1);
for (int i = 0; i < Mathf.Min(scoreData.Count, 15); i++)
{
EventManager.ScoreEntry score = scoreData[i];
EMInterface.CreateScoreEntry(scoreContainer, $"<color={(score.team == EventManager.Team.A ? TeamAColor : TeamBColor)}>{score.displayName}</color>", ((int)score.value1).ToString(), ((int)score.value2).ToString(), i + index + 1);
}
}
protected override float GetFirstScoreValue(EventManager.BaseEventPlayer eventPlayer) => (eventPlayer as CaptureTheFlagPlayer).FlagCaptures;
protected override float GetSecondScoreValue(EventManager.BaseEventPlayer eventPlayer) => eventPlayer.Kills;
protected override void SortScores(ref List<EventManager.ScoreEntry> list)
{
list.Sort(delegate (EventManager.ScoreEntry a, EventManager.ScoreEntry b)
{
int primaryScore = a.value1.CompareTo(b.value1);
if (primaryScore == 0)
return a.value2.CompareTo(b.value2);
return primaryScore;
});
}
#endregion
}
internal class CaptureTheFlagPlayer : EventManager.BaseEventPlayer
{
public int FlagCaptures { get; set; }
public bool IsCarryingFlag { get; set; }
}
internal class FlagController : MonoBehaviour
{
private Signage primary;
private Signage secondary;
private Transform tr;
private Vector3 basePosition;
private BoxCollider boxCollider;
private uint signImageCRC = 0;
private CaptureTheFlagEvent captureTheFlagEvent;
internal EventManager.Team Team { get; set; }
internal CaptureTheFlagPlayer FlagHolder { get; private set; }
internal bool IsAtBase { get; private set; } = true;
private const string SIGN_PREFAB = "assets/prefabs/deployable/signs/sign.post.single.prefab";
private const float ROTATE_SPEED = 48f;
internal static FlagController Create(CaptureTheFlagEvent captureTheFlagEvent, EventManager.Team team, Vector3 position)
{
Signage signage = Spawn(position);
FlagController flagController = signage.gameObject.AddComponent<FlagController>();
flagController.captureTheFlagEvent = captureTheFlagEvent;
flagController.Team = team;
flagController.basePosition = position;
return flagController;
}
private static Signage Spawn(Vector3 position)
{
Signage signage = GameManager.server.CreateEntity(SIGN_PREFAB, position) as Signage;
signage.enableSaving = false;
signage.Spawn();
Destroy(signage.GetComponent<MeshCollider>());
Destroy(signage.GetComponent<DestroyOnGroundMissing>());
Destroy(signage.GetComponent<GroundWatch>());
return signage;
}
private void Awake()
{
primary = GetComponent<Signage>();
tr = primary.transform;
}
private void Start()
{
secondary = Spawn(tr.position);
secondary.SetParent(primary);
secondary.transform.localPosition = Vector3.zero;
secondary.transform.localRotation = Quaternion.Euler(0f, 180f, 0f);
SetSignImages(primary);
SetSignImages(secondary);
primary.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
secondary.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
primary.gameObject.layer = (int)Rust.Layer.Reserved1;
boxCollider = primary.gameObject.AddComponent<BoxCollider>();
boxCollider.size = new Vector3(1.2f, 2f, 1f);
boxCollider.center = new Vector3(0f, 1.1f, 0f);
boxCollider.isTrigger = true;
}
private void Update()
{
tr.RotateAround(tr.position, tr.up, Time.deltaTime * ROTATE_SPEED);
primary.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
}
private void OnDestroy()
{
Destroy(boxCollider);
if (secondary != null && !secondary.IsDestroyed)
{
secondary.SetParent(null);
secondary.Kill(BaseNetworkable.DestroyMode.None);
}
if (primary != null && !primary.IsDestroyed)
{
primary.SetParent(null);
primary.Kill();
}
}
private void SetSignImages(Signage signage)
{
string hex = Team == EventManager.Team.B ? captureTheFlagEvent.TeamBColor : captureTheFlagEvent.TeamAColor;
if (signImageCRC == 0)
{
hex = hex.TrimStart('#');
int red = int.Parse(hex.Substring(0, 2), NumberStyles.AllowHexSpecifier);
int green = int.Parse(hex.Substring(2, 2), NumberStyles.AllowHexSpecifier);
int blue = int.Parse(hex.Substring(4, 2), NumberStyles.AllowHexSpecifier);
Color color = new Color((float)red / 255f, (float)green / 255f, (float)blue / 255f);
Color[] array = new Color[256 * 256];
for (int i = 0; i < array.Length; i++)
array[i] = color;
Texture2D texture2D = new Texture2D(256, 256);
texture2D.SetPixels(array);
byte[] bytes = texture2D.EncodeToPNG();
Destroy(texture2D);
signImageCRC = FileStorage.server.Store(bytes, FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID);
}
Array.Resize<uint>(ref signage.textureIDs, 1);
signage.textureIDs[0] = signImageCRC;
signage.SetFlag(BaseEntity.Flags.Locked, true);
}
private void OnTriggerEnter(Collider col)
{
if (captureTheFlagEvent.Status != EventManager.EventStatus.Started)
return;
CaptureTheFlagPlayer eventPlayer = col.GetComponent<CaptureTheFlagPlayer>();
if (eventPlayer == null || eventPlayer.IsDead)
return;
if (IsAtBase)
{
if (eventPlayer.Team != Team)
PickupFlag(eventPlayer);
else
{
if (eventPlayer.IsCarryingFlag)
{
FlagController enemyFlag = Team == EventManager.Team.A ? captureTheFlagEvent.TeamBFlag : captureTheFlagEvent.TeamAFlag;
enemyFlag.CaptureFlag(eventPlayer);
}
}
}
else
{
if (FlagHolder == null)
{
if (eventPlayer.Team != Team)
PickupFlag(eventPlayer);
else
{
ResetFlag();
captureTheFlagEvent.BroadcastToPlayers(GetMessage, "Notification.FlagReset", eventPlayer.Team, captureTheFlagEvent.GetTeamColor(eventPlayer.Team));
}
}
}
}
private void PickupFlag(CaptureTheFlagPlayer eventPlayer)
{
FlagHolder = eventPlayer;
eventPlayer.IsCarryingFlag = true;
IsAtBase = false;
InvokeHandler.CancelInvoke(this, DroppedTimeExpired);
primary.SetParent(eventPlayer.Player);
tr.localPosition = new Vector3(0f, 0.25f, -0.75f);
captureTheFlagEvent.BroadcastToPlayers(GetMessage, "Notification.FlagPickedUp", eventPlayer.Player.displayName, Team, captureTheFlagEvent.GetTeamColor(eventPlayer.Team), captureTheFlagEvent.GetTeamColor(Team));
}
internal void DropFlag(bool resetToBase)
{
primary.SetParent(null, true);
if (FlagHolder != null)
{
FlagHolder.IsCarryingFlag = false;
FlagHolder = null;
}
primary.UpdateNetworkGroup();
primary.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
if (resetToBase)
InvokeHandler.Invoke(this, DroppedTimeExpired, captureTheFlagEvent.flagRespawnTime);
}
private void CaptureFlag(CaptureTheFlagPlayer eventPlayer)
{
ResetFlag();
captureTheFlagEvent.OnFlagCaptured(eventPlayer, Team);
}
private void DroppedTimeExpired()
{
captureTheFlagEvent.BroadcastToPlayers(GetMessage, "Notification.FlagReset", Team, captureTheFlagEvent.GetTeamColor(Team));
ResetFlag();
}
private void ResetFlag()
{
if (FlagHolder != null)
{
FlagHolder.IsCarryingFlag = false;
FlagHolder = null;
}
InvokeHandler.CancelInvoke(this, DroppedTimeExpired);
primary.SetParent(null);
tr.position = basePosition;
primary.UpdateNetworkGroup();
primary.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
IsAtBase = true;
}
}
#endregion
#region Config
private static ConfigData Configuration;
private class ConfigData
{
[JsonProperty(PropertyName = "Respawn time (seconds)")]
public int RespawnTime { get; set; }
public Oxide.Core.VersionNumber Version { get; set; }
}
protected override void LoadConfig()
{
base.LoadConfig();
Configuration = Config.ReadObject<ConfigData>();
if (Configuration.Version < Version)
UpdateConfigValues();
Config.WriteObject(Configuration, true);
}
protected override void LoadDefaultConfig() => Configuration = GetBaseConfig();
private ConfigData GetBaseConfig()
{
return new ConfigData
{
RespawnTime = 5,
Version = Version
};
}
protected override void SaveConfig() => Config.WriteObject(Configuration, true);
private void UpdateConfigValues()
{
PrintWarning("Config update detected! Updating config values...");
Configuration.Version = Version;
PrintWarning("Config update completed!");
}
#endregion
#region Localization
public string Message(string key, ulong playerId = 0U) => lang.GetMessage(key, this, playerId != 0U ? playerId.ToString() : null);
private static Func<string, ulong, string> GetMessage;
private readonly Dictionary<string, string> Messages = new Dictionary<string, string>
{
["Score.FlagCaptures"] = "Flag Captures: {0}",
["Score.Kills"] = "Kills: {0}",
["Score.Name"] = "Kills",
["Score.Limit"] = "Score Limit : {0}",
["Score.Team"] = "{0} : <color={1}>Team A</color> | <color={2}>Team B</color> : {3}",
["Notification.FlagPickedUp"] = "<color={2}>{0}</color> has picked up <color={3}>Team {1}</color>'s flag",
["Notification.FlagReset"] = "<color={1}>Team {0}</color>'s flag has been returned to base",
["Notification.FlagCaptured"] = "<color={2}>{0}</color> has captured <color={3}>Team {1}</color>'s flag",
["Notification.FlagDropped"] = "<color={2}>{0}</color> has dropped <color={3}>Team {1}</color>'s flag"
};
#endregion
}
}