C#:
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("SpawnVehicles", "spunK", "2.0.0")]
[Description("Allow players to spawn vehicles via chat commands with payment system integration.")]
public class SpawnVehicles : CovalencePlugin
{
#region Fields
[PluginReference]
private Plugin Economics, ServerRewards;
private ConfigData _config;
private Dictionary<ulong, Dictionary<string, DateTime>> _cooldowns = new Dictionary<ulong, Dictionary<string, DateTime>>();
#endregion
#region Configuration
private class ConfigData
{
[JsonProperty("Require Building Privilege")]
public bool RequireBuildingPrivilege { get; set; } = true;
[JsonProperty("Check for Building Blocked")]
public bool CheckBuildingBlocked { get; set; } = true;
[JsonProperty("Default Payment Type (none, scrap, economics, serverrewards)")]
public string DefaultPaymentType { get; set; } = "scrap";
[JsonProperty("Spawn Distance From Player")]
public float SpawnDistance { get; set; } = 3f;
[JsonProperty("Spawn Height Offset")]
public float SpawnHeightOffset { get; set; } = 0.5f;
[JsonProperty("Vehicles")]
public Dictionary<string, VehicleConfig> Vehicles { get; set; } = new Dictionary<string, VehicleConfig>
{
["bike"] = new VehicleConfig
{
DisplayName = "Motorbike",
EntityPrefab = "assets/content/vehicles/bikes/motorbike.prefab",
SpawnWithFuel = false,
FuelAmount = 0,
Cooldown = 120f,
Costs = new Dictionary<string, int>
{
["scrap"] = 50,
["economics"] = 250,
["serverrewards"] = 125
}
},
["mini"] = new VehicleConfig
{
DisplayName = "Minicopter",
EntityPrefab = "assets/content/vehicles/minicopter/minicopter.entity.prefab",
SpawnWithFuel = true,
FuelAmount = 100,
Cooldown = 600f,
Costs = new Dictionary<string, int>
{
["scrap"] = 500,
["economics"] = 2500,
["serverrewards"] = 1250
}
},
["scrappy"] = new VehicleConfig
{
DisplayName = "Scrap Transport Helicopter",
EntityPrefab = "assets/content/vehicles/scrap heli carrier/scraptransporthelicopter.prefab",
SpawnWithFuel = true,
FuelAmount = 100,
Cooldown = 600f,
Costs = new Dictionary<string, int>
{
["scrap"] = 750,
["economics"] = 3750,
["serverrewards"] = 1875
}
},
["attackheli"] = new VehicleConfig
{
DisplayName = "Attack Helicopter",
EntityPrefab = "assets/content/vehicles/attackhelicopter/attackhelicopter.entity.prefab",
SpawnWithFuel = true,
FuelAmount = 100,
Cooldown = 900f,
Costs = new Dictionary<string, int>
{
["scrap"] = 2250,
["economics"] = 5000,
["serverrewards"] = 2500
}
},
["rhib"] = new VehicleConfig
{
DisplayName = "RHIB",
EntityPrefab = "assets/content/vehicles/boats/rhib/rhib.prefab",
SpawnWithFuel = true,
FuelAmount = 50,
Cooldown = 300f,
Costs = new Dictionary<string, int>
{
["scrap"] = 300,
["economics"] = 1000,
["serverrewards"] = 500
}
},
["boat"] = new VehicleConfig
{
DisplayName = "Rowboat",
EntityPrefab = "assets/content/vehicles/boats/rowboat/rowboat.prefab",
SpawnWithFuel = true,
FuelAmount = 50,
Cooldown = 180f,
Costs = new Dictionary<string, int>
{
["scrap"] = 125,
["economics"] = 500,
["serverrewards"] = 250
}
},
["snowmobile"] = new VehicleConfig
{
DisplayName = "Snowmobile",
EntityPrefab = "assets/content/vehicles/snowmobiles/snowmobile.prefab",
SpawnWithFuel = true,
FuelAmount = 50,
Cooldown = 240f,
Costs = new Dictionary<string, int>
{
["scrap"] = 150,
["economics"] = 750,
["serverrewards"] = 375
}
},
["horse"] = new VehicleConfig
{
DisplayName = "Horse",
EntityPrefab = "assets/content/vehicles/horse/ridablehorse.prefab",
Cooldown = 180f,
Costs = new Dictionary<string, int>
{
["scrap"] = 75,
["economics"] = 375,
["serverrewards"] = 188
}
}
};
public string ToJson() => JsonConvert.SerializeObject(this);
public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson());
}
private class VehicleConfig
{
[JsonProperty("Display Name")]
public string DisplayName { get; set; }
[JsonProperty("Entity Prefab")]
public string EntityPrefab { get; set; }
[JsonProperty("Spawn With Fuel")]
public bool SpawnWithFuel { get; set; } = true;
[JsonProperty("Fuel Amount")]
public int FuelAmount { get; set; } = 100;
[JsonProperty("Cooldown (seconds)")]
public float Cooldown { get; set; } = 300f;
[JsonProperty("Costs (payment type: amount)")]
public Dictionary<string, int> Costs { get; set; } = new Dictionary<string, int>();
}
protected override void LoadDefaultConfig() => _config = new ConfigData();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
_config = Config.ReadObject<ConfigData>();
if (_config == null)
{
throw new JsonException();
}
if (!_config.ToDictionary().Keys.SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys))
{
LogWarning("Configuration appears to be outdated; updating and saving");
SaveConfig();
}
}
catch (Exception ex)
{
LogWarning($"Configuration file {Name}.json is invalid; using defaults. Error: {ex.Message}");
LoadDefaultConfig();
}
}
protected override void SaveConfig()
{
Log($"Configuration changes saved to {Name}.json");
Config.WriteObject(_config, true);
}
#endregion
#region Localization
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["NoPermission"] = "You do not have permission to use this command.",
["NoPermissionVehicle"] = "You do not have permission to spawn a {0}.",
["SpawnedVehicle"] = "You have spawned a {0}.",
["NeedBuildingPrivilege"] = "You need building privilege to spawn vehicles here.",
["BuildingBlocked"] = "You cannot spawn vehicles in a building blocked area.",
["VehicleNotFound"] = "Vehicle '{0}' not found in configuration.",
["FailedToSpawn"] = "Failed to spawn {0}. Please try again.",
["OnCooldown"] = "You must wait <color=yellow>{0}</color> seconds before spawning another {1}.",
["InsufficientFunds"] = "You need <color=yellow>{0}</color> {1} to spawn a {2}.",
["PaymentDeducted"] = "You paid {0} {1} to spawn a {2}.",
["InvalidPaymentType"] = "Invalid payment type '{0}'. Valid types: none, scrap, economics, serverrewards",
["PluginNotLoaded"] = "The {0} plugin is not loaded. Payment type has been set to 'none'.",
["VehicleList"] = "Available vehicles:\n{0}",
["VehicleListItem"] = "- /{0} - {1} ({2})",
["Free"] = "Free"
}, this);
}
private string Lang(string key, string id = null, params object[] args)
{
return string.Format(lang.GetMessage(key, this, id), args);
}
#endregion
#region Hooks
private void Init()
{
permission.RegisterPermission("spawnvehicles.free", this);
permission.RegisterPermission("spawnvehicles.nocooldown", this);
foreach (var vehicleKey in _config.Vehicles.Keys)
{
permission.RegisterPermission($"spawnvehicles.{vehicleKey}", this);
}
AddCovalenceCommand("vehicles", nameof(CmdVehicleList));
foreach (var vehicleKey in _config.Vehicles.Keys)
{
AddCovalenceCommand(vehicleKey, nameof(CmdSpawnVehicle));
}
}
private void Unload()
{
_cooldowns.Clear();
}
#endregion
#region Commands
private void CmdVehicleList(IPlayer player, string command, string[] args)
{
var vehicleList = _config.Vehicles
.Select(v =>
{
var cost = GetCostString(v.Value, player.Id);
return Lang("VehicleListItem", player.Id, v.Key, v.Value.DisplayName, cost);
});
player.Reply(Lang("VehicleList", player.Id, string.Join("\n", vehicleList)));
}
private void CmdSpawnVehicle(IPlayer player, string command, string[] args)
{
var vehicleKey = command.TrimStart('/');
if (!_config.Vehicles.TryGetValue(vehicleKey, out var vehicleConfig))
{
ShowNotify(player, Lang("VehicleNotFound", player.Id, vehicleKey));
return;
}
if (!HasPermission(player, $"spawnvehicles.{vehicleKey}"))
{
ShowNotify(player, Lang("NoPermissionVehicle", player.Id, vehicleConfig.DisplayName));
return;
}
var basePlayer = player.Object as BasePlayer;
if (basePlayer == null)
{
return;
}
// Check building privilege
if (_config.RequireBuildingPrivilege && !basePlayer.IsBuildingAuthed() && !basePlayer.HasPrivilegeFromOther())
{
ShowNotify(player, Lang("NeedBuildingPrivilege", player.Id));
return;
}
// Check building blocked
if (_config.CheckBuildingBlocked && !basePlayer.CanBuild())
{
ShowNotify(player, Lang("BuildingBlocked", player.Id));
return;
}
// Check cooldown
if (!HasPermission(player, "spawnvehicles.nocooldown"))
{
if (IsOnCooldown(basePlayer.userID, vehicleKey, out var remainingTime))
{
ShowNotify(player, Lang("OnCooldown", player.Id, Math.Ceiling(remainingTime), vehicleConfig.DisplayName));
return;
}
}
// Handle payment
if (!HasPermission(player, "spawnvehicles.free"))
{
if (!TryTakePayment(basePlayer, vehicleConfig, out var paymentMessage))
{
ShowNotify(player, paymentMessage);
return;
}
}
// Spawn vehicle
if (!SpawnVehicle(basePlayer, vehicleKey, vehicleConfig))
{
ShowNotify(player, Lang("FailedToSpawn", player.Id, vehicleConfig.DisplayName));
return;
}
// Set cooldown
if (!HasPermission(player, "spawnvehicles.nocooldown"))
{
SetCooldown(basePlayer.userID, vehicleKey, vehicleConfig.Cooldown);
}
ShowNotify(player, Lang("SpawnedVehicle", player.Id, vehicleConfig.DisplayName));
}
#endregion
#region Core Methods
private bool SpawnVehicle(BasePlayer player, string vehicleKey, VehicleConfig vehicleConfig)
{
var spawnPosition = player.transform.position + player.transform.forward * _config.SpawnDistance;
spawnPosition.y += _config.SpawnHeightOffset;
var rotation = Quaternion.Euler(0, player.transform.rotation.eulerAngles.y, 0);
var entity = GameManager.server.CreateEntity(vehicleConfig.EntityPrefab, spawnPosition, rotation);
if (entity == null)
{
return false;
}
entity.OwnerID = player.userID;
entity.Spawn();
if (vehicleConfig.SpawnWithFuel && vehicleConfig.FuelAmount > 0)
{
var vehicle = entity as BaseVehicle;
if (vehicle != null)
{
IFuelSystem fuelSystem = vehicle.GetFuelSystem();
if (fuelSystem != null) fuelSystem.AddFuel(vehicleConfig.FuelAmount);
}
}
return true;
}
private bool TryTakePayment(BasePlayer player, VehicleConfig vehicleConfig, out string message)
{
message = string.Empty;
var paymentType = _config.DefaultPaymentType.ToLower();
if (!vehicleConfig.Costs.TryGetValue(paymentType, out var cost))
{
return true;
}
if (cost <= 0)
{
return true;
}
switch (paymentType)
{
case "none":
return true;
case "scrap":
var scrapAmount = player.inventory.GetAmount(-932201673);
if (scrapAmount < cost)
{
message = Lang("InsufficientFunds", player.UserIDString, cost, "scrap", vehicleConfig.DisplayName);
return false;
}
player.inventory.Take(null, -932201673, cost);
return true;
case "economics":
if (Economics == null)
{
message = Lang("PluginNotLoaded", player.UserIDString, "Economics");
return true;
}
var balance = Economics.Call<double>("Balance", player.userID);
if (balance < cost)
{
message = Lang("InsufficientFunds", player.UserIDString, cost, "coins", vehicleConfig.DisplayName);
return false;
}
Economics.Call("Withdraw", player.userID, (double)cost);
return true;
case "serverrewards":
if (ServerRewards == null)
{
message = Lang("PluginNotLoaded", player.UserIDString, "ServerRewards");
return true;
}
var points = ServerRewards.Call<int>("CheckPoints", player.userID);
if (points < cost)
{
message = Lang("InsufficientFunds", player.UserIDString, cost, "RP", vehicleConfig.DisplayName);
return false;
}
ServerRewards.Call("TakePoints", player.userID, cost);
return true;
default:
message = Lang("InvalidPaymentType", player.UserIDString, paymentType);
return false;
}
}
private string GetCostString(VehicleConfig vehicleConfig, string playerId)
{
var paymentType = _config.DefaultPaymentType.ToLower();
if (!vehicleConfig.Costs.TryGetValue(paymentType, out var cost) || cost <= 0 || paymentType == "none")
{
return Lang("Free", playerId);
}
var currencyName = paymentType switch
{
"scrap" => "scrap",
"economics" => "coins",
"serverrewards" => "RP",
_ => paymentType
};
return $"{cost} {currencyName}";
}
private void ShowNotify(IPlayer player, string message)
{
var basePlayer = player.Object as BasePlayer;
if (basePlayer == null)
{
return;
}
basePlayer.ShowToast(GameTip.Styles.Server_Event, message);
}
#endregion
#region Cooldown Management
private bool IsOnCooldown(ulong userId, string vehicleKey, out double remainingTime)
{
remainingTime = 0;
if (!_cooldowns.ContainsKey(userId))
{
return false;
}
if (!_cooldowns[userId].ContainsKey(vehicleKey))
{
return false;
}
var cooldownEnd = _cooldowns[userId][vehicleKey];
if (DateTime.UtcNow >= cooldownEnd)
{
_cooldowns[userId].Remove(vehicleKey);
return false;
}
remainingTime = (cooldownEnd - DateTime.UtcNow).TotalSeconds;
return true;
}
private void SetCooldown(ulong userId, string vehicleKey, float cooldownSeconds)
{
if (!_cooldowns.ContainsKey(userId))
{
_cooldowns[userId] = new Dictionary<string, DateTime>();
}
_cooldowns[userId][vehicleKey] = DateTime.UtcNow.AddSeconds(cooldownSeconds);
}
#endregion
#region Helpers
private bool HasPermission(IPlayer player, string perm)
{
return permission.UserHasPermission(player.Id, perm);
}
#endregion
}
}