C#:
// #define TESTING
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Facepunch;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Oxide.Plugins.VehicleBuyExtensionMethods;
using Rust;
using Rust.Modular;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using Physics = UnityEngine.Physics;
using Random = UnityEngine.Random;
using Time = UnityEngine.Time;
using UnityEngine.AI;
using Math = System.Math;
#if CARBON
using Carbon.Base;
using Carbon.Modules;
#endif
namespace Oxide.Plugins {
[Info("Purchase Vehicles from the Shop", "M&B-Studios & Mevent", "2.1.4")]
internal class VehicleBuy : RustPlugin {
private class LockedVehicleTracker {
public Dictionary<VehicleInfo, HashSet<BaseEntity>> VehiclesWithLocksByType {
get;
}
= new();
private readonly VehicleInfoManager _vehicleInfoManager;
public LockedVehicleTracker(VehicleInfoManager vehicleInfoManager) {
_vehicleInfoManager = vehicleInfoManager;
}
public void OnServerInitialized() {
foreach (var entity in BaseNetworkable.serverEntities) {
var baseEntity = entity as BaseEntity;
if (baseEntity == null) continue;
var vehicleInfo = _vehicleInfoManager.GetVehicleInfo(baseEntity);
if (vehicleInfo == null || GetVehicleLock(baseEntity) == null) continue;
OnLockAdded(baseEntity);
}
}
public void OnLockAdded(BaseEntity vehicle) {
GetEntityListForVehicle(vehicle)?.Add(vehicle);
}
public void OnLockRemoved(BaseEntity vehicle) {
GetEntityListForVehicle(vehicle)?.Remove(vehicle);
}
private HashSet<BaseEntity> EnsureEntityList(VehicleInfo vehicleInfo) {
if (!VehiclesWithLocksByType.TryGetValue(vehicleInfo, out var vehicleList)) {
vehicleList = new HashSet<BaseEntity>();
VehiclesWithLocksByType[vehicleInfo] = vehicleList;
}
return vehicleList;
}
private HashSet<BaseEntity> GetEntityListForVehicle(BaseEntity entity) {
var vehicleInfo = _vehicleInfoManager.GetVehicleInfo(entity);
if (vehicleInfo == null) return null;
return EnsureEntityList(vehicleInfo);
}
}
private const float MaxDeployDistance = 3;
private class VehicleInfo {
public string VehicleType;
public string[] PrefabPaths;
public Vector3 LockPosition;
public Quaternion LockRotation;
public string ParentBone;
public string CodeLockPermission {
get;
}
public string KeyLockPermission {
get;
}
public uint[] PrefabIds {
get;
private set;
}
public Func<BaseEntity, BaseEntity> DetermineLockParent = entity => entity;
public Func<BaseEntity, float> TimeSinceLastUsed = entity => 0;
public void OnServerInitialized() {
if (!Instance.permission.PermissionExists(CodeLockPermission, Instance)) Instance.permission.RegisterPermission(CodeLockPermission, Instance);
if (!Instance.permission.PermissionExists(KeyLockPermission, Instance)) Instance.permission.RegisterPermission(KeyLockPermission, Instance);
ServerMgr.Instance.InvokeRepeating(() => GetNewEnts(), 0, 60);
// Custom vehicles aren't currently allowed to specify prefabs since they reuse existing prefabs.if (PrefabPaths != null)
{
var prefabIds = new List<uint>();
foreach (var prefabName in PrefabPaths) {
var prefabId = StringPool.Get(prefabName);
if (prefabId != 0) prefabIds.Add(prefabId);
}
PrefabIds = prefabIds.ToArray();
}
}
// In the future, custom vehicles may be able to pass in a method to override this.public bool IsMounted(BaseEntity entity)
{
var vehicle = entity as BaseVehicle;
if (vehicle != null) return vehicle.AnyMounted();
var mountable = entity as BaseMountable;
if (mountable != null) return mountable.AnyMounted();
return false;
}
}
private class VehicleInfoManager {
private readonly Dictionary<uint, VehicleInfo> _prefabIdToVehicleInfo = new();
private readonly Dictionary<string, VehicleInfo> _customVehicleTypes = new();
public void OnServerInitialized() {
var allVehicles = new[] {
new VehicleInfo {
VehicleType = "attackhelicopter", PrefabPaths = new[] {
"assets/content/vehicles/attackhelicopter/attackhelicopter.entity.prefab"
}
, LockPosition = new Vector3(-0.6f, 1.08f, 1.01f), TimeSinceLastUsed = vehicle => Time.time - (vehicle as AttackHelicopter)?.lastEngineOnTime ?? Time.time
}
, new VehicleInfo {
VehicleType = "chinook", PrefabPaths = new[] {
"assets/prefabs/npc/ch47/ch47.entity.prefab"
}
, LockPosition = new Vector3(-1.175f, 2, 6.5f), TimeSinceLastUsed = vehicle => Time.time - (vehicle as CH47Helicopter)?.lastPlayerInputTime ?? Time.time
}
, new VehicleInfo {
VehicleType = "duosub", PrefabPaths = new[] {
"assets/content/vehicles/submarine/submarineduo.entity.prefab"
}
, LockPosition = new Vector3(-0.455f, 1.29f, 0.75f), LockRotation = Quaternion.Euler(0, 180, 10), TimeSinceLastUsed = vehicle => (vehicle as SubmarineDuo)?.timeSinceLastUsed ?? 0
}
, new VehicleInfo {
VehicleType = "hotairballoon", PrefabPaths = new[] {
"assets/prefabs/deployable/hot air balloon/hotairballoon.prefab"
}
, LockPosition = new Vector3(1.45f, 0.9f, 0), TimeSinceLastUsed = vehicle => Time.time - (vehicle as HotAirBalloon)?.sinceLastBlast ?? Time.time
}
, new VehicleInfo {
VehicleType = "kayak", PrefabPaths = new[] {
"assets/content/vehicles/boats/kayak/kayak.prefab"
}
, LockPosition = new Vector3(-0.43f, 0.2f, 0.2f), LockRotation = Quaternion.Euler(0, 90, 90), TimeSinceLastUsed = vehicle => (vehicle as Kayak)?.timeSinceLastUsed ?? 0
}
, new VehicleInfo {
VehicleType = "locomotive", PrefabPaths = new[] {
"assets/content/vehicles/trains/locomotive/locomotive.entity.prefab"
}
, LockPosition = new Vector3(-0.11f, 2.89f, 4.95f), TimeSinceLastUsed = vehicle => (vehicle as TrainEngine)?.decayingFor ?? 0
}
, new VehicleInfo {
VehicleType = "magnetcrane", PrefabPaths = new[] {
"assets/content/vehicles/crane_magnet/magnetcrane.entity.prefab"
}
, LockPosition = new Vector3(-1.735f, -1.445f, 0.79f), LockRotation = Quaternion.Euler(0, 0, 90), ParentBone = "Top", TimeSinceLastUsed = vehicle => Time.realtimeSinceStartup - (vehicle as MagnetCrane)?.lastDrivenTime ?? Time.realtimeSinceStartup
}
, new VehicleInfo {
VehicleType = "minicopter", PrefabPaths = new[] {
"assets/content/vehicles/minicopter/minicopter.entity.prefab"
}
, LockPosition = new Vector3(-0.15f, 0.7f, -0.1f), TimeSinceLastUsed = vehicle => Time.time - (vehicle as Minicopter)?.lastEngineOnTime ?? Time.time
}
, new VehicleInfo {
VehicleType = "modularcar", // There are at least 37 valid Modular Car prefabs.PrefabPaths = FindPrefabsOfType<ModularCar>(),
LockPosition = new Vector3(-0.9f, 0.35f, -0.5f), DetermineLockParent = vehicle => FindFirstDriverModule((ModularCar) vehicle), TimeSinceLastUsed = vehicle => Time.time - (vehicle as ModularCar)?.lastEngineOnTime ?? Time.time
}
, new VehicleInfo {
VehicleType = "rhib", PrefabPaths = new[] {
"assets/content/vehicles/boats/rhib/rhib.prefab"
}
, LockPosition = new Vector3(-0.68f, 2.00f, 0.7f), TimeSinceLastUsed = vehicle => (vehicle as RHIB)?.timeSinceLastUsedFuel ?? 0
}
, new VehicleInfo {
VehicleType = "ridablehorse", PrefabPaths = new[] {
"assets/content/vehicles/horse/ridablehorse2.prefab"
}
, LockPosition = new Vector3(-0.6f, 0.25f, -0.1f), LockRotation = Quaternion.Euler(0, 95, 90), ParentBone = "Horse_RootBone", TimeSinceLastUsed = vehicle => Time.time - (vehicle as RidableHorse2)?.lastRiddenTime ?? Time.time
}
, new VehicleInfo {
VehicleType = "rowboat", PrefabPaths = new[] {
"assets/content/vehicles/boats/rowboat/rowboat.prefab"
}
, LockPosition = new Vector3(-0.83f, 0.51f, -0.57f), TimeSinceLastUsed = vehicle => (vehicle as MotorRowboat)?.timeSinceLastUsedFuel ?? 0
}
, new VehicleInfo {
VehicleType = "scraptransport", PrefabPaths = new[] {
"assets/content/vehicles/scrap heli carrier/scraptransporthelicopter.prefab"
}
, LockPosition = new Vector3(-1.25f, 1.22f, 1.99f), TimeSinceLastUsed = vehicle => Time.time - (vehicle as ScrapTransportHelicopter)?.lastEngineOnTime ?? Time.time
}
, new VehicleInfo {
VehicleType = "sedan", PrefabPaths = new[] {
"assets/content/vehicles/sedan_a/sedantest.entity.prefab"
}
, LockPosition = new Vector3(-1.09f, 0.79f, 0.5f)
}
, new VehicleInfo {
VehicleType = "sedanrail", PrefabPaths = new[] {
"assets/content/vehicles/sedan_a/sedanrail.entity.prefab"
}
, LockPosition = new Vector3(-1.09f, 1.025f, -0.26f), TimeSinceLastUsed = vehicle => (vehicle as TrainEngine)?.decayingFor ?? 0
}
, new VehicleInfo {
VehicleType = "snowmobile", PrefabPaths = new[] {
"assets/content/vehicles/snowmobiles/snowmobile.prefab"
}
, LockPosition = new Vector3(-0.205f, 0.59f, 0.4f), TimeSinceLastUsed = vehicle => (vehicle as Snowmobile)?.timeSinceLastUsed ?? 0
}
, new VehicleInfo {
VehicleType = "solosub", PrefabPaths = new[] {
"assets/content/vehicles/submarine/submarinesolo.entity.prefab"
}
, LockPosition = new Vector3(0f, 1.85f, 0f), LockRotation = Quaternion.Euler(0, 90, 90), TimeSinceLastUsed = vehicle => (vehicle as BaseSubmarine)?.timeSinceLastUsed ?? 0
}
, new VehicleInfo {
VehicleType = "tomaha", PrefabPaths = new[] {
"assets/content/vehicles/snowmobiles/tomahasnowmobile.prefab"
}
, LockPosition = new Vector3(-0.37f, 0.4f, 0.125f), TimeSinceLastUsed = vehicle => (vehicle as Snowmobile)?.timeSinceLastUsed ?? 0
}
, new VehicleInfo {
VehicleType = "tugboat", PrefabPaths = new[] {
"assets/content/vehicles/boats/tugboat/tugboat.prefab"
}
, LockPosition = new Vector3(0.065f, 6.8f, 4.12f), LockRotation = Quaternion.Euler(0, 90, 60), TimeSinceLastUsed = vehicle => (vehicle as Tugboat)?.timeSinceLastUsedFuel ?? 0
}
, new VehicleInfo {
VehicleType = "workcart", PrefabPaths = new[] {
"assets/content/vehicles/trains/workcart/workcart.entity.prefab"
}
, LockPosition = new Vector3(-0.2f, 2.35f, 2.7f), TimeSinceLastUsed = vehicle => (vehicle as TrainEngine)?.decayingFor ?? 0
}
, new VehicleInfo {
VehicleType = "workcartaboveground", PrefabPaths = new[] {
"assets/content/vehicles/trains/workcart/workcart_aboveground.entity.prefab"
}
, LockPosition = new Vector3(-0.2f, 2.35f, 2.7f), TimeSinceLastUsed = vehicle => (vehicle as TrainEngine)?.decayingFor ?? 0
}
, new VehicleInfo {
VehicleType = "workcartcovered", PrefabPaths = new[] {
"assets/content/vehicles/trains/workcart/workcart_aboveground2.entity.prefab"
}
, LockPosition = new Vector3(-0.2f, 2.35f, 2.7f), TimeSinceLastUsed = vehicle => (vehicle as TrainEngine)?.decayingFor ?? 0
}
}
;
foreach (var vehicleInfo in allVehicles) {
vehicleInfo.OnServerInitialized();
foreach (var prefabId in vehicleInfo.PrefabIds) _prefabIdToVehicleInfo[prefabId] = vehicleInfo;
}
}
public void RegisterCustomVehicleType(VehicleInfo vehicleInfo) {
vehicleInfo.OnServerInitialized();
_customVehicleTypes[vehicleInfo.VehicleType] = vehicleInfo;
}
public VehicleInfo GetVehicleInfo(BaseEntity entity) {
if (_prefabIdToVehicleInfo.TryGetValue(entity.prefabID, out var vehicleInfo)) return vehicleInfo;
foreach (var customVehicleInfo in _customVehicleTypes.Values) if (customVehicleInfo.DetermineLockParent(entity) != null) return customVehicleInfo;
return null;
}
public BaseEntity GetCustomVehicleParent(BaseEntity entity) {
foreach (var vehicleInfo in _customVehicleTypes.Values) {
var lockParent = vehicleInfo.DetermineLockParent(entity);
if (lockParent != null) return lockParent;
}
return null;
}
}
internal struct VendingMachinePosition {
public Vector3 Offset;
public Transform transform;
public Vector3 Rotation;
public VendingMachinePosition(Transform position, Vector3 rotation, Vector3 offset) {
transform = position;
Rotation = rotation;
Offset = offset;
}
}
#region Fields
[PluginReference]
private Plugin ImageLibrary = null, Economics = null, ServerRewards = null, BankSystem = null;
private static VehicleBuy Instance;
#if CARBON
private ImageDatabaseModule imageDatabase;
#endif
private bool _enabledImageLibrary;
private Dictionary<ulong, ulong> PRM = new();
private const bool LangRu = false;
private const string Prefab_CodeLock_DeployedEffect = "assets/prefabs/locks/keypad/effects/lock-code-deploy.prefab", Prefab_CodeLock_DeniedEffect = "assets/prefabs/locks/keypad/effects/lock.code.denied.prefab", Prefab_CodeLock_UnlockEffect = "assets/prefabs/locks/keypad/effects/lock.code.unlock.prefab", VENDINGMACHINE_PREFAB = "assets/prefabs/deployable/vendingmachine/vendingmachine.deployed.prefab";
private const string PERM_USE = "vehiclebuy.use", PERM_FREE = "vehiclebuy.free", PERM_PICKUP = "vehiclebuy.pickup", PERM_RECALL = "vehiclebuy.recall";
private const string PURCHASE_EFFECT = "assets/prefabs/deployable/vendingmachine/effects/vending-machine-purchase-human.prefab";
private VehicleInfoManager _vehicleInfoManager;
private LockedVehicleTracker _lockedVehicleTracker;
private List<BaseEntity> VendingMachines = new();
private VendingMachinePosition BANDITCAMP_POSITION;
private VendingMachinePosition OUTPOST_POSITION;
private VendingMachinePosition fvA_POSITION;
private VendingMachinePosition fvB_POSITION;
private VendingMachinePosition fvC_POSITION;
private MonumentInfo Outpost;
private MonumentInfo BanditCamp;
private List<MonumentInfo> FishingVillagesA = new();
private List<MonumentInfo> FishingVillagesB = new();
private List<MonumentInfo> FishingVillagesC = new();
#endregion
#region Data
private Dictionary<ulong, PlayerData> _players = new();
internal class PlayerData {
public Dictionary<string, int> Cooldowns = new();
public Dictionary<string, int> PurchasedVehicles = new Dictionary<string, int>();
}
#endregion
#region Base Hooks
private bool CanPlayerBypassLock(BasePlayer player, BaseLock baseLock, bool provideFeedback) {
var hookResult = Interface.CallHook("CanUseLockedEntity", player, baseLock);
if (hookResult is bool) return (bool)hookResult;
var canAccessLock = IsPlayerAuthorizedToLock(player, baseLock);
if (canAccessLock) {
if (provideFeedback && !(baseLock is KeyLock)) Effect.server.Run(Prefab_CodeLock_UnlockEffect, baseLock, 0, Vector3.zero, Vector3.forward);
return true;
}
if (provideFeedback) Effect.server.Run(Prefab_CodeLock_DeniedEffect, baseLock, 0, Vector3.zero, Vector3.forward);
return false;
}
private static VehicleModuleSeating FindFirstDriverModule(ModularCar car) {
for (var socketIndex = 0; socketIndex < car.TotalSockets; socketIndex++) {
if (car.TryGetModuleAt(socketIndex, out var module)) {
var seatingModule = module as VehicleModuleSeating;
if (seatingModule != null && seatingModule.HasADriverSeat()) return seatingModule;
}
}
return null;
}
private void Init() {
_vehicleInfoManager = new VehicleInfoManager();
_lockedVehicleTracker = new LockedVehicleTracker(_vehicleInfoManager);
if (!_config.DisableVehiclesDamage) Unsubscribe(nameof(OnEntityTakeDamage));
}
private void OnServerInitialized() {
Outpost = TerrainMeta.Path.Monuments.Find(p => p.name.ToLower().Contains("monument/medium/compound"));
BanditCamp = TerrainMeta.Path.Monuments.Find(p => p.name.ToLower().Contains("monument/medium/bandit_town"));
BANDITCAMP_POSITION = new VendingMachinePosition {
Offset = new Vector3(-53.28f, 2f, 28.72f), transform = BanditCamp?.transform ?? null, Rotation = new Vector3(0, 51.4f, 0)
}
;
OUTPOST_POSITION = new VendingMachinePosition {
Offset = new Vector3(32f, 1.51f, -15.34f), transform = Outpost?.transform ?? null, Rotation = new Vector3(0, 270f, 0)
}
;
fvA_POSITION = new VendingMachinePosition {
Offset = new Vector3(1.2f, 2f, 4.48f), Rotation = new Vector3(0, 180f, 0)
}
;
fvB_POSITION = new VendingMachinePosition {
Offset = new Vector3(-10.04f, 2f, 20.58f), Rotation = new Vector3(0, 270f, 0)
}
;
fvC_POSITION = new VendingMachinePosition {
Offset = new Vector3(-8.72f, 2f, 12.23f), Rotation = new Vector3(0, 90f, 0)
}
;
var fvA = TerrainMeta.Path.Monuments.FindAll(p => p.name.ToLower().Contains("monument/fishing_village/fishing_village_a"));
var fvB = TerrainMeta.Path.Monuments.FindAll(p => p.name.ToLower().Contains("monument/fishing_village/fishing_village_b"));
var fvC = TerrainMeta.Path.Monuments.FindAll(p => p.name.ToLower().Contains("monument/fishing_village/fishing_village_c"));
if (fvA.Any()) FishingVillagesA.AddRange(fvA);
else PrintError("Fishing villages A not found at map");
if (fvB.Any()) FishingVillagesB.AddRange(fvB);
else PrintError("Fishing villages B not found at map");
if (fvC.Any()) FishingVillagesC.AddRange(fvC);
else PrintError("Fishing villages C not found at map");
if (Outpost == null) PrintError("Outpost not found at the map!");
if (BanditCamp == null) PrintError("Bandit Camp not found at the map!");
RegisterPermissions();
RegisterCommands();
Instance = this;
LoadSetupUI();
LoadImages();
_vehicleInfoManager.OnServerInitialized();
_lockedVehicleTracker.OnServerInitialized();
foreach (var entity in BaseNetworkable.serverEntities.OfType<BaseEntity>()) {
var vehicleInfo = _vehicleInfoManager.GetVehicleInfo(entity);
if (vehicleInfo == null) continue;
var lockEntity = GetVehicleLock(entity);
if (lockEntity == null) continue;
var transform = lockEntity.transform;
transform.localPosition = vehicleInfo.LockPosition;
transform.localRotation = vehicleInfo.LockRotation;
lockEntity.SendNetworkUpdate_Position();
}
Subscribe(nameof(OnEntityKill));
SpawnVendingMachines();
}
private void Unload() {
try {
DestroyVendingMachines();
foreach (var player in BasePlayer.activePlayerList) {
CuiHelper.DestroyUi(player, Layer);
CuiHelper.DestroyUi(player, "UI.Server.Panel.Content");
}
}
finally {
Instance = null;
}
}
#endregion
#region Functions
[ChatCommand("getposfva")]
private void cmdGetPosfva(BasePlayer player) {
if (!player.IsAdmin) return;
var pos = player.GetNetworkPosition();
var fva = FishingVillagesA.FirstOrDefault();
// player.Teleport(fva.transform.position);
player.ChatMessage($"Monument pos = {fva.transform.position}");
player.ChatMessage($"Local to mon = {fva.transform.InverseTransformPoint(player.GetNetworkPosition())}");
player.ChatMessage($"Global = X - {pos.x}, Y - {pos.y}, Z - {pos.z}");
player.ChatMessage($"qu - {player.GetNetworkRotation()}");
}
[ChatCommand("getposfvb")]
private void cmdGetPosfvb(BasePlayer player) {
if (!player.IsAdmin) return;
var pos = player.GetNetworkPosition();
var fva = FishingVillagesB.FirstOrDefault();
// player.Teleport(fva.transform.position);
player.ChatMessage($"Monument pos = {fva.transform.position}");
player.ChatMessage($"Local to mon = {fva.transform.InverseTransformPoint(player.GetNetworkPosition())}");
player.ChatMessage($"Global = X - {pos.x}, Y - {pos.y}, Z - {pos.z}");
player.ChatMessage($"qu - {player.GetNetworkRotation()}");
}
[ChatCommand("getposfvc")]
private void cmdGetPosfvc(BasePlayer player) {
if (!player.IsAdmin) return;
var pos = player.GetNetworkPosition();
var fva = FishingVillagesC.FirstOrDefault();
// player.Teleport(fva.transform.position);
player.ChatMessage($"Monument pos = {fva.transform.position}");
player.ChatMessage($"Local to mon = {fva.transform.InverseTransformPoint(player.GetNetworkPosition())}");
player.ChatMessage($"Global = X - {pos.x}, Y - {pos.y}, Z - {pos.z}");
player.ChatMessage($"qu - {player.GetNetworkRotation()}");
}
[ChatCommand("getposoutpost")]
private void cmdGetPosOP(BasePlayer player) {
if (!player.IsAdmin) return;
var pos = player.GetNetworkPosition();
player.ChatMessage($"Monument pos = {Outpost.transform.position}");
player.ChatMessage($"Local to mon = {Outpost.transform.InverseTransformPoint(player.GetNetworkPosition())}");
player.ChatMessage($"Global = X - {pos.x}, Y - {pos.y}, Z - {pos.z}");
player.ChatMessage($"qu - {player.GetNetworkRotation()}");
}
[ChatCommand("getposbandit")]
private void cmdGetPosBC(BasePlayer player) {
if (!player.IsAdmin) return;
var pos = player.GetNetworkPosition();
player.ChatMessage($"Monument pos = {BanditCamp.transform.position}");
player.ChatMessage($"Local to mon = {BanditCamp.transform.InverseTransformPoint(player.GetNetworkPosition())}");
player.ChatMessage($"Global = X - {pos.x}, Y - {pos.y}, Z - {pos.z}");
player.ChatMessage($"qu - {player.GetNetworkRotation()}");
}
private object OnGiveSoldItem(VendingMachine vending, Item soldItem, BasePlayer buyer) {
if (!VMProducts.ContainsKey(vending.net.ID)) return null;
var vmopt = VMProducts[vending.net.ID];
if (!vmopt.Any(x => x.RandomShortname == soldItem.info.shortname)) return null;
var item = vmopt.FirstOrDefault(x => x.RandomShortname == soldItem.info.shortname);
var itemToGive = ItemManager.CreateByItemID(item.Get().DeployableItemId, soldItem.amount, item.Get().Skin);
if (!string.IsNullOrEmpty(item.Get().Name)) itemToGive.name = item.Get().Name;
buyer.GiveItem(itemToGive);
return false;
}
private object CanAdministerVending(BasePlayer player, VendingMachine machine) {
if (VMProducts.ContainsKey(machine.net.ID)) return false;
return null;
}
private void SpawnVendingMachines() {
if (_config.VendingMachines.BanditCampSpawnMachine && BanditCamp) {
var machine = SpawnVendingMachine(BANDITCAMP_POSITION);
if (!machine.TryGetComponent<VendingMachine>(out var comp)) {
UnityEngine.Object.Destroy(machine.gameObject);
return;
}
SetupVendingMachine(machine, comp, _config.VendingMachines.BanditCampOrders);
}
if (_config.VendingMachines.OutpostSpawnMachine && Outpost) {
var machine = SpawnVendingMachine(OUTPOST_POSITION);
// BasePlayer.activePlayerList.First().Teleport(machine.transform.position);
if (!machine.TryGetComponent<VendingMachine>(out var comp)) {
UnityEngine.Object.Destroy(machine.gameObject);
return;
}
SetupVendingMachine(machine, comp, _config.VendingMachines.OutpostOrders);
}
if (_config.VendingMachines.FishingVillageASpawnMachine && FishingVillagesA.Any()) foreach (var x in FishingVillagesA) {
var machine = SpawnVendingMachine(fvA_POSITION, x.transform);
if (!machine.TryGetComponent<VendingMachine>(out var comp)) {
UnityEngine.Object.Destroy(machine.gameObject);
return;
}
SetupVendingMachine(machine, comp, _config.VendingMachines.FishingVillageAOrders);
}
if (_config.VendingMachines.FishingVillageBSpawnMachine && FishingVillagesB.Any()) foreach (var x in FishingVillagesB) {
var machine = SpawnVendingMachine(fvB_POSITION, x.transform);
if (!machine.TryGetComponent<VendingMachine>(out var comp)) {
UnityEngine.Object.Destroy(machine.gameObject);
return;
}
SetupVendingMachine(machine, comp, _config.VendingMachines.FishingVillageBOrders);
}
if (_config.VendingMachines.FishingVillageBSpawnMachine && FishingVillagesB.Any()) foreach (var x in FishingVillagesC) {
var machine = SpawnVendingMachine(fvC_POSITION, x.transform);
if (!machine.TryGetComponent<VendingMachine>(out var comp)) {
UnityEngine.Object.Destroy(machine.gameObject);
return;
}
SetupVendingMachine(machine, comp, _config.VendingMachines.FishingVillageCOrders);
}
}
private static void SendEffect(BasePlayer player, string effect) {
if (player == null || string.IsNullOrEmpty(effect)) return;
Effect.server.Run(effect, player, 0, Vector3.zero, Vector3.forward);
}
private void SetupVendingMachine(BaseEntity machine, VendingMachine comp, List<VMOrder> items) {
comp.shopName = "VEHICLE SHOP";
var usedShortnames = new Dictionary<string, string>();
foreach (var item in items) {
if (!_config.Vehicles.ContainsKey(item.VehicleKey)) continue;
var pricedef = ItemManager.FindItemDefinition(item.Shortname);
var randDef = ItemManager.itemList.Find(x => !usedShortnames.ContainsKey(x.shortname));
usedShortnames.Add(randDef.shortname, item.VehicleKey);
comp.sellOrders.sellOrders.Add(new ProtoBuf.VendingMachine.SellOrder {
itemToSellID = randDef.itemid, itemToSellAmount = 1, currencyID = pricedef.itemid, currencyAmountPerItem = item.Price, inStock = 10000, currencyIsBP = false, itemToSellIsBP = false, itemCondition = 100, itemConditionMax = 100
}
);
}
foreach (var (itemShortname, vehicle) in usedShortnames) {
var item = ItemManager.CreateByName(itemShortname, 10000, _config.Vehicles[vehicle].Skin);
item.MoveToContainer(comp.inventory);
}
VMProducts.Add(machine.net.ID, usedShortnames.Select(x => new VendingMachineCache {
RandomShortname = x.Key, VehicleKey = x.Value
}
));
VendingMachines.Add(machine);
}
internal class VendingMachineCache {
public string RandomShortname;
public string VehicleKey;
public VehicleInfoConfig Get() {
return Instance._config.Vehicles[VehicleKey];
}
}
private Dictionary<NetworkableId, IEnumerable<VendingMachineCache>> VMProducts = new();
private void DestroyVendingMachines() {
VendingMachines?.ForEach(x => x.Kill());
}
private BaseEntity SpawnVendingMachine(VendingMachinePosition position) {
return SpawnVendingMachine(position, position.transform);
}
private BaseEntity SpawnVendingMachine(VendingMachinePosition position, Transform transform) {
var entity = GameManager.server.CreateEntity(VENDINGMACHINE_PREFAB, transform.TransformPoint(position.Offset));
if (entity.TryGetComponent<StabilityEntity>(out var comp)) comp.grounded = true;
entity.OwnerID = 92929294944;
entity.Spawn();
entity.transform.rotation = transform.rotation * Quaternion.Euler(position.Rotation);
return entity;
}
private static string[] FindPrefabsOfType<T>() where T : BaseEntity {
var prefabList = Pool.Get<List<string>>();
try {
for (var i = 0; i < GameManifest.Current.entities.Length; i++) {
var entity = GameManager.server.FindPrefab(GameManifest.Current.entities[i])?.GetComponent<T>();
if (entity == null) continue;
prefabList.Add(entity.PrefabName);
}
return prefabList.ToArray();
}
finally {
Pool.FreeUnmanaged(ref prefabList);
}
}
private int GetVehiclePurchased(BasePlayer player, string key) {
return _players.TryGetValue(player.userID, out var playerData) && playerData.PurchasedVehicles.TryGetValue(key, out var purchased) ? purchased : 0;
}
private void SetVehiclePurchased(BasePlayer player, string key) {
_players.TryAdd(player.userID, new PlayerData());
_players[player.userID].PurchasedVehicles.TryAdd(key, 0);
_players[player.userID].PurchasedVehicles[key]++;
}
private void SetCooldown(BasePlayer player, string key, int cooldownTime) {
_players.TryAdd(player.userID, new PlayerData());
_players[player.userID].Cooldowns.TryAdd(key, 0);
_players[player.userID].Cooldowns[key] = Facepunch.Math.Epoch.Current + cooldownTime;
}
private int GetCooldown(BasePlayer player, string key) {
_players.TryAdd(player.userID, new PlayerData());
if (_players[player.userID].Cooldowns.TryGetValue(key, out var cooldown)) return cooldown - Facepunch.Math.Epoch.Current;
return -1;
}
private bool IsCooldown(BasePlayer player, string key) {
return GetCooldown(player, key) > 0;
}
private int ItemsCount(BasePlayer player, string shortname) {
return player.inventory.GetAmount(ItemManager.FindItemDefinition(shortname).itemid);
}
private bool CanBuy(BasePlayer player, VehicleInfoConfig product, string key) {
if (!permission.UserHasPermission(player.UserIDString, PERM_USE)) return false;
if (product.MaxPurchases > 0 && GetVehiclePurchased(player, key) >= product.MaxPurchases) {
player.ChatMessage(GetMessage(MAX_PURCHASES_EXCEEDED, player.UserIDString, product.Shortname));
return false;
}
if (permission.UserHasPermission(player.UserIDString, PERM_FREE)) return true;
return product.SellCurrency switch {
0 => ItemsCount(player, product.Shortname) >= product.Price, 1 => GetBalance(player, product) >= product.Price, 2 => GetBalance(player, product) >= product.Price, 3 => GetBalance(player, product) >= product.Price, _ => false
}
;
}
private bool Collect(BasePlayer player, VehicleInfoConfig product, string key) {
if (!CanBuy(player, product, key)) return false;
switch (product.SellCurrency) {
case 0 : player.inventory.Take(null, ItemManager.FindItemDefinition(product.Shortname).itemid, product.Price);
return true;
case 1 : case 2 : case 3 : Withdraw(player, product.Price, product.SellCurrency, product.Shortname);
return true;
default : return false;
}
}
private string GetRemainingCost(BasePlayer player, VehicleInfoConfig product) {
return product.SellCurrency switch {
0 => product.Price - ItemsCount(player, product.Shortname) + " " + ItemManager.FindItemDefinition(product.Shortname).displayName.english.ToUpper(), 1 => product.Price - GetBalance(player, product) + " " + _config.CurrencyName, 2 => product.Price - GetBalance(player, product) + " " + _config.CurrencyNameSR, 3 => product.Price - GetBalance(player, product) + " " + _config.CurrencyNameBS, _ => ""
}
;
}
private int GetBalance(BasePlayer player, VehicleInfoConfig product) {
if (product.SellCurrency == 1 && !Economics) {
PrintError("Economics plugin is not available!");
return -1;
}
if (product.SellCurrency == 2 && !ServerRewards) {
PrintError("ServerRewards plugin is not available!");
return -1;
}
if (product.SellCurrency == 3 && !BankSystem) {
PrintError("BankSystem plugin is not available!");
return -1;
}
return product.SellCurrency switch {
0 => ItemsCount(player, product.Shortname), 1 => Convert.ToInt32(Economics?.Call<double>("Balance", player.userID.Get())), 2 => Convert.ToInt32(ServerRewards?.Call("CheckPoints", player.userID.Get())), 3 => Convert.ToInt32(BankSystem?.Call("API_BankSystemBalance", player.userID.Get())), _ => throw new ArgumentOutOfRangeException()
}
;
}
private void Withdraw(BasePlayer player, int price, int type, string shortname) {
switch (type) {
case 0 : if (string.IsNullOrEmpty(shortname)) {
PrintError("Shortname in 'Withdraw' is null!");
return;
}
player.inventory.Take(null, ItemManager.FindItemDefinition(shortname).itemid, price);
break;
case 1 : Economics?.Call("Withdraw", player.userID.Get(), (double)price);
break;
case 2 : ServerRewards?.Call("TakePoints", player.userID.Get(), price);
break;
case 3 : BankSystem?.Call("API_BankSystemWithdraw", player.userID.Get(), price);
break;
}
}
private string GetCost(string key) {
if (!_config.Vehicles.ContainsKey(key)) return "";
var product = _config.Vehicles.FirstOrDefault(x => x.Key == key);
switch (product.Value.SellCurrency) {
case 0 : {
var itemDef = ItemManager.FindItemDefinition(product.Value.Shortname);
return itemDef == null ? "UNKNOWN ITEM" : $"{product.Value.Price} {itemDef.displayName.english.ToUpper()}";
}
case 1 : return $"{product.Value.Price} {_config.CurrencyName}";
case 2 : return $"{product.Value.Price} {_config.CurrencyNameSR}";
case 3 : return $"{product.Value.Price} {_config.CurrencyNameBS}";
default : return "ERROR CHECK CFG";
}
}
#region Working with Images
private Dictionary<string, string> _loadedImages = new Dictionary<string, string>();
private void AddImage(string url, string fileName, ulong imageId = 0) {
#if CARBON
imageDatabase.Queue(true, new Dictionary<string, string> {
[fileName]
= url
}
);
#else
ImageLibrary?.Call("AddImage", url, fileName, imageId);
#endif
}
private string GetImage(string name) {
if (_loadedImages.TryGetValue(name, out var imageID)) return imageID;
#if CARBON
return imageDatabase.GetImageString(name);
#else
return Convert.ToString(ImageLibrary?.Call("GetImage", name));
#endif
}
private bool HasImage(string name) {
#if CARBON
return Convert.ToBoolean(imageDatabase.HasImage(name));
#else
return Convert.ToBoolean(ImageLibrary?.Call("HasImage", name));
#endif
}
private void LoadImages() {
#if CARBON
imageDatabase = BaseModule.GetModule<ImageDatabaseModule>();
#endif
_enabledImageLibrary = true;
var imagesList = new Dictionary<string, string>();
foreach (var (prefab, vehicleSettings) in _config.Vehicles) RegisterImage(ref imagesList, prefab + ".image", vehicleSettings.Image);
foreach (var (name, url) in imagesList.ToArray()) {
if (!url.StartsWith("MeventImages/")) continue;
LoadImageFromFS(name, url);
imagesList.Remove(name);
}
if (imagesList.Count <= 0) return;
#if CARBON
imageDatabase.Queue(false, imagesList);
#else
timer.In(1f, () => {
if (ImageLibrary is not {
IsLoaded : true
}
) {
_enabledImageLibrary = false;
BroadcastILNotInstalled();
return;
}
ImageLibrary?.Call("ImportImageList", Title, imagesList, 0UL, true);
}
);
#endif
}
private void RegisterImage(ref Dictionary<string, string> images, string name, string image) {
if (string.IsNullOrEmpty(image) || string.IsNullOrEmpty(name)) return;
images.TryAdd(name, image);
}
private void BroadcastILNotInstalled() {
for (var i = 0; i < 5; i++) PrintError("IMAGE LIBRARY IS NOT INSTALLED.");
}
private void LoadImageFromFS(string name, string path) {
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(path)) return;
ServerMgr.Instance.StartCoroutine(LoadImage(name, path));
}
private IEnumerator LoadImage(string name, string path) {
var url = "file://" + Interface.Oxide.DataDirectory + Path.DirectorySeparatorChar + path;
using var www = UnityWebRequestTexture.GetTexture(url);
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success) {
Debug.LogError($"Image not found: {path}");
}
else {
var texture = DownloadHandlerTexture.GetContent(www);
try {
var image = texture.EncodeToPNG();
_loadedImages.TryAdd(name, FileStorage.server.Store(image, FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID).ToString());
}
finally {
UnityEngine.Object.DestroyImmediate(texture);
}
}
}
#endregion
private void RegisterPermissions() {
permission.RegisterPermission(PERM_USE, this);
permission.RegisterPermission(PERM_FREE, this);
permission.RegisterPermission(PERM_PICKUP, this);
permission.RegisterPermission(PERM_RECALL, this);
foreach (var vehicleInfo in _config.Vehicles.Values) if (!string.IsNullOrEmpty(vehicleInfo.Permission) && !permission.PermissionExists(vehicleInfo.Permission)) permission.RegisterPermission(vehicleInfo.Permission, this);
}
public void RegisterCommands() {
foreach (var x in _config.Commands) cmd.AddChatCommand(x, this, cmdVehicleBuy);
foreach (var vehicle in _config.Vehicles) AddCovalenceCommand(vehicle.Value.Command, nameof(CmdAddVehicle));
}
private bool GiveVehicle(ItemContainer container, VehicleInfoConfig vehicleSettings, bool needFuel = true) {
var item = ItemManager.CreateByItemID(vehicleSettings.DeployableItemId, 1, vehicleSettings.Skin);
item.name = vehicleSettings.Name;
return item.MoveToContainer(container, -1, false);
}
private void GetOutParts(ModularCar car) {
foreach (var child in car.children) {
var engineModule = child as VehicleModuleEngine;
if (engineModule == null) continue;
var engineStorage = engineModule.GetContainer() as EngineStorage;
if (engineStorage == null) continue;
engineStorage.inventory.Clear();
engineStorage.SendNetworkUpdate();
}
}
private void AddEngineParts(ModularCar car, List<string> shortnames) {
foreach (var child in car.children) {
var engineModule = child as VehicleModuleEngine;
if (engineModule == null) continue;
var engineStorage = engineModule.GetContainer() as EngineStorage;
if (engineStorage == null || !engineStorage.inventory.IsEmpty()) continue;
foreach (var x in shortnames) AddPartsToEngineStorage(engineStorage, x);
engineModule.RefreshPerformanceStats(engineStorage);
}
}
private void AddPartsToEngineStorage(EngineStorage engineStorage, string shortname) {
if (engineStorage.inventory == null) return;
var inventory = engineStorage.inventory;
for (var i = 0; i < inventory.capacity; i++) {
var item = inventory.GetSlot(i);
if (item != null) continue;
// if (tier > 0)
// {
TryAddEngineItem(engineStorage, -1, shortname);
// }
}
}
private bool TryAddEngineItem(EngineStorage engineStorage, int slot, string shortname) {
var component = ItemManager.FindItemDefinition(shortname);
var item = ItemManager.Create(component);
if (item == null) return false;
item.MoveToContainer(engineStorage.inventory, slot, false);
return true;
}
private bool GiveVehicle(BasePlayer player, VehicleInfoConfig vehicleSettings, bool needfuel = true) {
var item = ItemManager.CreateByItemID(vehicleSettings.DeployableItemId, 1, vehicleSettings.Skin);
item.name = vehicleSettings.Name + (needfuel ? "" : " ");
if (!player.inventory.GiveItem(item)) {
item.Drop(player.inventory.containerMain.dropPosition, player.inventory.containerMain.dropVelocity);
return false;
}
return true;
}
private BaseEntity GetMinDistance(Vector3 position, IEnumerable<BaseEntity> entities) {
BaseEntity result = null;
var min = float.PositiveInfinity;
var ents = entities.ToArray();
foreach (var t in ents) {
var dist = Vector3.Distance(position, t.transform.position);
// ... [Code preview truncated due to length. Full source in attached file] ...