C#:
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("FHorseNpcVendor", "fufeldev", "1.0.0")]
public class FHorseNpcVendor : RustPlugin
{
private const string PermAdmin = "fhorsenpcvendor.admin";
private const string NpcPrefab = "assets/prefabs/npc/bandit/shopkeepers/stables_shopkeeper.prefab";
private const string MachinePrefab = "assets/prefabs/deployable/vendingmachine/npcvendingmachines/shopkeeper_vm_invis.prefab";
private const string MachinePrefabAlt = "Assets/Prefabs/Deployable/VendingMachine/NPCVendingMachines/Shopkeeper_VM_Invis.prefab";
private const int StartingStock = 100;
private StoredData _data;
private NPCShopKeeper _npc;
private InvisibleVendingMachine _machine;
private class StoredData
{
public PositionData Spawn;
}
private class PositionData
{
public float X;
public float Y;
public float Z;
public float RotY;
}
private void Init()
{
permission.RegisterPermission(PermAdmin, this);
LoadData();
}
private void OnServerInitialized()
{
SpawnNpc();
}
private void Unload()
{
KillNpc();
}
private void OnVendingShopOpen(VendingMachine vending, BasePlayer player)
{
}
private void OnVendingTransaction(VendingMachine vending, BasePlayer buyer, int sellOrderId, int numberOfTransactions, ItemContainer targetContainer)
{
}
private object OnBuyVendingItem(VendingMachine vending, BasePlayer player, int sellOrderId, int numberOfTransactions)
{
if (!IsOurMachine(vending) || player == null) return null;
var realSellOrderId = GetRealSellOrderId(_machine, sellOrderId);
if (realSellOrderId < 0) return true;
_machine.DoTransaction(player, realSellOrderId, numberOfTransactions, player.inventory.containerMain);
Restock(_machine);
return true;
}
private object OnTakeCurrencyItem(VendingMachine vending, Item takenCurrencyItem)
{
if (!IsOurMachine(vending) || takenCurrencyItem == null) return null;
takenCurrencyItem.Remove();
return true;
}
[ChatCommand("horsenpc")]
private void CmdHorseNpc(BasePlayer player, string command, string[] args)
{
if (player == null || !player.IsAdmin && !permission.UserHasPermission(player.UserIDString, PermAdmin)) return;
if (args.Length == 0)
{
SendReply(player, "/horsenpc set - поставить продавца\n/horsenpc remove - удалить продавца\n/horsenpc respawn - переспавнить продавца");
return;
}
if (args[0] == "set")
{
_data.Spawn = new PositionData
{
X = player.transform.position.x,
Y = player.transform.position.y,
Z = player.transform.position.z,
RotY = player.eyes.rotation.eulerAngles.y
};
SaveData();
SpawnNpc();
SendReply(player, "Продавец Ranch поставлен.");
return;
}
if (args[0] == "remove")
{
_data.Spawn = null;
SaveData();
KillNpc();
SendReply(player, "Продавец Ranch удалён.");
return;
}
if (args[0] == "respawn")
{
SpawnNpc();
SendReply(player, "Продавец Ranch переспавнен.");
}
}
private void SpawnNpc()
{
KillNpc();
if (_data.Spawn == null) return;
var position = new Vector3(_data.Spawn.X, _data.Spawn.Y, _data.Spawn.Z);
var rotation = Quaternion.Euler(0f, _data.Spawn.RotY, 0f);
_machine = GameManager.server.CreateEntity(MachinePrefab, position, rotation) as InvisibleVendingMachine;
if (_machine == null) _machine = GameManager.server.CreateEntity(MachinePrefabAlt, position, rotation) as InvisibleVendingMachine;
if (_machine == null)
{
PrintError("Не удалось создать Shopkeeper_VM_Invis.prefab");
return;
}
_machine.enableSaving = false;
_machine.StartingStock = StartingStock;
_machine.RefillTime = 1f;
_machine.BypassDynamicPricing = true;
_machine.Spawn();
_npc = GameManager.server.CreateEntity(NpcPrefab, position, rotation) as NPCShopKeeper;
if (_npc == null)
{
PrintError("Не удалось создать stables_shopkeeper.prefab");
KillNpc();
return;
}
_npc.enableSaving = false;
_npc.Spawn();
_npc.transform.rotation = rotation;
_npc.EndSleeping();
_npc.SetAimDirection(rotation * Vector3.forward);
_npc.SendNetworkUpdateImmediate();
_npc.machine = _machine;
_npc.invisibleVendingMachineRef.Set(_machine);
_machine.SetAttachedNPC(_npc);
timer.Once(1.2f, () => InstallRanchShop(_machine));
}
private void InstallRanchShop(InvisibleVendingMachine machine)
{
if (machine == null || machine.IsDestroyed) return;
machine.StartingStock = StartingStock;
machine.RefillTime = 1f;
machine.BypassDynamicPricing = true;
if (machine.sellOrders == null || machine.sellOrders.sellOrders == null) machine.InstallDefaultSellOrders();
machine.CancelInvoke(machine.InstallFromVendingOrders);
machine.CancelInvoke(machine.Refill);
machine.ClearSellOrders();
machine.inventory.Clear();
machine.inventory.capacity = 300;
ItemManager.DoRemoves();
AddOrder(machine, "horse.saddle.single", 1, 150);
AddOrder(machine, "horse.shoes.basic", 1, 100);
AddOrder(machine, "horse.shoes.advanced", 1, 250);
machine.RefreshSellOrderStockLevel();
machine.SendSellOrders();
machine.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
}
private void Restock(InvisibleVendingMachine machine)
{
if (machine == null || machine.IsDestroyed) return;
machine.transactionActive = true;
EnsureItem(machine, "horse.saddle.single", StartingStock);
EnsureItem(machine, "horse.shoes.basic", StartingStock);
EnsureItem(machine, "horse.shoes.advanced", StartingStock);
machine.transactionActive = false;
machine.RefreshSellOrderStockLevel();
machine.SendSellOrders();
machine.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
}
private void EnsureItem(InvisibleVendingMachine machine, string shortName, int stockMultiplier)
{
var def = ItemManager.FindItemDefinition(shortName);
if (def == null) return;
int target = stockMultiplier;
int current = 0;
if (machine.inventory != null)
{
for (int i = 0; i < machine.inventory.itemList.Count; i++)
{
var item = machine.inventory.itemList[i];
if (item != null && item.info == def) current += item.amount;
}
}
if (current >= target) return;
int toAdd = target - current;
machine.inventory.AddItem(def, toAdd, 0UL, ItemContainer.LimitStack.All);
}
private void AddOrder(InvisibleVendingMachine machine, string shortName, int amount, int price)
{
var sellDefinition = ItemManager.FindItemDefinition(shortName);
var scrapDefinition = ItemManager.FindItemDefinition("scrap");
if (sellDefinition == null || scrapDefinition == null)
{
PrintWarning($"Не найден предмет для продажи: {shortName}");
return;
}
var order = new ProtoBuf.VendingMachine.SellOrder();
order.ShouldPool = false;
order.itemToSellID = sellDefinition.itemid;
order.itemToSellAmount = amount;
order.currencyID = scrapDefinition.itemid;
order.currencyAmountPerItem = price;
order.currencyIsBP = false;
order.itemToSellIsBP = false;
order.priceMultiplier = 1f;
order.receivedQuantityMultiplier = 1f;
machine.sellOrders.sellOrders.Add(order);
machine.transactionActive = true;
machine.inventory.AddItem(sellDefinition, amount * StartingStock, 0UL, ItemContainer.LimitStack.All);
machine.transactionActive = false;
}
private bool IsOurMachine(VendingMachine vending)
{
return _machine != null && vending != null && vending.EqualNetID(_machine);
}
private int GetRealSellOrderId(VendingMachine vending, int visibleSellOrderId)
{
if (vending == null || vending.sellOrders == null || vending.sellOrders.sellOrders == null) return -1;
var visibleIndex = 0;
for (int i = 0; i < vending.sellOrders.sellOrders.Count; i++)
{
var sellOrder = vending.sellOrders.sellOrders[i];
var itemDefinition = ItemManager.FindItemDefinition(sellOrder.itemToSellID);
var currencyDefinition = ItemManager.FindItemDefinition(sellOrder.currencyID);
if (itemDefinition == null || currencyDefinition == null) continue;
if (!itemDefinition.IsAllowed(vending.CurrentEraRestriction) || !currencyDefinition.IsAllowed(vending.CurrentEraRestriction)) continue;
if (visibleIndex == visibleSellOrderId) return i;
visibleIndex++;
}
return -1;
}
private void KillNpc()
{
if (_npc != null && !_npc.IsDestroyed) _npc.Kill();
if (_machine != null && !_machine.IsDestroyed) _machine.Kill();
_npc = null;
_machine = null;
}
private void LoadData()
{
_data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
if (_data == null) _data = new StoredData();
}
private void SaveData()
{
Interface.Oxide.DataFileSystem.WriteObject(Name, _data);
}
}
}