C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using JetBrains.Annotations;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using ProtoBuf;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using UnityEngine;
/*
* VERSION HISTORY
*
* V 1.0.1
* - fix null reference exception when loading
* - add config option do disable shop location
* - add default popular items
* - fix ui with chat command
* - add config option for chat command
* - change default command to /market to avoid conflicts
*
* V 1.0.2
* - fix incorrect amount displayed in shop view
* - fix possible NRE in AddMarker
* - change file name to prevent conflict with global::Marketplace
* - add lang support
* - add Translation API for item names
*
* V 1.0.3
* - change overlay to non scaled
* - move close button away from hostile marker
*
* V 1.0.4
* - initialize search in OnServerInitialized to make sure item list is loaded
*
* V 1.0.5
* - fix for December rust update
*
*/
namespace Oxide.Plugins
{
[Info(nameof(MarketplaceUi), "The_Kiiiing", "1.0.5")]
internal class MarketplaceUi : RustPlugin
{
#region Fields
private const string PERM_USE = "marketplaceui.use";
private const string UI_PANEL = "marketplaceui.ui";
private const string UI_COMMAND = "marketplaceui.cmd";
private static MarketplaceUi _instance;
private readonly List<ulong> openUis = new();
private readonly Dictionary<ulong, VendingOffer.SortMode> playerSortModes = new();
private DataStore dataStore;
private ItemSearch search;
[PluginReference, UsedImplicitly]
private readonly Plugin RustTranslationAPI;
#endregion
#region Configuration
private static Configuration _config;
private class Configuration
{
[JsonProperty("Show vending machine owner")]
public bool showShopOwner = true;
[JsonProperty("Show vending machine grid position")]
public bool showGridPos = true;
[JsonProperty("Enable access via chat command")]
public bool enableCommand = true;
[JsonProperty("Chat command")]
public string chatCommand = "market";
[JsonProperty("Enable access via computer station")]
public bool enableStation = true;
}
protected override void LoadConfig()
{
base.LoadConfig();
try
{
_config = Config.ReadObject<Configuration>();
if (_config == null) throw new Exception();
SaveConfig();
}
catch
{
PrintError("Your configuration file contains an error.Using default configuration values.");
LoadDefaultConfig();
}
}
protected override void SaveConfig() => Config.WriteObject(_config);
protected override void LoadDefaultConfig() => _config = new Configuration();
#endregion
#region Data
private class DataStore
{
[JsonProperty]
private Dictionary<int, int> topItems = new Dictionary<int, int>
{
{ -932201673 , 1 },
{ -946369541 , 1 },
{ 69511070 , 1 },
{ -1812555177 , 1 },
{ -1581843485 , 1 },
{ -1157596551 , 1 },
};
public void AddSearch(int itemid)
{
if (topItems.ContainsKey(itemid))
topItems[itemid]++;
else
topItems[itemid] = 1;
}
private int GetSearchScore(int itemid)
{
if (topItems.ContainsKey(itemid))
return topItems[itemid];
return 0;
}
public IOrderedEnumerable<int> SortItemsByScore(IEnumerable<int> input)
{
return input.OrderByDescending(GetSearchScore);
}
public IEnumerable<int> GetPopularItems(int amount)
{
//int count = amount > topItems.Count ? topItems.Count : amount;
var items = topItems.OrderByDescending(x => x.Value).Select(x => x.Key).Take(amount);
return items;
//if (amount < items.Count())
//{
// items.RemoveRange(amount, items.Count() - amount);
//}
//return items.OrderBy(x => 1);
}
}
private void LoadData()
{
var data = Interface.Oxide.DataFileSystem.ReadObject<DataStore>(Name);
if (data == null) data = new DataStore();
dataStore = data;
}
private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject(Name, dataStore);
#endregion
#region Core
private class VendingCache
{
public static VendingCache Instance { get; } = new VendingCache();
private DateTime time;
private readonly List<Shop> shopCache;
public bool IsEmpty => shopCache.IsEmpty();
private VendingCache()
{
time = DateTime.Now;
shopCache = new List<Shop>();
}
public VendingOffer FindOfferByGuid(string guid)
{
var ids = UnwrapGuid(guid);
var shop = shopCache.Find(x => x.id == ids[0]);
var offer = shop.FindOfferById(ids[1]);
return offer;
}
public Shop FindShopByGuid(string guid)
{
var ids = UnwrapGuid(guid);
return shopCache.Find(x => x.id == ids[0]);
}
private ulong[] UnwrapGuid(string guid)
{
var x = guid.Split(':');
return new ulong[] { UInt64.Parse(x[0]), UInt64.Parse(x[1]) };
}
public IEnumerable<VendingOffer> FindOffers(int itemid)
{
if (Mathf.Abs((float)DateTime.Now.Subtract(time).TotalMinutes) > 1)
{
Refresh();
}
var offers = new List<VendingOffer>();
foreach (var s in shopCache)
{
offers.AddRange(Array.FindAll(s.offers, x => x.offer.ItemId == itemid));
}
return offers;
}
public void Refresh(Action callback = null)
{
InvokeHandler.Instance.StartCoroutine(RefreshCoro(callback));
}
private IEnumerator RefreshCoro(Action callback)
{
DPrint($"refresh shops");
shopCache.Clear();
foreach (var vm in BaseNetworkable.serverEntities.OfType<VendingMachine>())
{
if (vm.net == null || !vm.IsBroadcasting())
{
continue;
}
// if (false && vm.OwnerID == 0) continue;
shopCache.Add(new Shop(vm));
}
time = DateTime.Now;
callback?.Invoke();
yield break;
}
}
private class VendingItemAmount
{
public int ItemId { get; }
public int Amount { get; }
public VendingItemAmount(int itemid, int amount)
{
ItemId = itemid;
Amount = amount;
}
}
private class Shop
{
public ulong id;
public VendingOffer[] offers;
public string shopName;
public Vector3 position;
public ulong ownerId;
public bool IsNpc => ownerId == 0UL;
public string GridPosition => MapHelper.PositionToString(position);
public Shop(VendingMachine vm)
{
id = vm.net.ID.Value;
shopName = vm.shopName;
position = vm.transform.position;
ownerId = vm.OwnerID;
var offerList = Pool.Get<List<VendingOffer>>();
uint oid = 0;
foreach (var offer in vm.sellOrders.sellOrders)
{
var vo = new VendingOffer(this, oid, offer.inStock, offer.itemToSellID, offer.itemToSellAmount, offer.currencyID, offer.currencyAmountPerItem);
offerList.Add(vo);
oid++;
}
offers = offerList.ToArray();
Pool.FreeUnmanaged(ref offerList);
}
public VendingOffer FindOfferById(ulong id)
{
return offers[id];
}
public int GetDistance(BasePlayer player)
{
return Mathf.RoundToInt(Vector3.Distance(player.transform.position, position));
}
public string SerializePosition()
{
return $"{position.x:N1}:{position.z:N1}";
}
public static Vector3 DeserializePosition(string serialized)
{
string[] split = serialized.Split(':');
if (split.Length != 2)
{
return default(Vector3);
}
return new Vector3(Single.Parse(split[0]), 0, Single.Parse(split[1]));
}
}
private class VendingOffer
{
public Shop Shop { get; }
public uint OfferId { get; }
public string Guid { get; }
public VendingItemAmount offer;
public VendingItemAmount cost;
public int TotalStock => stock * offer.Amount;
public int stock;
public VendingOffer(Shop shop, uint id, int stock, int itemid, int amount, int currency_itemid, int currency_amt)
{
this.Shop = shop;
this.OfferId = id;
this.stock = stock;
offer = new VendingItemAmount(itemid, amount);
cost = new VendingItemAmount(currency_itemid, currency_amt);
Guid = $"{Shop.id}:{OfferId}";
}
public int GetDistance(BasePlayer player)
{
return Mathf.RoundToInt(Vector3.Distance(player.transform.position, Shop.position));
}
public enum SortMode { Distance, Price, Amount }
public static IOrderedEnumerable<VendingOffer> SortOffers(BasePlayer player, IEnumerable<VendingOffer> offers)
{
var mode = GetPlayerSortMode(player);
if (mode == SortMode.Distance)
return offers.OrderBy(x => x.GetDistance(player));
else if (mode == SortMode.Price)
return offers.OrderBy(x => x.cost.Amount);
else if (mode == SortMode.Amount)
return offers.OrderByDescending(x => (x.offer.Amount * x.stock));
return (IOrderedEnumerable<VendingOffer>)offers;
}
}
#endregion
#region Hooks
private void Init()
{
_instance = this;
permission.RegisterPermission(PERM_USE, this);
RegisterLangMessages();
timer.In(5f, () =>
{
if (RustTranslationAPI == null)
{
PrintWarning("RustTranslationAPI is not installed, item names will always be displayed in english.You can download it here: https://umod.org/plugins/rust-translation-api");
}
});
}
private void OnServerInitialized()
{
search = new ItemSearch();
VendingCache.Instance.Refresh();
AddCovalenceCommand(UI_COMMAND, nameof(GuiCommandHandler));
AddCovalenceCommand(_config.chatCommand, nameof(CmdMarketplace));
LoadData();
}
private void Unload()
{
SaveData();
foreach (var player in BasePlayer.activePlayerList)
{
DestroyUi(player, true);
}
_instance = null;
}
private void OnServerSave() => SaveData();
private void OnEntityMounted(ComputerStation station, BasePlayer player)
{
if (_config.enableStation && permission.UserHasPermission(player.UserIDString, PERM_USE))
{
DrawButtonUi(player);
}
}
private void OnEntityDismounted(ComputerStation station, BasePlayer player)
{
DestroyUi(player);
}
private void CmdMarketplace(IPlayer iplayer)
{
BasePlayer player = iplayer.Object as BasePlayer;
if (!_config.enableCommand || player == null || iplayer.IsServer)
{
return;
}
if (permission.UserHasPermission(player.UserIDString, PERM_USE))
{
DrawUi(player);
}
else
{
player.ChatMessage("You don't have permission to do that");
}
}
#endregion
#region UI Cache
private class UICache
{
public string search = null;
public IOrderedEnumerable<VendingOffer> offerCache;
public ItemDefinition offerItem;
public int offerPage;
public Shop currentShop;
private static readonly Dictionary<ulong, UICache> playerCache = new Dictionary<ulong, UICache>();
public static UICache GetPlayerCache(BasePlayer player)
{
if (playerCache.ContainsKey(player.userID))
return playerCache[player.userID];
var cache = new UICache();
playerCache[player.userID] = cache;
return cache;
}
public static void ClearPlayerCache(BasePlayer player)
{
playerCache.Remove(player.userID);
}
}
#endregion
#region UI Commands
private void GuiCommandHandler(IPlayer iplayer, string command, string[] args)
{
if (iplayer.IsServer) return;
if (!permission.UserHasPermission(iplayer.Id, PERM_USE)) return;
BasePlayer player = iplayer.Object as BasePlayer;
if (args.Length < 1 || player == null) return;
UICache uiCache = UICache.GetPlayerCache(player);
DPrint($"{command} {String.Join(" ", args)}");
switch (args[0])
{
case "open":
DrawUi(player);
break;
case "close":
DestroyUi(player);
UICache.ClearPlayerCache(player);
break;
case "dismount":
DestroyUi(player);
UICache.ClearPlayerCache(player);
player.EnsureDismounted();
break;
case "search":
if (args.Length < 2) break;
string prefix = args[1];
uiCache.search = prefix;
var langEntry = search.GetEntry(lang.GetLanguage(player.UserIDString), RustTranslationAPI);
var results = langEntry.Search(prefix, 30, 10);
var orderedResults = dataStore.SortItemsByScore(results);
PatchItemResults(player, orderedResults);
break;
case "sort":
ChangePlayerSortMode(player);
uiCache.offerCache = VendingOffer.SortOffers(player, uiCache.offerCache);
PatchOfferResults(player);
break;
case "select_item":
if (args.Length < 2) break;
int itemid;
if (!Int32.TryParse(args[1], out itemid)) break;
if (args.Length == 3)
ChangePlayerSortMode(player);
else
dataStore.AddSearch(itemid);
var offers = VendingCache.Instance.FindOffers(itemid);
var sorted = VendingOffer.SortOffers(player, offers);
var itemdef = ItemManager.FindItemDefinition(itemid);
uiCache.offerCache = sorted;
uiCache.offerItem = itemdef;
PatchOfferResults(player);
break;
case "page":
if (args.Length < 2) break;
int page;
if (!Int32.TryParse(args[1], out page)) break;
uiCache.offerPage = page;
PatchOfferResults(player);
break;
case "details":
if (args.Length < 2) break;
var offer = VendingCache.Instance.FindShopByGuid(args[1]);
uiCache.currentShop = offer;
DrawShopOverlay(player);
break;
case "marker":
if (args.Length < 2) break;
Vector3 pos = Shop.DeserializePosition(args[1]);
AddMarker(player, pos);
DestroyUi(player, false, UiHelper.shopOverlay);
break;
}
}
#endregion
#region CUI
private void DrawButtonUi(BasePlayer player)
{
DestroyUi(player);
CuiElementContainer result = new CuiElementContainer();
string rootPanelName = result.Add(new CuiPanel
{
RectTransform =
{
// Does not scale (please bring back ui scale for computer station)
AnchorMin = "0.1 0.848",
AnchorMax = "0.2 0.894"
// Scales with ui scale
//AnchorMin = "0.5 0.5",
//AnchorMax = "0.5 0.5",
//OffsetMin = "-510 251",
//OffsetMax = "-350 284"
},
Image =
{
Color = UiHelper.transparentColor
}
}, "Hud.Menu", UI_PANEL);
result.Add(new CuiButton
{
RectTransform =
{
AnchorMin = "0 0",
AnchorMax = "1 1"
},
Text =
{
Text = GetLangMessage(Lang.OPEN_MARKETPLACE, player),
Align = TextAnchor.MiddleCenter,
Color = "0.05 0.05 0.05 1",
FontSize = 16
},
Button =
{
Command = $"{UI_COMMAND} open",
//Color = "0.9 0.9 0.9 0.9",
Close = UI_PANEL,
Color = "0.74 0.74 0.74 1",
Material = "assets/content/ui/uibackgroundblur-ingamemenu.mat",
}
}, rootPanelName);
CuiHelper.AddUi(player, result);
openUis.Add(player.userID);
}
private void DrawUi(BasePlayer player)
{
DestroyUi(player);
CuiElementContainer result = new CuiElementContainer();
string rootPanelName = result.Add(new CuiPanel
{
RectTransform =
{
AnchorMin = "0 0",
AnchorMax = "1 1"
},
Image =
{
Color = "0 0 0 1"
},
CursorEnabled = true,
KeyboardEnabled = true
}, "OverlayNonScaled", UI_PANEL);
#region Head
string headPanel = result.Add(new CuiPanel
{
Image = new CuiImageComponent
{
Color = UiHelper.panelColor
},
RectTransform =
{
AnchorMin = "0.01 0.92",
AnchorMax = "0.99 1"
},
}, rootPanelName);
result.Add(new CuiElement
{
Components =
{
new CuiRawImageComponent
{
Url = "https://i.imgur.com/fwi2NpB.png",
Color = "0.8 0.8 0.8 1",
},
new CuiRectTransformComponent
{
AnchorMin = $"0.01 0.15", AnchorMax = $"0.01 0.15",
OffsetMin = "0 0", OffsetMax = "40 40"
}
},
Parent = headPanel
});
result.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.05 0",
AnchorMax = "0.5 0.9"
},
Text =
{
Text = String.Join(" ", GetLangMessage(Lang.MARKETPLACE, player).ToUpperInvariant() as IEnumerable<char>),
Align = TextAnchor.MiddleLeft,
Color = UiHelper.textColor,
FontSize = 21
}
}, headPanel);
result.Add(new CuiButton
{
RectTransform =
{
AnchorMin = "0.95 0.2",
AnchorMax = "0.98 0.8"
},
Text =
{
Text = "X",
Align = TextAnchor.MiddleCenter,
Color = UiHelper.buttonTextColor,
FontSize = 15
},
Button =
{
Command = $"{UI_COMMAND} dismount",
Color = UiHelper.redButtonColor,
Close = UI_PANEL
}
}, headPanel);
#endregion
#region Search
string searchPanel = result.Add(new CuiPanel
{
Image = new CuiImageComponent
{
Color = UiHelper.panelColor
},
RectTransform =
{
AnchorMin = "0.01 0.85",
AnchorMax = "0.99 0.91"
},
}, rootPanelName);
result.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.01 0.2",
AnchorMax = "0.6 0.75"
},
Text =
{
Text = $"{GetLangMessage(Lang.SEARCH, player)}:",
Align = TextAnchor.MiddleLeft,
Color = UiHelper.textColor,
FontSize = 12
}
}, searchPanel);
string inputPanel = result.Add(new CuiPanel
{
Image = new CuiImageComponent
{
Color = UiHelper.panelColorSec
},
RectTransform =
{
AnchorMin = "0.07 0.2",
AnchorMax = "0.3 0.7"
}
}, searchPanel);
//Input
result.Add(new CuiElement
{
Parent = inputPanel,
Components =
{
new CuiInputFieldComponent
{
FontSize = 12,
Align = TextAnchor.MiddleLeft,
Command = $"{UI_COMMAND} search",
Color = UiHelper.textColor,
CharsLimit = 30,
Font = UiHelper.regularFont
},
new CuiRectTransformComponent
{
AnchorMin = "0.02 0", AnchorMax = "1 1",
}
}
});
#endregion
#region Search Results
string resultPanel = result.Add(new CuiPanel
{
Image = new CuiImageComponent
{
Color = UiHelper.panelColor
},
RectTransform =
{
AnchorMin = "0.01 0.5",
AnchorMax = "0.99 0.84"
},
}, rootPanelName, UiHelper.itemSearchPanel);
#endregion
#region Offers
result.Add(new CuiPanel
{
Image = new CuiImageComponent
{
Color = UiHelper.panelColor
},
RectTransform =
{
AnchorMin = "0.01 0.01",
AnchorMax = "0.99 0.49"
},
}, rootPanelName, UiHelper.offerPanel);
#endregion
CuiHelper.AddUi(player, result);
openUis.Add(player.userID);
PatchItemResults(player, dataStore.GetPopularItems(30));
}
private void PatchOfferResults(BasePlayer player)
{
//DestroyUi(player, panel: UiHelper.offerPanel);
var uiCache = UICache.GetPlayerCache(player);
var result = new CuiElementContainer();
var lang = this.lang.GetLanguage(player.UserIDString);
#region Head
result.Add(new CuiElement
{
Parent = UI_PANEL,
Components =
{
new CuiRectTransformComponent
{
AnchorMin = "0.01 0.01",
AnchorMax = "0.99 0.49"
},
new CuiImageComponent
{
Color = UiHelper.panelColor
},
},
Name = UiHelper.offerPanel,
DestroyUi = UiHelper.offerPanel
});
string offerPanel = UiHelper.offerPanel;/* = result.Add(new CuiPanel
{
Image = new CuiImageComponent
{
Color = UiHelper.panelColor
},
RectTransform =
{
AnchorMin = "0.01 0.01",
AnchorMax = "0.99 0.49"
},
}, UI_PANEL, UiHelper.offerPanel);*/
result.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.01 0.94",
AnchorMax = "0.3 0.98",
},
Text =
{
Text = GetLangMessage(Lang.OFFERS_FOR, player, $"<i>{GetItemNameTranslated(uiCache.offerItem, lang)}</i>"),
Align = TextAnchor.MiddleLeft,
Color = UiHelper.textColor,
FontSize = 12
}
}, offerPanel);
result.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.8 0.94",
AnchorMax = "0.9 0.98",
},
Text =
{
Text = GetLangMessage(Lang.SORT_BY, player),
Align = TextAnchor.MiddleRight,
Color = UiHelper.textColor,
FontSize = 10
}
}, offerPanel);
result.Add(new CuiButton
{
RectTransform =
{
AnchorMin = "0.905 0.94",
AnchorMax = "0.99 0.98"
},
Button =
{
Command = $"{UI_COMMAND} sort",
Color = UiHelper.greyButtonColor,
},
Text =
{
Text = GetLangMessage(GetPlayerSortMode(player).ToString(), player).ToUpper(),
FontSize = 10,
Align = TextAnchor.MiddleCenter,
Color = UiHelper.buttonTextColor
}
}, offerPanel);
#endregion
#region Offers
int r = 0; int c = 0;
int r_max = 4; int c_max = 6;
int itemsPerPage = r_max * c_max;
int page = uiCache.offerPage;
var pageItems = uiCache.offerCache.Skip(page * itemsPerPage).Take(itemsPerPage);
var view = GetLangMessage(Lang.VIEW, player).ToUpperInvariant();
var itemFor = GetLangMessage(Lang.FOR, player);
var inStock = GetLangMessage(Lang.ITEM_STOCK, player);
var totalStock = GetLangMessage(Lang.TOTAL_STOCK, player);
foreach (var offer in pageItems)
{
string itemPanel = result.Add(new CuiPanel
{
Image = new CuiImageComponent
{
Color = UiHelper.panelColorSec
},
RectTransform =
{
AnchorMin = $"{0.01+r*0.247} {0.79-c*0.15}",
AnchorMax = $"{0.25+r*0.247} {0.92-c*0.15}"
},
}, offerPanel);
// offer
result.Add(new CuiElement
{
Parent = itemPanel,
Components =
{
GetItemImage(offer.offer.ItemId),
new CuiRectTransformComponent
{
AnchorMin = "0.03 0.5",
AnchorMax = "0.03 0.5",
OffsetMin = "0 -15",
OffsetMax = "30 15"
}
}
});
result.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.03 0.5",
AnchorMax = "0.03 0.5",
OffsetMin = "15 -15",
OffsetMax = "40 -5"
},
Text =
{
Text = offer.offer.Amount.ToString(),
Align = TextAnchor.LowerCenter,
Color = UiHelper.textColor,
Font = UiHelper.regularFont,
FontSize = 8
}
}, itemPanel);
// for
result.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.15 0.1",
AnchorMax = "0.22 0.9"
},
Text =
{
Text = itemFor,
Align = TextAnchor.MiddleCenter,
Color = UiHelper.textColor,
FontSize = 10
}
}, itemPanel);
// Cost
result.Add(new CuiElement
{
Parent = itemPanel,
Components =
{
GetItemImage(offer.cost.ItemId),
new CuiRectTransformComponent
{
AnchorMin = "0.23 0.5",
AnchorMax = "0.23 0.5",
OffsetMin = "0 -15",
OffsetMax = "30 15"
}
}
});
result.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.25 0.5",
AnchorMax = "0.25 0.5",
OffsetMin = "15 -15",
OffsetMax = "40 -5"
},
Text =
{
Text = offer.cost.Amount.ToString(),
Align = TextAnchor.LowerCenter,
Color = UiHelper.textColor,
Font = UiHelper.regularFont,
FontSize = 8
}
}, itemPanel);
// owner name
result.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.4 0.65",
AnchorMax = "0.95 0.9"
},
Text =
{
Text = offer.Shop.shopName,
Align = TextAnchor.LowerLeft,
Color = UiHelper.textColor,
FontSize = 10
}
}, itemPanel);
// stock
result.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.4 0.32",
AnchorMax = "0.8 0.6"
},
Text =
{
Text = $"{String.Format(inStock, offer.stock)} ({String.Format(totalStock, offer.TotalStock)})",
Align = TextAnchor.LowerLeft,
Color = UiHelper.textColor,
Font = UiHelper.regularFont,
FontSize = 9
}
}, itemPanel);
// location, distance
result.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.4 0.05",
AnchorMax = "0.8 0.3"
},
Text =
{
Text = $"{offer.Shop.GridPosition} {offer.GetDistance(player)}m",
Align = TextAnchor.LowerLeft,
Color = UiHelper.textColor,
Font = UiHelper.regularFont,
FontSize = 9
}
}, itemPanel);
// set marker
result.Add(new CuiButton
{
RectTransform =
{
AnchorMin = "0.81 0.1",
AnchorMax = "0.98 0.5"
},
Button =
{
Command = $"{UI_COMMAND} details {offer.Guid}",
Color = UiHelper.colorBlue,
},
Text =
{
Text = view,
FontSize = 10,
Align = TextAnchor.MiddleCenter,
Color = UiHelper.buttonTextColor
}
}, itemPanel);
r++;
if (r >= r_max)
{
r = 0;
c++;
if (c >= c_max) break;
}
}
#endregion
#region Page navigation
int pages = Mathf.CeilToInt((float)uiCache.offerCache.Count() / (float)itemsPerPage);
result.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.85 0.01",
AnchorMax = "0.94 0.06"
},
Text =
{
Align = TextAnchor.LowerRight,
Text = GetLangMessage(Lang.PAGE, player, page+1, pages),
Color = UiHelper.textColor,
Font = UiHelper.regularFont,
FontSize = 10
}
}, offerPanel);
result.Add(new CuiButton
{
RectTransform =
{
AnchorMin = "0.945 0.01",
AnchorMax = "0.965 0.04"
},
Button =
{
Command = page-1 >= 0 ? $"{UI_COMMAND} page {page-1}" : string.Empty,
Color = page-1 >= 0 ? UiHelper.colorBlue : UiHelper.greyButtonColor
},
Text =
{
Align = TextAnchor.MiddleCenter,
Text = "<-",
Color = UiHelper.buttonTextColor,
FontSize = 10
}
}, offerPanel);
result.Add(new CuiButton
{
RectTransform =
{
AnchorMin = "0.97 0.01",
AnchorMax = "0.99 0.04"
},
Button =
{
Command = page+1 < pages ? $"{UI_COMMAND} page {page+1}" : string.Empty,
Color = page+1 < pages ? UiHelper.colorBlue : UiHelper.greyButtonColor
},
Text =
{
Align = TextAnchor.MiddleCenter,
Text = "->",
Color = UiHelper.buttonTextColor,
FontSize = 10
}
}, offerPanel);
#endregion
NextFrame(() =>
{
CuiHelper.AddUi(player, result);
});
}
private void PatchItemResults(BasePlayer player, IEnumerable<int> items)
{
DestroyUi(player, panel: UiHelper.itemSearchPanel);
var lang = this.lang.GetLanguage(player.UserIDString);
UICache uiCache = UICache.GetPlayerCache(player);
string title = uiCache.search == null ? GetLangMessage(Lang.POPULAR_ITEMS, player) : GetLangMessage(Lang.ITEM_RESULTS, player, $"<i>{uiCache.search}</i>");
var result = new CuiElementContainer();
string resultPanel = result.Add(new CuiPanel
{
Image = new CuiImageComponent
{
Color = UiHelper.panelColor
},
RectTransform =
{
AnchorMin = "0.01 0.5",
AnchorMax = "0.99 0.84"
},
}, UI_PANEL, UiHelper.itemSearchPanel);
result.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.01 0.92",
AnchorMax = "0.3 1",
},
Text =
{
Text = title,
Align = TextAnchor.LowerLeft,
Color = UiHelper.textColor,
FontSize = 12
}
}, resultPanel);
int r = 0;
int c = 0;
foreach (var itemId in items)
{
string itemPanel = result.Add(new CuiPanel
{
Image = new CuiImageComponent
{
Color = UiHelper.panelColorSec
},
RectTransform =
{
AnchorMin = $"{0.01+r*0.15} {0.74-c*0.18}",
AnchorMax = $"{0.155+r*0.15} {0.90-c*0.18}"
},
}, resultPanel);
result.Add(new CuiElement
{
Parent = itemPanel,
Components =
{
GetItemImage(itemId),
new CuiRectTransformComponent
{
AnchorMin = "0.03 0.5",
AnchorMax = "0.03 0.5",
OffsetMin = "0 -15",
OffsetMax = "30 15"
}
}
});
result.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.25 0.1",
AnchorMax = "0.9 0.85"
},
Text =
{
Text = GetItemNameTranslated(itemId, lang),
Align = TextAnchor.UpperLeft,
Color = UiHelper.textColor,
FontSize = 10
}
}, itemPanel);
result.Add(new CuiButton
{
RectTransform =
{
AnchorMin = "0 0",
AnchorMax = "1 1"
},
Button =
{
Command = $"{UI_COMMAND} select_item {itemId}",
Color = UiHelper.transparentColor,
}
}, itemPanel);
r++;
if (r > 5)
{
r = 0;
c++;
if (c > 4) break;
}
}
NextFrame(() =>
{
CuiHelper.AddUi(player, result);
});
}
private void DrawShopOverlay(BasePlayer player)
{
DestroyUi(player, panel: UiHelper.shopOverlay);
var uiCache = UICache.GetPlayerCache(player);
var result = new CuiElementContainer();
#region Overlay + Head
string rootPanelName = result.Add(new CuiPanel
{
RectTransform =
{
AnchorMin = "0 0",
AnchorMax = "1 1"
},
Image =
{
Color = "0 0 0 0.8"
},
}, UI_PANEL, UiHelper.shopOverlay);
string panelName = result.Add(new CuiPanel
{
RectTransform =
{
AnchorMin = "0.5 0.5",
AnchorMax = "0.5 0.5",
OffsetMin = "-200 -250",
OffsetMax = "200 250"
},
Image =
{
Color = UiHelper.panelColorNoAlpha
},
}, rootPanelName);
result.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.05 0.93",
AnchorMax = "0.95 1"
},
Text =
{
Text = uiCache.currentShop.shopName,
Align = TextAnchor.MiddleCenter,
Color = UiHelper.textColor,
FontSize = 13
}
}, panelName);
result.Add(new CuiButton
{
RectTransform =
{
AnchorMin = "0.95 0.98",
AnchorMax = "0.95 0.98",
OffsetMin = "-20 -20",
OffsetMax = "0 0"
},
Text =
{
Text = "X",
Align = TextAnchor.MiddleCenter,
Color = UiHelper.buttonTextColor,
FontSize = 10
},
Button =
{
Color = UiHelper.redButtonColor,
Close = UiHelper.shopOverlay
}
}, panelName);
#endregion
#region Info + Buttons
var away = GetLangMessage(Lang.AWAY, player);
result.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.05 0.8",
AnchorMax = "0.95 0.9"
},
Text =
{
Text = (!_config.showShopOwner ? String.Empty : $"{GetLangMessage(Lang.OWNER, player)}: {GetUsername(uiCache.currentShop.ownerId, player)}\n") +
(_config.showGridPos ? $"{GetLangMessage(Lang.LOCATION, player)}: {uiCache.currentShop.GridPosition} ({uiCache.currentShop.GetDistance(player)}m {away})" : $"{uiCache.currentShop.GetDistance(player)}m {away}"),
Align = TextAnchor.MiddleLeft,
Color = UiHelper.textColor,
Font = UiHelper.regularFont,
FontSize = 10
}
}, panelName);
result.Add(new CuiButton
{
RectTransform =
{
AnchorMin = "0.95 0.88",
AnchorMax = "0.95 0.88",
OffsetMin = "-70 -20",
OffsetMax = "0 0"
},
Text =
{
Text = GetLangMessage(Lang.SET_MARKER, player).ToUpperInvariant(),
Align = TextAnchor.MiddleCenter,
Color = UiHelper.buttonTextColorDark,
FontSize = 10
},
Button =
{
Color = UiHelper.colorYellow,
Command = $"{UI_COMMAND} marker {uiCache.currentShop.SerializePosition()}"
}
}, panelName);
#endregion
result.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.05 0.75",
AnchorMax = "0.95 0.8"
},
Text =
{
Text = GetLangMessage(Lang.OFFERS, player),
Align = TextAnchor.LowerLeft,
Color = UiHelper.textColor,
FontSize = 13
}
}, panelName);
int r = 0;
int r_max = 7;
foreach (var offer in uiCache.currentShop.offers)
{
string itemPanel = result.Add(new CuiPanel
{
Image = new CuiImageComponent
{
Color = UiHelper.panelColorSec
},
RectTransform =
{
AnchorMin = $"0.05 {0.65-r*0.1}",
AnchorMax = $"0.95 {0.74-r*0.1}"
},
}, panelName);
// offer
result.Add(new CuiElement
{
Parent = itemPanel,
Components =
{
GetItemImage(offer.offer.ItemId),
new CuiRectTransformComponent
{
AnchorMin = "0.03 0.5",
AnchorMax = "0.03 0.5",
OffsetMin = "0 -15",
OffsetMax = "30 15"
}
}
});
result.Add(new CuiLabel
// ... [Code preview truncated due to length. Full source in attached file] ...