C#:
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 System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
/* 1.0.1
* Fixed an issue with tugboats being free if the player had no required items in their inventory.
*/
namespace Oxide.Plugins {
[Info("BuyableTugboats", "https://discord.gg/YvkADY96ek", "1.0.1")]
[Description("Дозволяє купувати буксири у Рибальському селі")]
class BuyableTugboats : RustPlugin {
#region Config
private Configuration config;
public class Configuration {
[JsonProperty("Протягом якого часу інші гравці не зможуть сісти на буксир? [Це також призведе до повернення буксира, якщо гравець не забрав його]")]
public float safe_time = 300f;
[JsonProperty("Чи слід забрати човен, якщо гравець не забере його протягом безпечного часу?")]
public bool remove_after_safe_time = false;
[JsonProperty("Скільки палива має бути на нересті буксира?")]
public int starting_fuel = 100;
[JsonProperty("Намалювати на хаді гравців після покупки човна, щоб показати місце його появи?")]
public bool paint_boat_location = true;
[JsonProperty("Предмети, необхідні для придбання буксира")]
public List<ItemInfo> items = new List<ItemInfo>();
[JsonProperty("Локальні спавнові позиції")]
public Dictionary<Monument, List<Vector3>> LocalSpawns = new Dictionary<Monument, List<Vector3>>();
public string ToJson() => JsonConvert.SerializeObject(this);
public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson());
}
public class ItemInfo {
public string shortname;
public ulong skin;
public int amount;
public ItemInfo(string shortname, ulong skin, int amount) {
this.shortname = shortname;
this.skin = skin;
this.amount = amount;
}
}
protected override void LoadDefaultConfig() {
config = new Configuration();
config.items = DefaultItems;
config.LocalSpawns = DefaultLocalSpawnPoints;
}
Dictionary<Monument, List<Vector3>> DefaultLocalSpawnPoints {
get {
return new Dictionary<Monument, List<Vector3>>() {
[Monument.LargeFishingVillage]
= new List<Vector3>() {
new Vector3(17.8f, 1.9f, 33.4f), new Vector3(-6.1f, 1.9f, 38.8f), new Vector3(-44.6f, 1.9f, 20.5f), new Vector3(50.7f, 1.9f, 11.1f), new Vector3(52.3f, 1.9f, -6.2f), new Vector3(34.4f, 1.9f, 31.5f), new Vector3(-28.8f, 1.9f, 40f), new Vector3(-50.8f, 1.9f, 3.2f),
}
, [Monument.SmallFishingVillage] = new List<Vector3>() {
new Vector3(-12.6f, 1.9f, 40.9f), new Vector3(6.9f, 1.9f, 51.2f), new Vector3(26.8f, 1.9f, 25.3f), new Vector3(-37.8f, 1.9f, -6.6f), new Vector3(-39.1f, 1.9f, 25.4f), new Vector3(35f, 1.9f, -1.3f),
}
}
;
}
}
List<ItemInfo> DefaultItems {
get {
return new List<ItemInfo>() {
new ItemInfo("scrap", 0, 500), new ItemInfo("wood", 0, 2000), new ItemInfo("metal.fragments", 0, 200), new ItemInfo("metal.refined", 0, 20),
}
;
}
}
public enum Monument {
LargeFishingVillage, SmallFishingVillage
}
protected override void LoadConfig() {
base.LoadConfig();
try {
config = Config.ReadObject<Configuration>();
if (config == null) {
throw new JsonException();
}
SaveConfig();
}
catch {
PrintToConsole($"Файл конфігурації {Name}.json недійсний; використовуючи значення за замовчуванням");
LoadDefaultConfig();
}
}
protected override void SaveConfig() {
PrintToConsole($"Зміни конфігурації збережено в {Name}.json");
Config.WriteObject(config, true);
}
#endregion
#region Data
PlayerEntity pcdData;
private DynamicConfigFile PCDDATA;
const string perm_admin = "buyabletugboats.admin";
const string perm_free = "buyabletugboats.free";
const string perm_use = "buyabletugboats.use";
bool CanAccess(BasePlayer player) => permission.UserHasPermission(player.UserIDString, perm_use) || permission.UserHasPermission(player.UserIDString, perm_admin);
void Init() {
PCDDATA = Interface.Oxide.DataFileSystem.GetFile(this.Name);
LoadData();
permission.RegisterPermission(perm_admin, this);
permission.RegisterPermission(perm_free, this);
permission.RegisterPermission(perm_use, this);
}
void Unload() {
SaveData();
foreach (var player in BasePlayer.activePlayerList) {
DestroyUI(player);
}
}
void SaveData() {
PCDDATA.WriteObject(pcdData);
}
void LoadData() {
try {
pcdData = Interface.Oxide.DataFileSystem.ReadObject<PlayerEntity>(this.Name);
}
catch {
Puts("Не вдалося завантажити дані, створюється новий файл");
pcdData = new PlayerEntity();
}
}
class PlayerEntity {
public Dictionary<ulong, PCDInfo> spawnData = new Dictionary<ulong, PCDInfo>();
}
class PCDInfo {
}
#endregion;
#region Localization
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
["MissingNPC"]
= "Не вдалося знайти власника магазину NPC поблизу.", ["AddPointSuccess"] = "Збережено нове місце: {0} у тип рибальського села: {1}", ["NoSpawnRoom"] = "Недостатньо місця для створення вашого човна.", ["MissingItems"] = "У вас недостатньо предметів, щоб побудувати це судно.", ["RepossessNotification"] = "У вас є {0} секунд, щоб забрати свій буксир, перш ніж його повернуть. Сядьте на місце водія, щоб отримати його.", ["ReposessedNotification"] = "Ваш буксир було вилучено, оскільки ви не прийняли доставку.", ["UITugboatOption"] = "Як щодо буксира?", ["UIOptionNumber"] = "5", ["UIBoatVendorTitle"] = "Продавець човнів", ["UIVendorText"] = "Вам знадобляться деякі елементи, щоб ми могли створити це для вас...", ["BoatSpawnedNotification"] = "Ваш човен побудований і чекає на вас поруч.", ["hudLocationText"] = "<size=20>Буксир</size>", ["DisableNoclip"] = "Вимкніть noclip, щоб використовувати цю команду.",
}
, this);
}
#endregion
#region Hooks
void OnServerInitialized(bool initial) {
foreach (var item in ItemManager.GetItemDefinitions()) ItemIDs.Add(item.shortname, item.itemid);
}
Dictionary<string, int> ItemIDs = new Dictionary<string, int>();
#endregion
#region CUI
void DestroyUI(BasePlayer player) {
CuiHelper.DestroyUi(player, "Option5");
CuiHelper.DestroyUi(player, "TugboatPurchasePanel");
}
private void Option5(BasePlayer player) {
var container = new CuiElementContainer();
container.Add(new CuiPanel {
CursorEnabled = false, Image = {
Color = "0.1607843 0.1607843 0.1294118 1"
}
, RectTransform = {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "146 -137", OffsetMax = "426 -117"
}
}
, "Overlay", "Option5");
container.Add(new CuiElement {
Name = "ResponseText", Parent = "Option5", Components = {
new CuiTextComponent {
Text = lang.GetMessage("UITugboatOption", this, player.UserIDString), Font = "robotocondensed-regular.ttf", FontSize = 11, Align = TextAnchor.MiddleLeft, Color = "0.6980392 0.6745098 0.6352941 1"
}
, new CuiRectTransformComponent {
AnchorMin = "0 0.5", AnchorMax = "0 0.5", OffsetMin = "24.966 -10", OffsetMax = "280.501 10"
}
}
}
);
container.Add(new CuiPanel {
CursorEnabled = false, Image = {
Color = "0.3529412 0.4431373 0.2235294 1"
}
, RectTransform = {
AnchorMin = "0 0.5", AnchorMax = "0 0.5", OffsetMin = "5 -8", OffsetMax = "21 8"
}
}
, "Option5", "NumberPanel");
container.Add(new CuiElement {
Name = "text", Parent = "NumberPanel", Components = {
new CuiTextComponent {
Text = lang.GetMessage("UIOptionNumber", this, player.UserIDString), Font = "robotocondensed-bold.ttf", FontSize = 10, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1"
}
, new CuiRectTransformComponent {
AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "0 0", OffsetMax = "0 0"
}
}
}
);
container.Add(new CuiButton {
Button = {
Color = "1 1 1 0", Command = "sendtugboatbuyui"
}
, Text = {
Text = " ", Font = "robotocondensed-regular.ttf", FontSize = 14, Align = TextAnchor.MiddleCenter, Color = "0 0 0 1"
}
, RectTransform = {
AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "0 0", OffsetMax = "0.002 0"
}
}
, "Option5", "Button");
CuiHelper.DestroyUi(player, "Option5");
CuiHelper.AddUi(player, container);
}
[ConsoleCommand("sendtugboatbuyui")]
void SendTugboatBuyMenu(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
NPCTalking npc;
if (!Talkers.TryGetValue(player, out npc)) return;
Unsubscribe(nameof(OnNpcConversationEnded));
npc.ForceEndConversation(player);
Subscribe(nameof(OnNpcConversationEnded));
CuiHelper.DestroyUi(player, "Option5");
TugboatPurchasePanel(player);
}
Dictionary<BasePlayer, NPCTalking> Talkers = new Dictionary<BasePlayer, NPCTalking>();
private void TugboatPurchasePanel(BasePlayer player) {
var container = new CuiElementContainer();
container.Add(new CuiPanel {
CursorEnabled = true, Image = {
Color = "0.1226415 0.122063 0.122063 0.8431373"
}
, RectTransform = {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "139.523 -107.097", OffsetMax = "445.477 89.497"
}
}
, "Overlay", "TugboatPurchasePanel");
container.Add(new CuiPanel {
Image = {
Color = "0.2901961 0.2705882 0.2588235 0.8"
}
, RectTransform = {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-143.4 72.38", OffsetMax = "-41.455 89.9"
}
}
, "TugboatPurchasePanel", "TitlePanel");
container.Add(new CuiElement {
Name = "Title", Parent = "TitlePanel", Components = {
new CuiTextComponent {
Text = lang.GetMessage("UIBoatVendorTitle", this, player.UserIDString), Font = "robotocondensed-bold.ttf", FontSize = 10, Align = TextAnchor.MiddleCenter, Color = "0.7529413 0.7411765 0.7333333 1"
}
, new CuiRectTransformComponent {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-50.969 -8.76", OffsetMax = "50.971 8.76"
}
}
}
);
container.Add(new CuiPanel {
CursorEnabled = false, Image = {
Color = "0.2901961 0.2705882 0.2588235 0.8"
}
, RectTransform = {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-143.4 26.423", OffsetMax = "141.984 67.1"
}
}
, "TugboatPurchasePanel", "ConvoPanel");
container.Add(new CuiElement {
Name = "text", Parent = "ConvoPanel", Components = {
new CuiTextComponent {
Text = lang.GetMessage("UIVendorText", this, player.UserIDString), Font = "robotocondensed-regular.ttf", FontSize = 10, Align = TextAnchor.UpperLeft, Color = "0.7529412 0.7411765 0.7333333 1"
}
, new CuiRectTransformComponent {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-134.999 -20.339", OffsetMax = "135.001 20.338"
}
}
}
);
var count = 0;
var row = 0;
foreach (var item in config.items) {
int itemID;
container.Add(new CuiElement {
Name = $"Item_{count}_{row}", Parent = "TugboatPurchasePanel", Components = {
new CuiImageComponent {
Color = "1 1 1 1", ItemId = ItemIDs.TryGetValue(item.shortname, out itemID) ? itemID : 1751045826, SkinId = item.skin
}
, new CuiRectTransformComponent {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = $"{-143.4 + (count * 110)} {-10.6 - (row * 38)}", OffsetMax = $"{-111.4 + (count * 110)} {21.4- (row * 38)}"
}
}
}
);
container.Add(new CuiElement {
Name = "text", Parent = $"Item_{count}_{row}", Components = {
new CuiTextComponent {
Text = $"x{item.amount}", Font = "robotocondensed-regular.ttf", FontSize = 12, Align = TextAnchor.MiddleLeft, Color = "1 1 1 1"
}
, new CuiRectTransformComponent {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "19 -16", OffsetMax = "67 16"
}
}
}
);
count++;
if (count > 2) {
count = 0;
row++;
}
}
container.Add(new CuiButton {
Button = {
Color = "0.3764706 0.4470589 0.2117647 1", Command = "trybuildtugboat"
}
, Text = {
Text = "BUILD", Font = "robotocondensed-bold.ttf", FontSize = 14, Align = TextAnchor.MiddleCenter, Color = "0.8196079 0.8313726 0.764706 1"
}
, RectTransform = {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-32 -88.4", OffsetMax = "32 -64.4"
}
}
, "TugboatPurchasePanel", "button");
container.Add(new CuiButton {
Button = {
Color = "0.6784314 0.2156863 0 1", Command = "closetugboatbuildmenu"
}
, Text = {
Text = "X", Font = "robotocondensed-bold.ttf", FontSize = 10, Align = TextAnchor.MiddleCenter, Color = "0.9686275 0.9137256 0.8705883 1"
}
, RectTransform = {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "132.98 78.297", OffsetMax = "148.98 94.297"
}
}
, "TugboatPurchasePanel", "close");
CuiHelper.DestroyUi(player, "TugboatPurchasePanel");
CuiHelper.AddUi(player, container);
}
[ConsoleCommand("closetugboatbuildmenu")]
void CloseMenu(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
DestroyUI(player);
}
[ConsoleCommand("trybuildtugboat")]
void TryBuildTugboat(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
DestroyUI(player);
var entities = FindEntitiesOfType<NPCTalking>(player.transform.position, 5f);
bool foundNPC = false;
foreach (var npc in entities) {
if (npc.ShortPrefabName == "boat_shopkeeper") {
foundNPC = true;
break;
}
}
Pool.FreeList(ref entities);
if (!foundNPC) {
PrintToChat(player, lang.GetMessage("MissingNPC", this, player.UserIDString));
return;
}
var monument = GetClosestVillage(player.transform.position);
SpawnBoat(player, monument, monument.displayPhrase.english.Equals("large fishing village", StringComparison.OrdinalIgnoreCase) ? Monument.LargeFishingVillage : Monument.SmallFishingVillage);
}
public class TakeItemsInfo {
public int foundTotal;
public List<Item> items;
}
bool TakeItems(BasePlayer player) {
if (permission.UserHasPermission(player.UserIDString, perm_free)) return true;
Dictionary<ItemInfo, TakeItemsInfo> foundItems = new Dictionary<ItemInfo, TakeItemsInfo>();
foreach (var item in player.inventory.AllItems()) {
foreach (var entry in config.items) {
if (item.info.shortname != entry.shortname || item.skin != entry.skin) continue;
TakeItemsInfo found;
if (!foundItems.TryGetValue(entry, out found)) {
foundItems.Add(entry, found = new TakeItemsInfo() {
foundTotal = item.amount, items = Pool.GetList<Item>()
}
);
found.items.Add(item);
}
else if (found.foundTotal < entry.amount) {
found.foundTotal += item.amount;
found.items.Add(item);
}
}
}
if (foundItems.Count == 0) {
FreeLists(foundItems);
return false;
}
foreach (var entry in foundItems) {
if (entry.Value.foundTotal < entry.Key.amount) {
FreeLists(foundItems);
foundItems.Clear();
foundItems = null;
return false;
}
else {
entry.Value.foundTotal = entry.Key.amount;
}
}
foreach (var entry in foundItems) {
foreach (var item in entry.Value.items) {
if (entry.Value.foundTotal <= 0) break;
if (item.amount > entry.Value.foundTotal) {
item.UseItem(entry.Value.foundTotal);
entry.Value.foundTotal = 0;
}
else {
entry.Value.foundTotal -= item.amount;
item.Remove();
}
}
}
FreeLists(foundItems);
foundItems.Clear();
foundItems = null;
return true;
}
void FreeLists(Dictionary<ItemInfo, TakeItemsInfo> info) {
foreach (var entry in info) FreeList(entry.Value);
}
void FreeList(TakeItemsInfo list) => Pool.FreeList(ref list.items);
void OnNpcConversationStart(NPCTalking npcTalking, BasePlayer player, ConversationData conversationData) {
if (!CanAccess(player)) return;
if (!Talkers.ContainsKey(player)) Talkers.Add(player, npcTalking);
else Talkers[player] = npcTalking;
return;
}
void OnNpcConversationEnded(NPCTalking npcTalking, BasePlayer player) {
DestroyUI(player);
Talkers.Remove(player);
}
void OnNpcConversationRespond(NPCTalking npcTalking, BasePlayer player, ConversationData conversationData, ConversationData.ResponseNode responseNode) {
if (!CanAccess(player)) return;
if (responseNode.responseText.Contains("buy a boat")) Option5(player);
else DestroyUI(player);
return;
}
#endregion
#region Boat spawn
MonumentInfo GetClosestVillage(Vector3 pos) {
var closest = -1f;
MonumentInfo result = null;
foreach (var monument in TerrainMeta.Path.Monuments) {
//Puts($"Monument: {monument.displayPhrase.english}");
var dist = Vector3.Distance(pos, monument.transform.position);
if (!monument.IsSafeZone) continue;
if (closest < 0 || dist < closest) {
closest = dist;
result = monument;
}
}
return result;
}
[ChatCommand("btshowspawnpoints")]
void ShowSpawnPoints(BasePlayer player) {
if (!permission.UserHasPermission(player.UserIDString, perm_admin)) return;
if (!player.IsAdmin && !player.IsDeveloper && player.IsFlying) {
PrintToChat(player, lang.GetMessage("DisableNoclip", this, player.UserIDString));
return;
// BasePlayer => FinalizeTick => NoteAdminHack => Ban => Cheat Detected!
}
MonumentInfo monument = GetClosestVillage(player.transform.position);
if (monument == null || !monument.displayPhrase.english.Contains("Fishing Village")) {
player.ChatMessage("Could not find monument Fishing Village.");
return;
}
var type = monument.displayPhrase.english.Equals("Large Fishing Village", StringComparison.OrdinalIgnoreCase) ? Monument.LargeFishingVillage : Monument.SmallFishingVillage;
List<Vector3> locs;
if (!config.LocalSpawns.TryGetValue(type, out locs)) return;
var wasAdmin = player.IsAdmin;
if (!wasAdmin) {
player.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, true);
player.SendNetworkUpdateImmediate();
}
foreach (var loc in locs) {
player.SendConsoleCommand("ddraw.text", 10f, Color.yellow, ConvertLocalsToWorld(monument, loc), "<size=20>X</size>");
}
if (!wasAdmin) {
player.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, false);
player.SendNetworkUpdateImmediate();
}
}
[ChatCommand("btaddspawnpoint")]
void SetBoatSpawnPoint(BasePlayer player) {
if (!permission.UserHasPermission(player.UserIDString, perm_admin)) return;
if (!player.IsAdmin && !player.IsDeveloper && player.IsFlying) {
PrintToChat(player, lang.GetMessage("DisableNoclip", this, player.UserIDString));
return;
// BasePlayer => FinalizeTick => NoteAdminHack => Ban => Cheat Detected!
}
MonumentInfo monument = GetClosestVillage(player.transform.position);
if (monument == null || !monument.displayPhrase.english.Contains("Fishing Village")) {
player.ChatMessage("Could not find monument Fishing Village.");
return;
}
var type = monument.displayPhrase.english.Equals("Large Fishing Village", StringComparison.OrdinalIgnoreCase) ? Monument.LargeFishingVillage : Monument.SmallFishingVillage;
Vector3 localPosition = monument.transform.InverseTransformPoint(player.transform.position);
List<Vector3> locs;
if (!config.LocalSpawns.TryGetValue(type, out locs)) return;
locs.Add(localPosition);
PrintToChat(player, string.Format(lang.GetMessage("AddPointSuccess", this, player.UserIDString), localPosition, type));
var wasAdmin = player.IsAdmin;
if (!wasAdmin) {
player.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, true);
player.SendNetworkUpdateImmediate();
}
foreach (var loc in locs) {
player.SendConsoleCommand("ddraw.text", 10f, Color.yellow, ConvertLocalsToWorld(monument, loc), "<size=20>X</size>");
}
if (!wasAdmin) {
player.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, false);
player.SendNetworkUpdateImmediate();
}
SaveConfig();
}
Vector3 ConvertLocalsToWorld(MonumentInfo monument, Vector3 loc) {
return monument.transform.localToWorldMatrix.MultiplyPoint3x4(loc);
}
void SpawnBoat(BasePlayer player, MonumentInfo monument, Monument type) {
Vector3 pos = Vector3.zero;
foreach (var _pos in config.LocalSpawns[type]) {
var worldPos = ConvertLocalsToWorld(monument, _pos);
var entities = FindEntitiesOfType<BaseBoat>(worldPos, 5f);
if (entities.Count > 0) {
Pool.FreeList(ref entities);
continue;
}
Pool.FreeList(ref entities);
pos = worldPos;
break;
}
if (pos == Vector3.zero) {
PrintToChat(player, lang.GetMessage("NoSpawnRoom", this, player.UserIDString));
return;
}
if (!TakeItems(player)) {
PrintToChat(player, lang.GetMessage("MissingItems", this, player.UserIDString));
return;
}
var rot = Quaternion.LookRotation((pos - monument.transform.position).normalized);
var tugboat = GameManager.server.CreateEntity("assets/content/vehicles/boats/tugboat/tugboat.prefab", pos, rot) as Tugboat;
tugboat.Spawn();
AddFuel(tugboat);
AddSafety(player, tugboat);
if (!config.paint_boat_location) return;
if (!player.IsAdmin && !player.IsDeveloper && player.IsFlying) {
return;
// BasePlayer => FinalizeTick => NoteAdminHack => Ban => Cheat Detected!
}
var wasAdmin = player.IsAdmin;
if (!wasAdmin) {
player.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, true);
player.SendNetworkUpdateImmediate();
}
player.SendConsoleCommand("ddraw.text", 10f, Color.yellow, pos, lang.GetMessage("hudLocationText", this, player.UserIDString));
if (!wasAdmin) {
player.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, false);
player.SendNetworkUpdateImmediate();
}
}
void AddFuel(Tugboat tugboat) {
if (config.starting_fuel < 1) return;
ItemManager.CreateByName("lowgradefuel", config.starting_fuel).MoveToContainer(tugboat.fuelSystem.GetFuelContainer().inventory);
}
private static List<T> FindEntitiesOfType<T>(Vector3 a, float n, int m = -1) where T : BaseEntity {
int hits = Physics.OverlapSphereNonAlloc(a, n, Vis.colBuffer, m, QueryTriggerInteraction.Collide);
List<T> entities = Pool.GetList<T>();
for (int i = 0; i < hits; i++) {
var entity = Vis.colBuffer[i]?.ToBaseEntity() as T;
if (entity != null && !entities.Contains(entity)) entities.Add(entity);
Vis.colBuffer[i] = null;
}
return entities;
}
#endregion
#region Monitor bought boat
Dictionary<Tugboat, BasePlayer> BoughtBoat = new Dictionary<Tugboat, BasePlayer>();
object CanMountEntity(BasePlayer player, BaseMountable entity) {
var tugboat = entity.GetParentEntity() as Tugboat;
if (tugboat == null) return null;
BasePlayer owner;
if (BoughtBoat.TryGetValue(tugboat, out owner)) {
if (owner != player) return false;
RemoveSafety(tugboat);
}
return null;
}
Dictionary<Tugboat, Timer> TugboatTimers = new Dictionary<Tugboat, Timer>();
void AddSafety(BasePlayer player, Tugboat tugboat) {
BoughtBoat.Add(tugboat, player);
AddTugboatTimer(tugboat);
if (config.remove_after_safe_time) PrintToChat(player, string.Format(lang.GetMessage("RepossessNotification", this, player.UserIDString), config.safe_time));
else PrintToChat(player, lang.GetMessage("BoatSpawnedNotification", this, player.UserIDString));
}
void RemoveSafety(Tugboat tugboat) {
BoughtBoat.Remove(tugboat);
RemoveTugboatTimer(tugboat);
}
void AddTugboatTimer(Tugboat tugboat) {
TugboatTimers.Add(tugboat, timer.Once(config.safe_time, () => {
if (config.remove_after_safe_time) {
tugboat.Invoke(tugboat.KillMessage, 0.01f);
BasePlayer player;
if (BoughtBoat.TryGetValue(tugboat, out player)) PrintToChat(player, lang.GetMessage("ReposessedNotification", this, player.UserIDString));
}
RemoveSafety(tugboat);
}
));
}
void RemoveTugboatTimer(Tugboat tugboat) {
Timer _timer;
if (!TugboatTimers.TryGetValue(tugboat, out _timer)) return;
if (_timer != null && !_timer.Destroyed) _timer.Destroy();
TugboatTimers.Remove(tugboat);
}
#endregion
}
}