C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Oxide.Core;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Oxide.Plugins {
[Info("PoliticalSurvival", "Pho3niX90", "0.9.15")]
[Description("Political Survival - Become the ruler, tax your subjects and keep them in line!")]
class PoliticalSurvival : RustPlugin {
bool firstRun = false;
public bool DebugMode = false;
Ruler ruler;
private PSConfig config;
private Core.Libraries.Time _time = GetLibrary<Core.Libraries.Time>();
static PoliticalSurvival _instance;
private List<Ruler> rulerList = new List<Ruler>();
private Dictionary<ulong, MapMarkerGenericRadius> _mapMarker;
private VendingMachineMapMarker _mapMarkerVending;
#region Settings Class
public class TaxSource {
public bool DispenserGather;
public bool CropGather;
public bool DispenserBonus;
public bool QuarryGather;
public bool ExcavatorGather;
public bool CollectiblePickup;
public bool SurveyGather;
public bool RecyclerScrap;
public TaxSource createDefault() {
DispenserGather = true;
CropGather = true;
DispenserBonus = true;
QuarryGather = true;
ExcavatorGather = true;
CollectiblePickup = true;
SurveyGather = true;
RecyclerScrap = true;
return this;
}
}
public class Ruler {
public Vector3 taxContainerVector3;
public NetworkableId taxContainerID;
public double tax;
public ulong userId;
public string displayName;
public ulong rulerId;
public uint rulerSince;
public int resourcesGot;
public string realm;
public Ruler(Vector3 tcv4, NetworkableId txId, double tx, ulong rlr, string rlrname, string rlm, ulong rid) {
taxContainerVector3 = tcv4;
taxContainerID = txId;
tax = tx;
userId = rlr;
displayName = rlrname;
realm = rlm;
rulerId = rid;
}
public Ruler() {
}
public int GetResourceCount() {
return resourcesGot;
}
public Ruler SetRulerSince(uint since) {
rulerSince = since;
return this;
}
public Ruler SetResourcesGot(int amnt) {
resourcesGot = amnt;
return this;
}
public long GetRulerSince() {
return rulerSince;
}
public Ruler SetTaxContainerVector3(Vector3 vec) {
taxContainerVector3 = vec;
return this;
}
public Vector3 GetTaxContainerVector3() {
return taxContainerVector3;
}
public Ruler SetTaxContainerID(NetworkableId storage) {
taxContainerID = storage;
return this;
}
public NetworkableId GetTaxContainerID() {
return taxContainerID;
}
public Ruler SetTaxLevel(double tx) {
tax = tx;
return this;
}
public double GetTaxLevel() {
return tax;
}
public Ruler SetRuler(ulong rlr) {
userId = rlr;
rulerId = rlr;
rulerSince = (new Core.Libraries.Time()).GetUnixTimestamp();
return this;
}
public ulong GetRuler() {
return userId;
}
public double GetRuleLengthInMinutes() {
return (new Core.Libraries.Time().GetUnixTimestamp() - rulerSince) / 60.0;
}
public double GetRulerOfflineMinutes() {
return _instance.rulerOfflineAt == 0 ? 0.0 : ((new Core.Libraries.Time().GetUnixTimestamp() - _instance.rulerOfflineAt) / 60.0);
}
public Ruler SetRulerName(string name) {
displayName = name;
return this;
}
public string GetRulerName() {
return displayName;
}
public Ruler SetRealmName(string rlm) {
realm = rlm;
return this;
}
public string GetRealmName() {
return realm;
}
}
#endregion
#region Components
#region Heli Vars
public int HeliLifeTimeMinutes = 5;
public float HeliBaseHealth = 50000.0f;
public float HeliSpeed = 50f;
public float HeliSpeedMax = 200f;
public int NumRockets = 50;
public float ScanFrequencySeconds = 5;
public float TargetVisible = 1000;
public float MaxTargetRange = 300;
public bool NotifyPlayers = true;
public BasePlayer target;
#endregion
class HeliComponent : FacepunchBehaviour {
private BaseHelicopter heli;
private PatrolHelicopterAI AI;
private bool isFlying = true;
private bool isRetiring = false;
float timer;
float timerAdd;
void Awake() {
heli = GetComponent<BaseHelicopter>();
AI = heli.GetComponent<PatrolHelicopterAI>();
heli.startHealth = _instance.HeliBaseHealth;
AI.maxSpeed = Mathf.Clamp(_instance.HeliSpeed, 0.1f, _instance.HeliSpeedMax);
AI.numRocketsLeft = _instance.NumRockets;
attachGuns(AI);
timerAdd = (Time.realtimeSinceStartup + Convert.ToSingle(_instance.HeliLifeTimeMinutes * 60));
InvokeRepeating("ScanForTargets", _instance.ScanFrequencySeconds, _instance.ScanFrequencySeconds);
}
void FixedUpdate() {
timer = Time.realtimeSinceStartup;
if (timer >= timerAdd && !isRetiring) {
isRetiring = true;
}
if (isRetiring && isFlying) {
CancelInvoke("ScanForTargets");
isFlying = false;
heliRetire();
}
}
internal void ScanForTargets() {
foreach (ulong targetSteamId in _instance.target.Team.members) {
BasePlayer teamMemberToAttack = BasePlayer.Find(targetSteamId.ToString());
if (teamMemberToAttack.IsConnected) {
UpdateTargets(teamMemberToAttack);
_instance.DebugLog("Heli target found " + teamMemberToAttack);
}
UpdateAi();
}
}
void UpdateAi() {
_instance.DebugLog("Heli updating AI");
AI.UpdateTargetList();
AI.MoveToDestination();
AI.UpdateRotation();
AI.UpdateSpotlight();
AI.AIThink();
AI.DoMachineGuns();
}
void UpdateTargets(BasePlayer Player) {
AI._targetList.Add(new PatrolHelicopterAI.targetinfo((BaseEntity)Player, Player));
}
internal void attachGuns(PatrolHelicopterAI helicopter) {
if (helicopter == null) return;
var guns = new List<HelicopterTurret>();
guns.Add(helicopter.leftGun);
guns.Add(helicopter.rightGun);
for (int i = 0; i < guns.Count; i++) {
// Leave these as hardcoded for now
var turret = guns[i];
turret.fireRate = 0.125f;
turret.timeBetweenBursts = 3f;
turret.burstLength = 3f;
turret.maxTargetRange = _instance.MaxTargetRange;
}
}
internal void heliRetire() {
AI.Retire();
}
public void UnloadComponent() {
Destroy(this);
}
void OnDestroy() {
CancelInvoke("ScanForTargets");
}
}
#endregion
#region Variables
Dictionary<string, string> serverMessages;
int worldSize = ConVar.Server.worldsize;
BasePlayer currentRuler;
uint rulerOfflineAt = 0;
private ILocator liveLocator = null;
private ILocator locator = null;
private bool Changed = false;
protected Dictionary<string, Timer> Timers {
get;
}
= new Dictionary<string, Timer>();
#endregion
void Init() {
config = new PSConfig(this);
_mapMarker = new Dictionary<ulong, MapMarkerGenericRadius>();
}
private void Loaded() {
LoadServerMessages();
LoadRuler();
_instance = this;
Puts("Political Survival is starting...");
if (ConVar.Server.worldsize == 0) Puts("WARNING: worldsize is reporting as 0, this is not possible and will default to config size.Please make sure the config has the correct size.");
if (ConVar.Server.worldsize > 0) {
worldSize = ConVar.Server.worldsize;
config.worldSize = ConVar.Server.worldsize;
}
liveLocator = new RustIOLocator(worldSize);
locator = new LocatorWithDelay(liveLocator, 60);
if (ruler.GetRulerSince() == 0) {
ruler.SetRulerSince(_time.GetUnixTimestamp());
}
Puts("Realm name is " + ruler.GetRealmName());
Puts("Tax level is " + ruler.GetTaxLevel());
Puts("TaxChest is set " + !ruler.GetTaxContainerVector3().Equals(Vector3.negativeInfinity));
Puts("Political Survival: Started");
currentRuler = GetPlayer(ruler.GetRuler().ToString());
Puts("Current ruler " + (currentRuler != null ? "is set" : "is null"));
if (currentRuler != null) Puts("Ruler is " + ruler.GetRuler() + " (" + currentRuler.displayName + ")");
Timers.Add("AdviseRulerPosition", timer.Repeat(Math.Max(config.broadcastRulerPositionEvery, 60), 0, () => AdviseRulerPosition()));
SaveRuler();
Puts($"Ruler offline at {rulerOfflineAt}");
timer.Once(300, () => {
if (rulerOfflineAt != 0 || currentRuler == null || currentRuler.IsConnected) {
if (config.chooseNewRulerOnDisconnect && (ruler.GetRulerOfflineMinutes() >= (1 * config.chooseNewRulerOnDisconnectMinutes) || (rulerOfflineAt == 0 && (currentRuler == null || !currentRuler.IsConnected)))) {
TryForceNewRuler(true);
}
}
}
);
RemoveMarkersForce();
}
void Unload() {
SaveRuler();
RemoveMarkers();
foreach (Timer t in Timers.Values) t.Destroy();
Timers.Clear();
}
void OnPlayerConnected(BasePlayer player) {
if (config.showWelcomeMsg) PrintToChat(player.displayName + " " + GetMsg("PlayerConnected") + " " + ruler.GetRealmName());
if (currentRuler != null && ruler.userId == currentRuler.userID) {
rulerOfflineAt = 0;
}
}
void OnPlayerDisconnected(BasePlayer player, string reason) {
if (config.showWelcomeMsg) PrintToChat(player.displayName + " " + GetMsg("PlayerDisconnected") + " " + ruler.GetRealmName());
if (currentRuler != null && player.userID == currentRuler.userID) {
rulerOfflineAt = _time.GetUnixTimestamp();
timer.Once(60 * config.chooseNewRulerOnDisconnectMinutes, () => {
if (rulerOfflineAt != 0) TryForceNewRuler(true);
}
);
}
}
#region GatheringHooks
void OnDispenserGather(ResourceDispenser dispenser, BaseEntity entity, Item item) {
DebugLog("OnDispenserGather start");
//if (!config.taxSource.DispenserGather || dispenser == null || entity == null || Item == null || ruler.GetTaxContainerID() == 0) return;
BasePlayer player = entity as BasePlayer;
if (player != null) {
DebugLog("OnDispenserGather stage 2 " + item.flags.ToString() + " " + item.amount + " " + player.displayName);
AddToTaxContainer(item, player.displayName, out item.amount);
}
}
private void OnDispenserBonus(ResourceDispenser dispenser, BaseEntity entity, Item item) {
//if (!config.taxSource.DispenserBonus) return;
BasePlayer player = entity as BasePlayer;
DebugLog("OnDispenserBonus start");
AddToTaxContainer(item, player.displayName, out item.amount);
}
void OnGrowableGather(GrowableEntity plant, Item item, BasePlayer player) {
//if (!config.taxSource.CropGather) return;
DebugLog("OnPlantGather start");
AddToTaxContainer(item, player.displayName, out item.amount);
}
private void OnQuarryGather(MiningQuarry quarry, Item item) {
DebugLog("OnQuarryGather start");
//if (!config.taxSource.QuarryGather) return;
AddToTaxContainer(item, quarry.name, out item.amount);
}
private void OnExcavatorGather(ExcavatorArm excavator, Item item) {
DebugLog("OnExcavatorGather start");
//if (!config.taxSource.ExcavatorGather) return;
AddToTaxContainer(item, excavator.name, out item.amount);
}
private void OnCollectiblePickup(Item item, BasePlayer player) {
DebugLog("OnCollectiblePickup start");
//if (!config.taxSource.CollectiblePickup) return;
AddToTaxContainer(item, player.displayName, out item.amount);
}
private void OnSurveyGather(SurveyCharge surveyCharge, Item item) {
DebugLog("OnSurveyGather start");
//if (!config.taxSource.SurveyGather) return;
AddToTaxContainer(item, surveyCharge.name, out item.amount);
}
private int OnRecycleItemOutput(string itemName, int itemAmount) {
if (itemAmount <= 1 || !IsChestSet()) return itemAmount;
Item item = ItemManager.CreateByName(itemName, itemAmount);
DebugLog("OnRecycleItemOutput start");
AddToTaxContainer(item, null, out item.amount);
return item.amount;
}
#endregion
void AddToTaxContainer(Item item, string displayName, out int netAmount) {
try {
if (!IsChestSet() || item == null || ruler.GetTaxContainerID().IsValid || ruler.GetRuler() == 0 || ruler.GetTaxLevel() == 0 || ruler.GetTaxContainerVector3() == Vector3.negativeInfinity) {
netAmount = item.amount;
return;
}
ItemDefinition ToAdd = ItemManager.FindItemDefinition(item.info.itemid);
int Tax = Convert.ToInt32(Math.Ceiling((item.amount * ruler.GetTaxLevel()) / 100));
ItemContainer container = FindStorageContainer(ruler.GetTaxContainerID()).inventory;
if (ToAdd != null && container != null) {
if (item.CanMoveTo(container)) {
container.AddItem(ToAdd, Tax);
ruler.resourcesGot += Tax;
}
}
DebugLog("User " + displayName + " gathered " + item.amount + " x " + item.info.shortname + ", and " + Tax + " was taxed");
DebugLog("items added to tax container");
netAmount = item.amount - Tax;
}
catch (Exception e) {
netAmount = item.amount;
}
}
void OnEntityDeath(BaseCombatEntity entity, HitInfo info) {
if (entity == null) return;
BasePlayer player = entity as BasePlayer;
if (player != null) {
if (IsRuler(player.userID)) {
BasePlayer killer = null;
if (info != null) killer = info.Initiator as BasePlayer;
if (killer != null && killer.userID != player.userID && !(killer is NPCPlayer)) {
RemoveMarkers();
SetRuler(killer);
PrintToChat(string.Format(GetMsg("RulerMurdered"), killer.displayName));
}
else {
ruler.SetRuler(0).SetRulerName(null);
currentRuler = null;
PrintToChat(string.Format(GetMsg("RulerDied")));
if (TryForceNewRuler(true)) PrintToChat(GetMsg("NewRuler"), currentRuler.displayName);
}
SaveRuler();
}
}
}
public void TryForceRuler() {
if ((currentRuler == null && TryForceNewRuler(false)) || ruler.GetRulerOfflineMinutes() >= config.chooseNewRulerOnDisconnectMinutes) {
RemoveMarkers();
PrintToChat(GetMsg("NewRuler"), currentRuler.displayName);
}
}
public bool TryForceNewRuler(bool force) {
if (currentRuler != null && !force) return false;
BasePlayer player = GetRandomPlayer();
if (player != null) {
RemoveMarkers();
SetRuler(player);
PrintToChat(GetMsg("NewRuler"), currentRuler.displayName);
return true;
}
return false;
}
#region Commands
[ChatCommand("fnr")]
void TryForceRulerCmd(BasePlayer player, string command, string[] args) {
if (!player.IsAdmin) {
Puts($"Player {player.displayName} tried using fnr");
return;
}
if (args.Length != 1) {
if (TryForceNewRuler(true)) {
PrintToChat(GetMsg("NewRuler"), currentRuler.displayName);
}
else {
PrintToChat(GetMsg("ForceRullerErr"));
}
}
else if (args.Length == 1) {
BasePlayer ruler = null;
try {
ruler = BasePlayer.Find(args[0]);
}
catch (Exception e) {
if (player != null) PrintToChat(player, "ERR: " + GetMsg("PlayerNotFound"), args[0]);
return;
}
if (ruler == null) {
PrintToChat(GetMsg("PlayerNotFound"), args[0]);
return;
}
SetRuler(ruler);
PrintToChat(GetMsg("NewRuler"), currentRuler.displayName);
}
}
[ConsoleCommand("forcenewruler")]
void TryForceNewRulerConsoleCommand(BasePlayer player, string command, string[] args) {
TryForceRulerCmd(player, command, args);
}
[ChatCommand("heli")]
void HeliCommmand(BasePlayer player, string command, string[] args) {
if (!IsRuler(player.userID)) {
PrintToChat(player, "You aren't the ruler");
return;
}
if (args.Length != 1) {
PrintToChat(player, "Usage '/heli player' where player can also be partial name");
return;
}
BasePlayer playerToAttack = GetPlayer(args[0]);
if (playerToAttack == null) {
PrintToChat(player, GetMsg("PlayerNotFound"), args[0]);
return;
}
Puts("Can afford heli?");
if (!CanAffordheliStrike(player)) {
PrintToChat(player, GetMsg("OrderingHeliCost"), config.heliItemCostQty, ItemManager.FindItemDefinition(config.heliItemCost).displayName.english);
return;
}
int heliCount = UnityEngine.Object.FindObjectsOfType<BaseHelicopter>().Count();
if (heliCount >= config.maxHelis) {
PrintToChat(player, GetMsg("NomoreAirspace"), config.maxHelis);
return;
}
Puts("OrderheliStrike");
OrderheliStrike(playerToAttack);
PrintToChat(player, GetMsg("HeliInbound"));
}
[ChatCommand("taxrange")]
void AdmSetTaxChestCommand(BasePlayer player, string command, string[] args) {
if (player.IsAdmin && args.Length == 2) {
int taxMin = 0;
int taxMax = 10;
int.TryParse(args[0], out taxMin);
int.TryParse(args[1], out taxMax);
config.taxMin = taxMin;
config.taxMax = taxMax;
PrintToChat(player, $"Tax range set to Min:{config.taxMin}% - Max:{config.taxMax}%");
SaveConfig();
SaveRuler();
}
}
[ChatCommand("settaxchest")]
void SetTaxChestCommand(BasePlayer player, string command, string[] arguments) {
if (!IsRuler(player.userID)) {
SendReply(player, GetMsg("RulerError"), player.UserIDString);
return;
}
var layers = LayerMask.GetMask("Deployed");
RaycastHit hit = new RaycastHit();
if (Player != null && Physics.Raycast(player.eyes.HeadRay(), out hit, 50, layers)) {
BaseEntity entity = hit.GetEntity();
if (entity != null && (entity.ShortPrefabName.Contains("box.wooden") || entity.ShortPrefabName.Contains("cupboard.tool.deployed") || entity.ShortPrefabName.Contains("vending.machine"))) {
Vector3 boxPosition = entity.transform.position;
StorageContainer boxStorage = FindStorageContainer(boxPosition);
if (boxStorage != null) {
ruler.SetTaxContainerVector3(boxPosition).SetTaxContainerID(entity.net.ID);
if (entity.ShortPrefabName.Contains("box.wooden")) {
entity.skinID = config.taxBoxSkinId;
//https://steamcommunity.com/sharedfiles/filedetails/?id=1482844040&searchtext=
entity.SendNetworkUpdate();
}
DebugLog("Chest set");
SaveRuler();
SendReply(player, GetMsg("SetNewTaxChest"), player.UserIDString);
}
}
else {
DebugLog("Looking at " + entity.ShortPrefabName);
SendReply(player, GetMsg("SetNewTaxChestNotFound"), player.UserIDString);
SendReply(player, GetMsg("SettingNewTaxChest"), player.UserIDString);
}
}
else {
SendReply(player, GetMsg("SetNewTaxChestNotFound"), player.UserIDString);
SendReply(player, GetMsg("SettingNewTaxChest"), player.UserIDString);
}
}
[ChatCommand("tax")]
void InfoCommand2(BasePlayer player, string command, string[] arguments) {
InfoCommand(player, command, arguments);
}
[ChatCommand("rinfo")]
void InfoCommand(BasePlayer player, string command, string[] arguments) {
string RulerName = string.Empty;
if (ruler.GetRuler() > 0) {
BasePlayer BaseRuler = BasePlayer.FindAwakeOrSleeping(ruler.GetRuler().ToString());
RulerName = BaseRuler != null ? BaseRuler.displayName : GetMsg("ClaimRuler", player.UserIDString);
}
else {
RulerName = GetMsg("ClaimRuler", player.UserIDString);
}
if (ruler.GetRuler() != 0) {
SendReply(player, GetMsg("RulerName"), ruler.GetRulerName());
}
else {
SendReply(player, GetMsg("ClaimRuler", player.UserIDString));
}
SendReply(player, GetMsg("InfoRealmName", player.UserIDString), ruler.GetRealmName());
SendReply(player, GetMsg("InfoTaxLevel", player.UserIDString), ruler.GetTaxLevel() + "%" + ((!IsChestSet()) ? " (0%, chest not set)" : ""));
SendReply(player, GetMsg("InfoRuleLength", player.UserIDString), Math.Round(ruler.GetRuleLengthInMinutes()) + " minutes");
SendReply(player, GetMsg("InfoResources", player.UserIDString), ruler.GetResourceCount());
if (IsRuler(player.userID)) {
SendReply(player, GetMsg("SettingNewTaxChest", player.UserIDString));
SendReply(player, GetMsg("InfoTaxCmd", player.UserIDString), config.taxMin, config.taxMax + ": " + ruler.GetTaxLevel() + "%");
}
}
[ChatCommand("claimruler")]
void ClaimRuler(BasePlayer player, string command, string[] arguments) {
if (currentRuler == null) {
PrintToChat(GetMsg("IsNowRuler"), player.displayName);
SetRuler(player);
}
}
[ChatCommand("settax")]
void SetTaxCommand(BasePlayer player, string command, string[] args) {
if (IsRuler(player.userID)) {
int newTaxLevel = 0;
if (int.TryParse(args[0], out newTaxLevel)) {
double oldTax = ruler.GetTaxLevel();
if (newTaxLevel == ruler.GetTaxLevel()) return;
Puts("Tax have been changed by " + player.displayName + " from " + ruler.GetTaxLevel() + " to " + newTaxLevel);
Puts($"Tax {config.taxMin} {config.taxMax}");
if (newTaxLevel > config.taxMax) {
SendReply(player, GetMsg("MaxTax"), config.taxMax);
newTaxLevel = config.taxMax;
}
else if (newTaxLevel < config.taxMin) {
SendReply(player, GetMsg("MinTax"), config.taxMin);
newTaxLevel = config.taxMin;
}
SetTaxLevel(newTaxLevel);
PrintToChat(GetMsg("UpdateTaxMessage"), oldTax, newTaxLevel);
}
}
else {
SendReply(player, GetMsg("RulerError", player.UserIDString));
}
}
//TODO ended here with case renaming to camelCase
[ChatCommand("realmname")]
void RealmNameCommand(BasePlayer player, string command, string[] arguments) {
if (IsRuler(player.userID)) {
string NewName = MergeParams(0, arguments);
if (!String.IsNullOrEmpty(NewName)) {
SetRealmName(NewName);
}
}
else SendReply(player, GetMsg("RulerError", player.UserIDString));
}
[ChatCommand("rplayers")]
void PlayersCommand(BasePlayer player, string command, string[] arguments) {
StringBuilder builder = new StringBuilder();
int playerCount = BasePlayer.activePlayerList.Count;
builder.Append(string.Format(GetMsg("OnlinePlayers"), playerCount) + " ");
List<string> players = new List<string>();
foreach (BasePlayer pl in BasePlayer.activePlayerList) {
players.Add("<color=#ff0000ff>" + pl.displayName + "</color>");
}
builder.Append(String.Join(", ", players));
SendReply(player, builder.ToString());
}
#endregion
bool IsPlayerOnline(string partialNameOrID) {
return GetPlayer(partialNameOrID).IsConnected;
}
bool IsChestSet() {
return ruler.taxContainerID.IsValid;
}
BasePlayer GetPlayer(string partialNameOrID) {
return BasePlayer.Find(partialNameOrID);
}
string MergeParams(int start, string[] paramz) {
var merged = new StringBuilder();
for (int i = start; i < paramz.Length; i++) {
if (i > start) merged.Append(" ");
merged.Append(paramz[i]);
}
return merged.ToString();
}
bool IsRuler(ulong steamId) {
return currentRuler != null && currentRuler.userID == steamId;
}
public bool CanAffordheliStrike(BasePlayer player) {
Item item = player.inventory.FindItemByItemID(config.heliItemCost);
return item != null ? item.amount >= config.heliItemCostQty : false;
}
public void OrderheliStrike(BasePlayer playerToAttack) {
// Deduct the cost
if (currentRuler == null) currentRuler = GetPlayer(ruler.GetRuler().ToString());
List<Item> collector = new List<Item>();
Item item = currentRuler.inventory.FindItemByItemID(config.heliItemCost);
if (item != null) {
currentRuler.inventory.Take(collector, item.info.itemid, config.heliItemCostQty);
Puts("Spawn the birdie");
//spawn the birdie
BaseHelicopter ent = GameManager.server.CreateEntity("assets/prefabs/npc/patrol helicopter/patrolhelicopter.prefab", new Vector3(), new Quaternion(), true) as BaseHelicopter;
if (ent != null && playerToAttack != null) {
target = playerToAttack;
ent.GetComponent<PatrolHelicopterAI>().SetInitialDestination(playerToAttack.transform.position + new Vector3(0.0f, 10f, 0.0f), 0.25f);
ent.Spawn();
ent.gameObject.AddComponent<HeliComponent>();
timer.Once(HeliLifeTimeMinutes * 60, () => ent.GetComponent<HeliComponent>().heliRetire());
}
}
else {
SendReply(currentRuler, "Something went wrong");
}
}
void SetRuler(BasePlayer bpruler) {
Puts("New Ruler! " + bpruler.displayName);
ruler.SetRuler(bpruler.userID).SetRulerName(bpruler.displayName).SetTaxContainerID(new NetworkableId()).SetTaxLevel(config.taxMin).SetTaxContainerVector3(Vector3.negativeInfinity).SetRealmName(GetMsg("DefaultRealm")).SetRulerSince(_time.GetUnixTimestamp()).SetResourcesGot(0);
currentRuler = bpruler;
SaveRuler();
}
void SetTaxLevel(double newTaxLevel) {
ruler.SetTaxLevel(newTaxLevel);
SaveRuler();
}
void SetRealmName(string newName) {
if (newName.Length > 36) newName = newName.Substring(0, 36);
PrintToChat(string.Format(GetMsg("RealmRenamed"), newName));
ruler.SetRealmName(newName);
SaveRuler();
}
StorageContainer FindStorageContainer(Vector3 position) {
foreach (StorageContainer cont in StorageContainer.FindObjectsOfType<StorageContainer>()) {
Vector3 ContPosition = cont.transform.position;
if (ContPosition == position) {
Puts("Tax Container instance found: " + cont.GetEntity().GetInstanceID());
ruler.SetTaxContainerID(cont.net.ID);
return cont;
}
}
return null;
}
StorageContainer FindStorageContainer(NetworkableId netid) {
return (StorageContainer)BaseNetworkable.serverEntities.Find(netid);
}
#region Player Grid Coordinates and Locators
public interface ILocator {
string GridReference(Vector3 component, out bool moved);
}
public class RustIOLocator : ILocator {
public RustIOLocator(int worldSize) {
worldSize = (worldSize != 0) ? worldSize : (ConVar.Server.worldsize > 0) ? ConVar.Server.worldsize : _instance.config.worldSize;
translate = worldSize / 2f;
//offset
gridWidth = (worldSize * 0.0066666666666667f);
scale = worldSize / gridWidth;
}
private readonly float translate;
private readonly float scale;
private readonly float gridWidth;
public string GridReference(Vector3 pos, out bool moved) {
float x = pos.x + translate;
float z = pos.z + translate;
int lat = (int)Math.Floor(x / scale);
//letter
char latChar = (char)('A' + lat);
int lon = (int)Math.Round(gridWidth) - (int)Math.Floor(z / scale);
//number
moved = false;
// We dont know, so just return false
return string.Format("{0}{1}", latChar, lon);
}
}
public class LocatorWithDelay : ILocator {
public LocatorWithDelay(ILocator liveLocator, int updateInterval) {
this.liveLocator = liveLocator;
this.updateInterval = updateInterval;
}
private readonly ILocator liveLocator;
private readonly int updateInterval;
private readonly Dictionary<Vector3, ExpiringCoordinates> locations = new Dictionary<Vector3, ExpiringCoordinates>();
public string GridReference(Vector3 pos, out bool moved) {
ExpiringCoordinates item = null;
bool m;
if (locations.ContainsKey(pos)) {
item = locations[pos];
if (item.Expires < DateTime.Now) {
string location = liveLocator.GridReference(pos, out m);
item.GridChanged = item.Location != location;
item.Location = location;
item.Expires = DateTime.Now.AddSeconds(updateInterval);
}
}
else {
item = new ExpiringCoordinates();
item.Location = liveLocator.GridReference(pos, out m);
item.GridChanged = true;
item.Expires = DateTime.Now.AddSeconds(updateInterval);
locations.Add(pos, item);
}
moved = item.GridChanged;
return item.Location;
}
class ExpiringCoordinates {
public string Location {
get;
set;
}
public bool GridChanged {
get;
set;
}
public DateTime Expires {
get;
set;
}
}
}
#endregion
#region Misc
private MonumentInfo FindMonument(Vector3 pos) {
MonumentInfo monumentClosest;
foreach (var monument in TerrainMeta.Path.Monuments) {
if (monument.name.Contains("oil", CompareOptions.IgnoreCase) || monument.name.Contains("cargo", CompareOptions.IgnoreCase)) {
float dist = Vector3.Distance(monument.transform.position, pos);
if (dist <= 80) {
monumentClosest = monument;
return monumentClosest;
}
}
else {
continue;
}
}
return null;
}
#endregion
#region Timers and Events
void UpdateMarker(BasePlayer ruler) {
if (ruler != null) {
VendingMachineMapMarker marker = GameManager.server.CreateEntity("assets/prefabs/deployable/vendingmachine/vending_mapmarker.prefab", ruler.transform.position).GetComponent<VendingMachineMapMarker>();
marker.markerShopName = "The ruler was last spotted here";
marker.OwnerID = ruler.userID;
_mapMarkerVending = marker;
marker.Spawn();
foreach (BasePlayer player in BasePlayer.activePlayerList) {
MapMarkerGenericRadius marker2 = GameManager.server.CreateEntity("assets/prefabs/tools/map/genericradiusmarker.prefab", ruler.transform.position).GetComponent<MapMarkerGenericRadius>();
marker2.alpha = 0.8f;
marker2.color1 = Color.red;
marker2.color2 = Color.green;
marker2.OwnerID = player.userID;
marker2.radius = 0.3f;
marker2.enabled = true;
marker2.Spawn();
marker2.SendUpdate();
if (!_mapMarker.ContainsKey(player.userID)) _mapMarker.Add(player.userID, marker2);
}
}
}
void RemoveMarkers() {
foreach (MapMarkerGenericRadius marker in _mapMarker.Values) {
marker.Kill();
}
if (_mapMarkerVending != null) _mapMarkerVending.Kill();
_mapMarker.Clear();
_mapMarkerVending = null;
}
void RemoveMarkersForce() {
MapMarkerGenericRadius[] markers = GameObject.FindObjectsOfType<MapMarkerGenericRadius>();
foreach (MapMarkerGenericRadius marker in markers) {
marker.Kill();
}
VendingMachineMapMarker[] markers2 = GameObject.FindObjectsOfType<VendingMachineMapMarker>();
foreach (VendingMachineMapMarker marker in markers2) {
if (marker.markerShopName.Equals("The ruler was last spotted here")) {
marker.Kill();
}
}
}
void AdviseRulerPosition() {
try {
if (currentRuler != null && currentRuler.IsConnected) {
if (config.reasignOnAfk) {
double afkMinutes = currentRuler.IdleTime / 60d;
Puts("AFK: " + afkMinutes);
if (afkMinutes >= (config.reasignAfterMinutes - 2) && afkMinutes < config.reasignAfterMinutes) {
SendReply(currentRuler, $"You have been afk for {afkMinutes} minutes, new ruler will be chosen in a minute if you do not move");
}
if (afkMinutes >= 5) {
PrintToChat("A new ruler was chosen, as the previous ruler was AFK for 5mins");
TryForceNewRuler(true);
}
}
if ((config.broadcastRulerPosition || (config.broadcastRulerPositionAfterPercentage > 0 && ruler.GetTaxLevel() > config.broadcastRulerPositionAfterPercentage))) {
bool moved;
if (currentRuler == null) return;
string rulerMonument = FindMonument(currentRuler.transform.position)?.displayPhrase.english;
string rulerGrid = locator.GridReference(currentRuler.transform.position, out moved);
string rulerCoords = rulerMonument != null && rulerMonument.Length > 0 ? rulerMonument : rulerGrid;
if (moved) {
RemoveMarkers();
UpdateMarker(currentRuler);
PrintToChat(GetMsg("RulerLocation_Moved"), currentRuler.displayName, rulerCoords);
}
else {
PrintToChat(GetMsg("RulerLocation_Static"), currentRuler.displayName, rulerCoords);
}
}
else {
}
if (config.chooseNewRulerOnDisconnect && (currentRuler == null && BasePlayer.activePlayerList.Count > 0)) {
timer.Once((60 * config.chooseNewRulerOnDisconnectMinutes) - 5, () => TryForceRuler());
}
}
else {
if (BasePlayer.activePlayerList.Count > 0) {
PrintToChat(GetMsg("RulerOffline"));
TryForceNewRuler(true);
}
}
}
catch (Exception e) {
}
}
BasePlayer GetRandomPlayer() {
ListHashSet<BasePlayer> players = BasePlayer.activePlayerList;
int activePlayers = players.Count;
if (activePlayers > 1) {
return players[Core.Random.Range(0, activePlayers - 1)];
}
return activePlayers == 1 ? players.First() : null;
}
#endregion
void SaveRuler() {
Interface.Oxide.DataFileSystem.WriteObject<Ruler>("PoliticalSurvival", ruler, true);
}
void LoadRuler() {
if (Interface.Oxide.DataFileSystem.ExistsDatafile("PoliticalSurvival")) {
ruler = Interface.Oxide.DataFileSystem.ReadObject<Ruler>("PoliticalSurvival");
Puts("ruler loaded");
}
else {
Puts("Settings doesn't exist, creating default");
ruler = new Ruler().SetRuler(0).SetRealmName(GetMsg("DefaultRealm")).SetTaxLevel(0.0).SetTaxContainerID(new NetworkableId()).SetResourcesGot(0).SetTaxContainerVector3(Vector3.negativeInfinity);
SaveRuler();
}
}
void DebugLog(string msg) {
if (DebugMode) Puts(msg);
}
string GetMsg(string msg, string userId = null) => userId != null ? lang.GetMessage(msg, this, userId) : lang.GetMessage(msg, this);
void LoadServerMessages() {
serverMessages = new Dictionary<string, string>();
serverMessages.Add("StartingInformation", "<color=yellow>Welcome to {0}</color>.If you are new, we run a custom plugin where you can become the server Ruler, tax players, and control the economy.Type <color=#008080ff>/rinfo</color> for more information.");
serverMessages.Add("PlayerConnected", "has connected to");
serverMessages.Add("PlayerDisconnected", "has disconnected from");
serverMessages.Add("RulerDied", "<color=#ff0000ff>The Ruler has died!</color>");
serverMessages.Add("RulerMurdered", "<color=#ff0000ff>The Ruler has been murdered by {0}, who is now the new Ruler.</color>");
serverMessages.Add("RealmRenamed", "The realm has been renamed to <color=#008080ff>{0}</color>");
serverMessages.Add("DefaultRealm", "Land of the cursed");
serverMessages.Add("OnlinePlayers", "Online players ({0}):");
serverMessages.Add("PrivateError", "is either offline or you typed the name wrong.");
serverMessages.Add("PrivateFrom", "PM from");
serverMessages.Add("PrivateTo", "PM sent to");
serverMessages.Add("RulerError", "You need to be the Ruler to do that!");
serverMessages.Add("SettingNewTaxChest", "Look at a Wooden box or TC and type <color=#008080ff>/settaxchest</color>");
serverMessages.Add("SetNewTaxChestNotFound", "You must look at a wooden box or TC to set tax chest");
serverMessages.Add("SetNewTaxChest", "You have set the new tax chest.");
serverMessages.Add("ClaimRuler", "There is no ruler! <color=#008080ff>/claimruler</color> to become the new Ruler!");
serverMessages.Add("IsNowRuler", "<color=#008080ff><b>{0}</b></color> is now the Ruler!");
serverMessages.Add("InfoRuler", "Ruler");
serverMessages.Add("InfoRealmName", "<color=#008080ff>Realm Name</color> {0}");
serverMessages.Add("InfoTaxLevel", "<color=#008080ff>Tax level</color> {0}");
serverMessages.Add("InfoRuleLength", "<color=#008080ff>Rule Length</color> {0}");
serverMessages.Add("InfoResources", "<color=#008080ff>Resources Received</color> {0}");
serverMessages.Add("InfoTaxCmd", "Use <color=#008080ff>/settax {0}-{1}</color> to set tax level");
serverMessages.Add("RulerLocation_Moved", "Ruler <color=#ff0000ff>{0}</color> is on the move, now at <color=#ff0000ff>{1}</color>.");
serverMessages.Add("RulerLocation_Static", "Ruler <color=#ff0000ff>{0}</color> is camping out at <color=#ff0000ff>{1}</color>");
serverMessages.Add("UpdateTaxMessage", "The ruler has changed the tax from <color=#ff0000ff>{0}%</color> to <color=#ff0000ff>{1}%</color>");
serverMessages.Add("PlayerNotFound", "player \"{0}\" not found, or ambiguous");
serverMessages.Add("NewRuler", "<color=#008080ff>{0}</color> has been made the new Ruler.Kill him!");
serverMessages.Add("ForceRullerErr", "Couldn't force a new ruler :(");
serverMessages.Add("MinTax", "Min allowed tax is {0}");
serverMessages.Add("MaxTax", "Max allowed tax is {0}");
serverMessages.Add("RulerName", "<color=#008080ff>Ruler: </color> {0}");
serverMessages.Add("NomoreAirspace", "Insufficient airspace for more than {0} helicopters, please wait for existing patrols to complete");
serverMessages.Add("OrderingHeliCost", "Ordering a heli strike costs {0} {1}");
serverMessages.Add("HeliInbound", "The heli is inbound");
serverMessages.Add("RulerOffline", "Ruler went offline!");
lang.RegisterMessages(serverMessages, this);
}
#region Config
private class PSConfig {
// Config default vars
public bool Debug = false;
public bool showWelcomeMsg = false;
public int maxHelis = 2;
public string heliItemCost = "metal.refined";
public int heliItemCostQty = 500;
public bool broadcastRulerPosition = true;
public int broadcastRulerPositionEvery = 60;
public int broadcastRulerPositionAfterPercentage = 10;
public bool chooseNewRulerOnDisconnect = true;
public int chooseNewRulerOnDisconnectMinutes = 5;
public int taxMin = 0;
public int taxMax = 15;
//public TaxSource taxSource = new TaxSource().createDefault();
public ulong taxBoxSkinId = 1482844040;
public int reasignAfterMinutes = 5;
public bool reasignOnAfk = true;
public int worldSize = ConVar.Server.worldsize;
// Plugin reference
private PoliticalSurvival plugin;
public PSConfig(PoliticalSurvival plugin) {
this.plugin = plugin;
/**
* Load all saved config values
* */
GetConfig(ref Debug, "Debug: Show additional debug console logs");
GetConfig(ref showWelcomeMsg, "Show connect
// ... [Code preview truncated due to length. Full source in attached file] ...