Guest User

Untitled

a guest
Feb 11th, 2025
160
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 48.36 KB | None | 0 0
  1. using System;
  2. using System.Text;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using Oxide.Core;
  6. using Oxide.Core.Plugins;
  7. using Newtonsoft.Json;
  8. using System.Linq;
  9.  
  10. namespace Oxide.Plugins
  11. {
  12. [Info ("Trade", "Calytic", "1.2.44")]
  13. class Trade : RustPlugin
  14. {
  15. #region Configuration
  16.  
  17. string box;
  18. int slots;
  19. float cooldownMinutes;
  20. float maxRadius;
  21. float pendingSeconds;
  22. float radiationMax;
  23. bool allowSafeZone;
  24.  
  25. [PluginReference]
  26. Plugin Ignore, Clans, Friends;
  27.  
  28. Dictionary<string, DateTime> tradeCooldowns = new Dictionary<string, DateTime> ();
  29.  
  30. #endregion
  31.  
  32. #region Trade State
  33.  
  34. class OnlinePlayer
  35. {
  36. public BasePlayer Player;
  37. public StorageContainer View;
  38. public OpenTrade Trade;
  39.  
  40. public PlayerInventory inventory {
  41. get {
  42. return Player.inventory;
  43. }
  44. }
  45.  
  46. public ItemContainer containerMain {
  47. get {
  48. return Player.inventory.containerMain;
  49. }
  50. }
  51.  
  52. public OnlinePlayer (BasePlayer player)
  53. {
  54. }
  55.  
  56. public void Clear ()
  57. {
  58. View = null;
  59. Trade = null;
  60. }
  61. }
  62.  
  63. Hash<BasePlayer, OnlinePlayer> onlinePlayers = new Hash<BasePlayer, OnlinePlayer> ();
  64.  
  65. class OpenTrade
  66. {
  67. public OnlinePlayer source;
  68. public OnlinePlayer target;
  69.  
  70. public BasePlayer sourcePlayer {
  71. get {
  72. return source.Player;
  73. }
  74. }
  75.  
  76. public BasePlayer targetPlayer {
  77. get {
  78. return target.Player;
  79. }
  80. }
  81.  
  82. public bool complete = false;
  83. public bool closing = false;
  84.  
  85. public bool sourceAccept = false;
  86. public bool targetAccept = false;
  87.  
  88. public OpenTrade (OnlinePlayer source, OnlinePlayer target)
  89. {
  90. this.source = source;
  91. this.target = target;
  92. }
  93.  
  94. public OnlinePlayer GetOther (OnlinePlayer onlinePlayer)
  95. {
  96. if (source == onlinePlayer) {
  97. return target;
  98. }
  99.  
  100. return source;
  101. }
  102.  
  103. public BasePlayer GetOther (BasePlayer player)
  104. {
  105. if (sourcePlayer == player) {
  106. return targetPlayer;
  107. }
  108.  
  109. return sourcePlayer;
  110. }
  111.  
  112. public void ResetAcceptance ()
  113. {
  114. sourceAccept = false;
  115. targetAccept = false;
  116. }
  117.  
  118. public bool IsInventorySufficient ()
  119. {
  120. if (target == null || source == null) {
  121. return false;
  122. }
  123.  
  124. if (target.containerMain == null || source.containerMain == null) {
  125. return false;
  126. }
  127.  
  128. if ((target.containerMain.capacity - target.containerMain.itemList.Count) < source.View.inventory.itemList.Count ||
  129. (source.containerMain.capacity - source.containerMain.itemList.Count) < target.View.inventory.itemList.Count) {
  130. return true;
  131. }
  132.  
  133. return false;
  134. }
  135.  
  136. public bool IsValid ()
  137. {
  138. if (IsSourceValid () && IsTargetValid ())
  139. return true;
  140.  
  141. return false;
  142. }
  143.  
  144. public bool IsSourceValid ()
  145. {
  146. if (sourcePlayer != null && sourcePlayer.IsConnected)
  147. return true;
  148.  
  149. return false;
  150. }
  151.  
  152. public bool IsTargetValid ()
  153. {
  154. if (targetPlayer != null && targetPlayer.IsConnected)
  155. return true;
  156.  
  157. return false;
  158. }
  159. }
  160.  
  161. class PendingTrade
  162. {
  163. public BasePlayer Target;
  164. public Timer Timer;
  165.  
  166. public PendingTrade (BasePlayer target)
  167. {
  168. Target = target;
  169. }
  170.  
  171. public void Destroy ()
  172. {
  173. if (Timer != null && !Timer.Destroyed) {
  174. Timer.Destroy ();
  175. }
  176. }
  177. }
  178.  
  179. List<OpenTrade> openTrades = new List<OpenTrade> ();
  180. Dictionary<BasePlayer, PendingTrade> pendingTrades = new Dictionary<BasePlayer, PendingTrade> ();
  181. #endregion
  182.  
  183. #region Initialization
  184.  
  185. void Init ()
  186. {
  187.  
  188. UnsubscribeAll ();
  189. }
  190.  
  191. void UnsubscribeAll ()
  192. {
  193. //Unsubscribe(nameof(CanNetworkTo));
  194. Unsubscribe (nameof (OnItemAction));
  195. Unsubscribe (nameof (OnItemAddedToContainer));
  196. Unsubscribe (nameof (CanMoveItem));
  197. Unsubscribe (nameof (OnItemRemovedFromContainer));
  198. }
  199.  
  200. void SubscribeAll ()
  201. {
  202. //Subscribe(nameof(CanNetworkTo));
  203. Subscribe (nameof (OnItemAction));
  204. Subscribe (nameof (OnItemAddedToContainer));
  205. Subscribe (nameof (CanMoveItem));
  206. Subscribe (nameof (OnItemRemovedFromContainer));
  207. }
  208.  
  209. void Loaded ()
  210. {
  211. permission.RegisterPermission ("trade.use", this);
  212. permission.RegisterPermission ("trade.accept", this);
  213.  
  214. LoadMessages ();
  215.  
  216. CheckConfig ();
  217.  
  218. box = GetConfig ("Settings", "box", "assets/prefabs/deployable/woodenbox/woodbox_deployed.prefab");
  219. slots = GetConfig ("Settings", "slots", 30);
  220. cooldownMinutes = GetConfig ("Settings", "cooldownMinutes", 5f);
  221. maxRadius = GetConfig ("Settings", "maxRadius", 5000f);
  222. pendingSeconds = GetConfig ("Settings", "pendingSeconds", 25f);
  223. radiationMax = GetConfig ("Settings", "radiationMax", 1f);
  224. allowSafeZone = GetConfig ("Settings", "allowSafeZone", true);
  225. }
  226.  
  227. void Unloaded ()
  228. {
  229. foreach (var player in BasePlayer.activePlayerList) {
  230. OnlinePlayer onlinePlayer;
  231. if (onlinePlayers.TryGetValue (player, out onlinePlayer)) {
  232. if (onlinePlayer.Trade != null) {
  233. TradeCloseBoxes (onlinePlayer.Trade);
  234. } else if (onlinePlayer.View != null) {
  235. CloseBoxView (player, onlinePlayer.View);
  236. }
  237. }
  238. }
  239. }
  240.  
  241. protected new void LoadDefaultConfig ()
  242. {
  243. Config ["Settings", "box"] = "assets/prefabs/deployable/woodenbox/woodbox_deployed.prefab";
  244. Config ["Settings", "slots"] = 30;
  245. Config ["Settings", "cooldownMinutes"] = 5;
  246. Config ["Settings", "maxRadius"] = 5000f;
  247. Config ["Settings", "pendingSeconds"] = 25f;
  248. Config ["Settings", "radiationMax"] = 1;
  249. Config ["Settings", "allowSafeZone"] = true;
  250. Config ["VERSION"] = Version.ToString ();
  251. }
  252.  
  253. void CheckConfig ()
  254. {
  255. if (Config ["VERSION"] == null) {
  256. // FOR COMPATIBILITY WITH INITIAL VERSIONS WITHOUT VERSIONED CONFIG
  257. ReloadConfig ();
  258. } else if (GetConfig<string> ("VERSION", "") != Version.ToString ()) {
  259. // ADDS NEW, IF ANY, CONFIGURATION OPTIONS
  260. ReloadConfig ();
  261. }
  262. }
  263.  
  264. protected void ReloadConfig ()
  265. {
  266. Config ["VERSION"] = Version.ToString ();
  267.  
  268. // NEW CONFIGURATION OPTIONS HERE
  269. Config ["Settings", "radiationMax"] = GetConfig ("Settings", "radiationMax", 1f);
  270. // END NEW CONFIGURATION OPTIONS
  271.  
  272. PrintToConsole ("Upgrading configuration file");
  273. SaveConfig ();
  274. }
  275.  
  276. void LoadMessages ()
  277. {
  278. lang.RegisterMessages (new Dictionary<string, string>
  279. {
  280. {"Inventory: You", "You do not have enough room in your inventory"},
  281. {"Inventory: Them", "Their inventory does not have enough room"},
  282. {"Inventory: Generic", "Insufficient inventory space"},
  283.  
  284. {"Player: Not Found", "No player found by that name"},
  285. {"Player: Unknown", "Unknown"},
  286. {"Player: Yourself", "You cannot trade with yourself"},
  287.  
  288. {"Status: Completing", "Completing trade.."},
  289. {"Status: No Pending", "You have no pending trade requests"},
  290. {"Status: Pending", "They already have a pending trade request"},
  291. {"Status: Received", "You have received a trade request from {0}. Type <color=#00FF00>/trade accept</color> to begin trading"},
  292. {"Status: They Interrupted", "They moved or closed the trade"},
  293. {"Status: You Interrupted", "You moved or closed the trade"},
  294.  
  295. {"Trade: Sent", "Trade request sent"},
  296. {"Trade: They Declined", "They declined your trade request"},
  297. {"Trade: You Declined", "You declined their trade request"},
  298. {"Trade: They Accepted", "{0} accepted."},
  299. {"Trade: You Accepted", "You accepted."},
  300. {"Trade: Pending", "Trade pending."},
  301.  
  302. {"Denied: Permission", "You lack permission to do that"},
  303. {"Denied: Privilege", "You do no have building privilege"},
  304. {"Denied: Swimming", "You cannot do that while swimming"},
  305. {"Denied: Falling", "You cannot do that while falling"},
  306. {"Denied: Mounted", "You cannot do that while mounted"},
  307. {"Denied: Wounded", "You cannot do that while wounded"},
  308. {"Denied: Irradiated", "You cannot do that while irradiated"},
  309. {"Denied: Generic", "You cannot do that right now"},
  310. {"Denied: They Busy", "That player is busy"},
  311. {"Denied: They Ignored You", "They ignored you"},
  312. {"Denied: Distance", "Too far away"},
  313. {"Denied: Ship", "You cannot do that while on a ship"},
  314. {"Denied: Lift", "You cannot do that while on a lift"},
  315. {"Denied: Balloon", "You cannot do that while on a balloon"},
  316. {"Denied: Safe Zone", "You cannot do that while in a safe zone"},
  317.  
  318. {"Item: BP", "BP"},
  319.  
  320. {"Syntax: Trade Accept", "Invalid syntax. /trade accept"},
  321. {"Syntax: Trade", "Invalid syntax. /trade \"Player Name\""},
  322.  
  323. {"Cooldown: Seconds", "You are doing that too often, try again in a {0} seconds(s)."},
  324. {"Cooldown: Minutes", "You are doing that too often, try again in a {0} minute(s)."},
  325. }, this);
  326. }
  327.  
  328. #endregion
  329.  
  330. #region Oxide Hooks
  331.  
  332. //object CanNetworkTo(BaseNetworkable entity, BasePlayer target)
  333. //{
  334. // if (entity == null || target == null || entity == target) return null;
  335. // if (target.IsAdmin) return null;
  336.  
  337. // OnlinePlayer onlinePlayer;
  338. // bool IsMyBox = false;
  339. // if (onlinePlayers.TryGetValue(target, out onlinePlayer))
  340. // {
  341. // if (onlinePlayer.View != null && onlinePlayer.View.net.ID == entity.net.ID)
  342. // {
  343. // IsMyBox = true;
  344. // }
  345. // }
  346.  
  347. // if (IsTradeBox(entity) && !IsMyBox) return false;
  348.  
  349. // return null;
  350. //}
  351.  
  352. void OnPlayerConnected (BasePlayer player)
  353. {
  354. if (onlinePlayers.TryGetValue(player, out OnlinePlayer onlinePlayer))
  355. {
  356. onlinePlayer.View = null;
  357. onlinePlayer.Trade = null;
  358. return;
  359. }
  360.  
  361. onlinePlayers[player] = new OnlinePlayer(player)
  362. {
  363. Player = player
  364. };
  365. }
  366.  
  367. void OnPlayerDisconnected (BasePlayer player)
  368. {
  369. OnlinePlayer onlinePlayer;
  370. if (onlinePlayers.TryGetValue (player, out onlinePlayer))
  371. {
  372. if (onlinePlayer.Trade != null) {
  373. TradeCloseBoxes (onlinePlayer.Trade);
  374. } else if (onlinePlayer.View != null) {
  375. CloseBoxView (player, onlinePlayer.View);
  376. }
  377. }
  378. }
  379.  
  380. void OnPlayerLootEnd (PlayerLoot inventory)
  381. {
  382. var player = inventory.GetComponent<BasePlayer> ();
  383. if (player == null)
  384. return;
  385.  
  386. OnlinePlayer onlinePlayer;
  387. if (onlinePlayers.TryGetValue (player, out onlinePlayer) && onlinePlayer.View != null) {
  388. if (onlinePlayer.View == inventory.entitySource && onlinePlayer.Trade != null) {
  389. OpenTrade t = onlinePlayer.Trade;
  390.  
  391. if (!t.closing) {
  392. t.closing = true;
  393. if (!onlinePlayer.Trade.complete) {
  394. if (onlinePlayer.Trade.sourcePlayer == player) {
  395. TradeReply (t, "Status: They Interrupted", "Status: You Interrupted");
  396. } else {
  397. TradeReply (t, "Status: You Interrupted", "Status: They Interrupted");
  398. }
  399. }
  400. CloseBoxView (player, (StorageContainer)inventory.entitySource);
  401. }
  402. }
  403. }
  404. }
  405.  
  406. void OnItemAction (Item item, string cmd)
  407. {
  408. if (cmd == "drop") {
  409. BasePlayer player = item.GetOwnerPlayer ();
  410.  
  411. if (player is BasePlayer) {
  412. OnlinePlayer onlinePlayer;
  413. if (onlinePlayers.TryGetValue (player, out onlinePlayer) && onlinePlayer.Trade != null && player.inventory != null) {
  414. if (item.parent == player.inventory.containerMain && !onlinePlayer.Trade.IsInventorySufficient ()) {
  415. ShowTrades (onlinePlayer.Trade, "Trade: Pending");
  416. }
  417. }
  418. }
  419. }
  420. }
  421.  
  422. void OnItemAddedToContainer (ItemContainer container, Item item)
  423. {
  424. if (container.playerOwner is BasePlayer) {
  425. OnlinePlayer onlinePlayer;
  426. if (onlinePlayers.TryGetValue (container.playerOwner, out onlinePlayer) && onlinePlayer.Trade != null) {
  427. OpenTrade t = onlinePlayers [container.playerOwner].Trade;
  428.  
  429. if (!t.complete) {
  430. t.ResetAcceptance ();
  431.  
  432. if (t.IsValid ()) {
  433. ShowTrades (t, "Trade: Pending");
  434. } else {
  435. TradeCloseBoxes (t);
  436. }
  437. }
  438. }
  439. }
  440. }
  441.  
  442. object CanMoveItem (Item item, PlayerInventory playerLoot, uint targetContainer, int targetSlot)
  443. {
  444. var player = playerLoot.GetComponent<BasePlayer> ();
  445. if (player == null) {
  446. return null;
  447. }
  448.  
  449. OnlinePlayer onlinePlayer;
  450. if (onlinePlayers.TryGetValue (player, out onlinePlayer) && onlinePlayer.Trade != null) {
  451. OpenTrade t = onlinePlayers [player].Trade;
  452. if (t.closing) {
  453. return false;
  454. }
  455. }
  456.  
  457. return null;
  458. }
  459.  
  460. void OnItemRemovedFromContainer (ItemContainer container, Item item)
  461. {
  462. if (container.playerOwner is BasePlayer) {
  463. OnlinePlayer onlinePlayer;
  464. if (onlinePlayers.TryGetValue (container.playerOwner, out onlinePlayer) && onlinePlayer.Trade != null) {
  465. OpenTrade t = onlinePlayers [container.playerOwner].Trade;
  466. if (!t.complete) {
  467. t.ResetAcceptance ();
  468.  
  469. if (t.IsValid ()) {
  470. ShowTrades (t, "Trade: Pending");
  471. } else {
  472. TradeCloseBoxes (t);
  473. }
  474. }
  475. }
  476. }
  477. }
  478.  
  479. #endregion
  480.  
  481. #region Commands
  482.  
  483. [ChatCommand ("trade")]
  484. void cmdTrade (BasePlayer player, string command, string [] args)
  485. {
  486. if (args.Length == 1) {
  487. if (args [0] == "accept") {
  488. if (!CanPlayerTrade (player, "trade.accept"))
  489. return;
  490.  
  491. AcceptTrade (player);
  492. return;
  493. }
  494. }
  495.  
  496. if (args.Length != 1) {
  497. if (pendingTrades.ContainsKey (player)) {
  498. SendReply (player, GetMsg ("Syntax: Trade Accept", player));
  499. } else {
  500. SendReply (player, GetMsg ("Syntax: Trade", player));
  501. }
  502.  
  503. return;
  504. }
  505.  
  506. var targetPlayer = FindPlayerByPartialName (args [0]);
  507. if (targetPlayer == null) {
  508. SendReply (player, GetMsg ("Player: Not Found", player));
  509. return;
  510. }
  511.  
  512. if (targetPlayer == player) {
  513. SendReply (player, GetMsg ("Player: Yourself", player));
  514. return;
  515. }
  516.  
  517. if (!CheckCooldown (player)) {
  518. return;
  519. }
  520.  
  521. if (Ignore != null) {
  522. var IsIgnored = Ignore.Call ("IsIgnored", player.UserIDString, targetPlayer.UserIDString);
  523. if ((bool)IsIgnored == true) {
  524. SendReply (player, GetMsg ("Denied: They Ignored You", player));
  525. return;
  526. }
  527. }
  528.  
  529. OnlinePlayer onlineTargetPlayer;
  530. if (onlinePlayers.TryGetValue (targetPlayer, out onlineTargetPlayer) && onlineTargetPlayer.Trade != null) {
  531. SendReply (player, GetMsg ("Denied: They Busy", player));
  532. return;
  533. }
  534.  
  535. if (maxRadius > 0) {
  536. if (targetPlayer.Distance (player) > maxRadius) {
  537. SendReply (player, GetMsg ("Denied: Distance", player));
  538. return;
  539. }
  540. }
  541.  
  542. if (!CanPlayerTrade (player, "trade.use"))
  543. return;
  544.  
  545. if (pendingTrades.ContainsKey (player)) {
  546. SendReply (player, GetMsg ("Status: Pending", player));
  547. } else {
  548. SendReply (targetPlayer, GetMsg ("Status: Received", targetPlayer), player.displayName);
  549. SendReply (player, GetMsg ("Trade: Sent", player));
  550. var pendingTrade = new PendingTrade (targetPlayer);
  551. pendingTrades.Add (player, pendingTrade);
  552.  
  553. pendingTrade.Timer = timer.In (pendingSeconds, delegate () {
  554. if (pendingTrades.ContainsKey (player)) {
  555. pendingTrades.Remove (player);
  556. SendReply (player, GetMsg ("Trade: They Declined", player));
  557. SendReply (targetPlayer, GetMsg ("Trade: You Declined", targetPlayer));
  558. }
  559. });
  560. }
  561. }
  562.  
  563. [ConsoleCommand ("trade")]
  564. void ccTrade (ConsoleSystem.Arg arg)
  565. {
  566. if (arg.Connection == null)
  567. return;
  568. if (arg.Connection.player == null)
  569. return;
  570. cmdTrade (arg.Connection.player as BasePlayer, arg.cmd.Name, arg.Args);
  571. }
  572.  
  573. [ConsoleCommand ("trade.decline")]
  574. void ccTradeDecline (ConsoleSystem.Arg arg)
  575. {
  576. if (arg.Connection == null)
  577. return;
  578. if (arg.Connection.player == null)
  579. return;
  580. var player = arg.Connection.player as BasePlayer;
  581.  
  582. OnlinePlayer onlinePlayer;
  583. if (onlinePlayers.TryGetValue (player, out onlinePlayer) && onlinePlayer.Trade != null) {
  584. onlinePlayer.Trade.closing = true;
  585. var target = onlinePlayer.Trade.GetOther (player);
  586. SendReply (player, GetMsg ("Trade: You Declined", player));
  587. SendReply (target, GetMsg ("Trade: They Declined", target));
  588.  
  589. TradeCloseBoxes (onlinePlayer.Trade);
  590. } else if (player is BasePlayer) {
  591. HideTrade (player);
  592. }
  593. }
  594.  
  595. [ConsoleCommand ("trade.accept")]
  596. void ccTradeAccept (ConsoleSystem.Arg arg)
  597. {
  598. if (arg.Connection == null)
  599. return;
  600. if (arg.Connection.player == null)
  601. return;
  602. var player = arg.Connection.player as BasePlayer;
  603.  
  604. TradeAccept (player);
  605. }
  606.  
  607. void TradeAccept (BasePlayer player)
  608. {
  609. OnlinePlayer onlinePlayer;
  610. if (onlinePlayers.TryGetValue (player, out onlinePlayer) && onlinePlayer.Trade != null) {
  611. var t = onlinePlayers [player].Trade;
  612. if (t.sourcePlayer == player) {
  613. if (!CheckSourceInventory (t)) {
  614. return;
  615. }
  616.  
  617. t.sourceAccept = true;
  618. } else if (t.targetPlayer == player) {
  619. if (!CheckTargetInventory (t)) {
  620. return;
  621. }
  622.  
  623. t.targetAccept = true;
  624. }
  625.  
  626. if (t.targetAccept == true && t.sourceAccept == true) {
  627. CompleteTrade (t);
  628. } else {
  629. ShowTrades (t, "Trade: Pending");
  630. }
  631. } else if (player is BasePlayer) {
  632. HideTrade (player);
  633. }
  634. }
  635.  
  636. void CompleteTrade (OpenTrade t)
  637. {
  638. if (t.IsInventorySufficient ()) {
  639. t.ResetAcceptance ();
  640. ShowTrades (t, "Inventory: Generic");
  641. return;
  642. }
  643. if (t.complete) {
  644. return;
  645. }
  646. t.complete = true;
  647. t.closing = true;
  648.  
  649. TradeCooldown (t);
  650.  
  651. TradeReply (t, "Status: Completing");
  652. Interface.Oxide.NextTick (() => FinishTrade (t));
  653. }
  654.  
  655. bool CheckSourceInventory (OpenTrade t)
  656. {
  657. var i = t.target.View.inventory.itemList.Count;
  658. var f = t.source.containerMain.capacity - t.source.containerMain.itemList.Count;
  659. if (i > f) {
  660.  
  661. TradeReply (t, "Inventory: Them", "Inventory: You");
  662.  
  663. t.sourceAccept = false;
  664. ShowTrades (t, "Inventory: Generic");
  665. return false;
  666. }
  667.  
  668. return true;
  669. }
  670.  
  671. bool CheckTargetInventory (OpenTrade t)
  672. {
  673. var i = t.source.View.inventory.itemList.Count;
  674. var f = t.target.containerMain.capacity - t.target.containerMain.itemList.Count;
  675. if (i > f) {
  676. TradeReply (t, "Inventory: You", "Inventory: Them");
  677. t.targetAccept = false;
  678. ShowTrades (t, "Inventory: Generic");
  679. return false;
  680. }
  681.  
  682. return true;
  683. }
  684.  
  685. #endregion
  686.  
  687. #region GUI
  688.  
  689. public string jsonTrade = @"[{""name"":""TradeMsg"",""parent"":""Overlay"",""components"":[{""type"":""UnityEngine.UI.Image"",""color"":""0 0 0 0.76"",""imagetype"":""Filled""},{""type"":""RectTransform"",""anchormax"":""0.77 0.91"",""anchormin"":""0.24 0.52""}]},{""name"":""SourceLabel{1}"",""parent"":""TradeMsg"",""components"":[{""type"":""UnityEngine.UI.Text"",""text"":""{sourcename}"",""fontSize"":""16"",""align"":""UpperLeft""},{""type"":""RectTransform"",""anchormax"":""0.48 0.98"",""anchormin"":""0.03 0.91""}]},{""name"":""TargetLabel{2}"",""parent"":""TradeMsg"",""components"":[{""type"":""UnityEngine.UI.Text"",""text"":""{targetname}"",""fontSize"":""17""},{""type"":""RectTransform"",""anchormax"":""0.97 0.98"",""anchormin"":""0.52 0.91""}]},{""name"":""SourceItemsPanel{3}"",""parent"":""TradeMsg"",""components"":[{""type"":""UnityEngine.UI.RawImage"",""color"":""0 0 0 0.52"",""imagetype"":""Filled""},{""type"":""RectTransform"",""anchormax"":""0.47 0.9"",""anchormin"":""0.03 0.13""}]},{""name"":""SourceItemsText"",""parent"":""SourceItemsPanel{3}"",""components"":[{""type"":""UnityEngine.UI.Text"",""text"":""{sourceitems}"",""fontSize"":""14"",""align"":""UpperLeft""},{""type"":""RectTransform"",""anchormax"":""0.99 0.99"",""anchormin"":""0.01 0.01""}]},{""name"":""TargetItemsPanel{4}"",""parent"":""TradeMsg"",""components"":[{""type"":""UnityEngine.UI.RawImage"",""color"":""0 0 0 0.52"",""imagetype"":""Filled""},{""type"":""RectTransform"",""anchormax"":""0.96 0.9"",""anchormin"":""0.52 0.13""}]},{""name"":""TargetItemsText"",""parent"":""TargetItemsPanel{4}"",""components"":[{""type"":""UnityEngine.UI.Text"",""text"":""{targetitems}"",""fontSize"":""14"",""align"":""UpperLeft""},{""type"":""RectTransform"",""anchormax"":""0.99 0.99"",""anchormin"":""0.01 0.01""}]},{""name"":""AcceptTradeButton{5}"",""parent"":""TradeMsg"",""components"":[{""type"":""UnityEngine.UI.Button"",""color"":""0 0.95 0.14 0.54"",""command"":""trade.accept""},{""type"":""RectTransform"",""anchormax"":""0.47 0.09"",""anchormin"":""0.35 0.03""}]},{""name"":""AcceptTradeLabel"",""parent"":""AcceptTradeButton{5}"",""components"":[{""type"":""UnityEngine.UI.Text"",""text"":""Accept"",""fontSize"":""13"",""align"":""MiddleCenter""},{""type"":""RectTransform"",""anchormax"":""1 1"",""anchormin"":""0 0""}]},{""name"":""DeclineTradeButton{6}"",""parent"":""TradeMsg"",""components"":[{""type"":""UnityEngine.UI.Button"",""color"":""0.95 0 0.02 0.61"",""command"":""trade.decline""},{""type"":""RectTransform"",""anchormax"":""0.15 0.09"",""anchormin"":""0.03 0.03""}]},{""name"":""DeclineTradeLabel"",""parent"":""DeclineTradeButton{6}"",""components"":[{""type"":""UnityEngine.UI.Text"",""text"":""Decline"",""fontSize"":""13"",""align"":""MiddleCenter""},{""type"":""RectTransform"",""anchormax"":""1 1"",""anchormin"":""0 0""}]},{""name"":""TargetStatusLabel{7}"",""parent"":""TradeMsg"",""components"":[{""type"":""UnityEngine.UI.Text"",""text"":""{targetstatus}"",""fontSize"":""14"",""align"":""UpperLeft""},{""type"":""RectTransform"",""anchormax"":""0.97 0.09"",""anchormin"":""0.52 0.01""}]}]
  690. ";
  691. private void ShowTrade (BasePlayer player, OpenTrade trade, string status)
  692. {
  693. HideTrade (player);
  694.  
  695. OnlinePlayer onlinePlayer;
  696. if (!onlinePlayers.TryGetValue (player, out onlinePlayer)) {
  697. return;
  698. }
  699.  
  700. if (onlinePlayer.View == null) {
  701. return;
  702. }
  703.  
  704. StorageContainer sourceContainer = onlinePlayer.View;
  705. StorageContainer targetContainer = null;
  706. BasePlayer target = null;
  707.  
  708. if (trade.sourcePlayer == player && trade.target.View != null) {
  709. targetContainer = trade.target.View;
  710. target = trade.targetPlayer;
  711. if (target is BasePlayer) {
  712. if (trade.targetAccept) {
  713. status += string.Format (GetMsg ("Trade: They Accepted", player), CleanName (target.displayName));
  714. } else if (trade.sourceAccept) {
  715. status += GetMsg ("Trade: You Accepted", player);
  716. }
  717. } else {
  718. return;
  719. }
  720. } else if (trade.targetPlayer == player && trade.source.View != null) {
  721. targetContainer = trade.source.View;
  722. target = trade.sourcePlayer;
  723. if (target is BasePlayer) {
  724. if (trade.sourceAccept) {
  725. status += string.Format (GetMsg ("Trade: They Accepted", player), CleanName (target.displayName));
  726. } else if (trade.targetAccept) {
  727. status += GetMsg ("Trade: You Accepted", player);
  728. }
  729. } else {
  730. return;
  731. }
  732. }
  733.  
  734. if (targetContainer == null || target == null) {
  735. return;
  736. }
  737.  
  738. string send = jsonTrade;
  739. for (int i = 1; i < 100; i++) {
  740. send = send.Replace ("{" + i + "}", Oxide.Core.Random.Range (9999, 99999).ToString ());
  741. }
  742.  
  743. send = send.Replace ("{sourcename}", CleanName (player.displayName));
  744. if (target != null) {
  745. send = send.Replace ("{targetname}", CleanName (target.displayName));
  746. } else {
  747. send = send.Replace ("{targetname}", GetMsg ("Player: Unknown", player));
  748. }
  749. send = send.Replace ("{targetstatus}", status);
  750.  
  751. var slotsAvailable = target.inventory.containerMain.capacity - (target.inventory.containerMain.itemList.Count);
  752. List<string> sourceItems = new List<string> ();
  753. var x = 1;
  754. foreach (Item i in sourceContainer.inventory.itemList) {
  755. string n = "";
  756. if (i.IsBlueprint ()) {
  757. n = i.amount + " x <color=lightblue>" + i.blueprintTargetDef.displayName.english + " [" + GetMsg ("Item: BP", player) + "]</color>";
  758. } else {
  759. n = i.amount + " x " + i.info.displayName.english;
  760. if(i.info.condition.enabled)
  761. {
  762. var conditionPercent = System.Math.Round(i.condition * 100 / i.info.condition.max, 0);
  763. if(conditionPercent <= 25f)
  764. {
  765. n += " [<color=red>" + conditionPercent + "%</color>]";
  766. }
  767. else if(conditionPercent <= 75f)
  768. {
  769. n += " [<color=yellow>" + conditionPercent + "%</color>]";
  770. }
  771. else if(conditionPercent <= 99f)
  772. {
  773. n += " [<color=green>" + conditionPercent + "%</color>]";
  774. }
  775. }
  776. }
  777.  
  778. if (x > slotsAvailable) {
  779. n = "<color=red>" + n + "</color>";
  780. }
  781. x++;
  782.  
  783. sourceItems.Add (n);
  784. }
  785.  
  786. send = send.Replace ("{sourceitems}", string.Join ("\n", sourceItems.ToArray ()));
  787.  
  788. if (player != target) {
  789. slotsAvailable = player.inventory.containerMain.capacity - (player.inventory.containerMain.itemList.Count);
  790. List<string> targetItems = new List<string> ();
  791. x = 1;
  792. if (targetContainer != null) {
  793. foreach (Item i in targetContainer.inventory.itemList) {
  794. string n2 = "";
  795. if (i.IsBlueprint ()) {
  796. n2 = i.amount + " x <color=lightblue>" + i.blueprintTargetDef.displayName.english + " [" + GetMsg ("Item: BP", player) + "]</color>";
  797. } else {
  798. n2 = i.amount + " x " + i.info.displayName.english;
  799. if (i.info.condition.enabled)
  800. {
  801. var conditionPercent = System.Math.Round(i.condition * 100 / i.info.condition.max, 0);
  802. if (conditionPercent <= 25f)
  803. {
  804. n2 += " [<color=red>" + conditionPercent + "%</color>]";
  805. }
  806. else if (conditionPercent <= 75f)
  807. {
  808. n2 += " [<color=yellow>" + conditionPercent + "%</color>]";
  809. }
  810. else if (conditionPercent <= 99f)
  811. {
  812. n2 += " [<color=green>" + conditionPercent + "%</color>]";
  813. }
  814. }
  815. }
  816. if (x > slotsAvailable) {
  817. n2 = "<color=red>" + n2 + "</color>";
  818. }
  819. x++;
  820. targetItems.Add (n2);
  821. }
  822. }
  823.  
  824. send = send.Replace ("{targetitems}", string.Join ("\n", targetItems.ToArray ()));
  825. } else {
  826. send = send.Replace ("{targetitems}", "");
  827. }
  828.  
  829. CommunityEntity.ServerInstance.ClientRPCEx (new Network.SendInfo { connection = player.net.connection }, null, "AddUI", send);
  830. }
  831.  
  832. private void HideTrade (BasePlayer player)
  833. {
  834. if (player.IsConnected) {
  835. CommunityEntity.ServerInstance.ClientRPCEx (new Network.SendInfo { connection = player.net.connection }, null, "DestroyUI", "TradeMsg");
  836. }
  837. }
  838.  
  839. #endregion
  840.  
  841. #region Core Methods
  842.  
  843. bool CheckCooldown (BasePlayer player)
  844. {
  845. if (cooldownMinutes > 0) {
  846. DateTime startTime;
  847. if (tradeCooldowns.TryGetValue (player.UserIDString, out startTime)) {
  848. var endTime = DateTime.Now;
  849.  
  850. var span = endTime.Subtract (startTime);
  851. if (span.TotalMinutes > 0 && span.TotalMinutes < Convert.ToDouble (cooldownMinutes)) {
  852. double timeleft = System.Math.Round (Convert.ToDouble (cooldownMinutes) - span.TotalMinutes, 2);
  853. if (timeleft < 1) {
  854. double timelefts = System.Math.Round ((Convert.ToDouble (cooldownMinutes) * 60) - span.TotalSeconds);
  855. SendReply (player, string.Format (GetMsg ("Cooldown: Seconds", player), timelefts.ToString ()));
  856. } else {
  857. SendReply (player, string.Format (GetMsg ("Cooldown: Minutes", player), System.Math.Round (timeleft).ToString ()));
  858. }
  859. return false;
  860. } else {
  861. tradeCooldowns.Remove (player.UserIDString);
  862. }
  863. }
  864. }
  865.  
  866. return true;
  867. }
  868.  
  869. void TradeCloseBoxes (OpenTrade trade)
  870. {
  871. if (trade.IsSourceValid ()) {
  872. CloseBoxView (trade.sourcePlayer, trade.source.View);
  873. }
  874.  
  875. if (trade.IsTargetValid () && trade.targetPlayer != trade.sourcePlayer) {
  876. CloseBoxView (trade.targetPlayer, trade.target.View);
  877. }
  878. }
  879.  
  880. void TradeReply (OpenTrade trade, string msg, string msg2 = null)
  881. {
  882. if (msg2 == null) {
  883. msg2 = msg;
  884. }
  885.  
  886. if (trade.targetPlayer != null) {
  887. SendReply (trade.targetPlayer, GetMsg (msg, trade.targetPlayer));
  888. }
  889.  
  890. if (trade.sourcePlayer != null) {
  891. SendReply (trade.sourcePlayer, GetMsg (msg2, trade.sourcePlayer));
  892. }
  893. }
  894.  
  895. void ShowTrades (OpenTrade trade, string msg)
  896. {
  897. ShowTrade (trade.sourcePlayer, trade, GetMsg (msg, trade.sourcePlayer));
  898. ShowTrade (trade.targetPlayer, trade, GetMsg (msg, trade.targetPlayer));
  899. }
  900.  
  901. void TradeCooldown (OpenTrade trade)
  902. {
  903. PlayerCooldown (trade.targetPlayer);
  904. PlayerCooldown (trade.sourcePlayer);
  905. }
  906.  
  907. void PlayerCooldown (BasePlayer player)
  908. {
  909. if (player.IsAdmin) {
  910. return;
  911. }
  912. if (tradeCooldowns.ContainsKey (player.UserIDString)) {
  913. tradeCooldowns.Remove (player.UserIDString);
  914. }
  915.  
  916. tradeCooldowns.Add (player.UserIDString, DateTime.Now);
  917. }
  918.  
  919. void FinishTrade (OpenTrade t)
  920. {
  921. foreach (var i in t.source.View.inventory.itemList.ToArray ()) {
  922. i.MoveToContainer (t.target.containerMain);
  923. }
  924.  
  925. foreach (var i in t.target.View.inventory.itemList.ToArray ()) {
  926. i.MoveToContainer (t.source.containerMain);
  927. }
  928.  
  929. TradeCloseBoxes (t);
  930. }
  931.  
  932. void AcceptTrade (BasePlayer player)
  933. {
  934. BasePlayer source = null;
  935.  
  936. PendingTrade pendingTrade = null;
  937.  
  938. foreach (KeyValuePair<BasePlayer, PendingTrade> kvp in pendingTrades) {
  939. if (kvp.Value.Target == player) {
  940. pendingTrade = kvp.Value;
  941. source = kvp.Key;
  942. break;
  943. }
  944. }
  945.  
  946. if (source != null && pendingTrade != null) {
  947. pendingTrade.Destroy ();
  948. pendingTrades.Remove (source);
  949. StartTrades (source, player);
  950. } else {
  951. SendReply (player, GetMsg ("Status: No Pending", player));
  952. }
  953. }
  954.  
  955. void StartTrades (BasePlayer source, BasePlayer target)
  956. {
  957. var trade = new OpenTrade (onlinePlayers [source], onlinePlayers [target]);
  958. StartTrade (source, target, trade);
  959. if (source != target) {
  960. StartTrade (target, source, trade);
  961. }
  962. }
  963.  
  964. void StartTrade (BasePlayer source, BasePlayer target, OpenTrade trade)
  965. {
  966. OpenBox (source, source);
  967.  
  968. if (!openTrades.Contains (trade)) {
  969. openTrades.Add (trade);
  970. }
  971. onlinePlayers [source].Trade = trade;
  972.  
  973. timer.In (0.1f, () => ShowTrade (source, trade, GetMsg ("Trade: Pending", source)));
  974. }
  975.  
  976. void OpenBox (BasePlayer player, BaseEntity target)
  977. {
  978. SubscribeAll ();
  979. var ply = onlinePlayers [player];
  980. if (ply.View == null) {
  981. OpenBoxView (player, target);
  982. return;
  983. }
  984.  
  985. CloseBoxView (player, ply.View);
  986. timer.In (1f, () => OpenBoxView (player, target));
  987. }
  988.  
  989. void OpenBoxView (BasePlayer player, BaseEntity targArg)
  990. {
  991. var pos = new Vector3 (player.transform.position.x, player.transform.position.y - 1, player.transform.position.z);
  992. var boxContainer = GameManager.server.CreateEntity (box, pos) as StorageContainer;
  993. boxContainer.GetComponent<DestroyOnGroundMissing> ().enabled = false;
  994. boxContainer.GetComponent<GroundWatch> ().enabled = false;
  995. boxContainer.transform.position = pos;
  996.  
  997. // Custom Code
  998. DestroyMeshCollider(boxContainer);
  999. boxContainer.SetParent(player, true, true);
  1000. // end of custom code
  1001.  
  1002. if (!boxContainer) return;
  1003.  
  1004. StorageContainer view = boxContainer as StorageContainer;
  1005. view.limitNetworking = true;
  1006. player.EndLooting ();
  1007. if (targArg is BasePlayer) {
  1008.  
  1009. BasePlayer target = targArg as BasePlayer;
  1010. ItemContainer container = new ItemContainer ();
  1011. container.playerOwner = player;
  1012. container.ServerInitialize (null, slots);
  1013. if (container.uid.Value == 0)
  1014. container.GiveUID ();
  1015.  
  1016. view.enableSaving = false;
  1017. view.Spawn ();
  1018. view._inventory = container;
  1019.  
  1020. onlinePlayers [player].View = view;
  1021. timer.In (0.1f, () => view.PlayerOpenLoot (player));
  1022. }
  1023. }
  1024.  
  1025. private void DestroyMeshCollider(BaseEntity entity)
  1026. {
  1027. foreach (MeshCollider collider in entity.GetComponentsInChildren<MeshCollider>())
  1028. {
  1029. UnityEngine.Object.DestroyImmediate(collider);
  1030. }
  1031. }
  1032.  
  1033. void CloseBoxView (BasePlayer player, StorageContainer view)
  1034. {
  1035.  
  1036. OnlinePlayer onlinePlayer;
  1037. if (!onlinePlayers.TryGetValue (player, out onlinePlayer)) return;
  1038. if (onlinePlayer.View == null) return;
  1039.  
  1040. HideTrade (player);
  1041. if (onlinePlayer.Trade != null) {
  1042. OpenTrade t = onlinePlayer.Trade;
  1043. t.closing = true;
  1044.  
  1045. if (t.sourcePlayer == player && t.targetPlayer != player && t.target.View != null) {
  1046. t.target.Trade = null;
  1047. CloseBoxView (t.targetPlayer, t.target.View);
  1048. } else if (t.targetPlayer == player && t.sourcePlayer != player && t.source.View != null) {
  1049. t.source.Trade = null;
  1050. CloseBoxView (t.sourcePlayer, t.source.View);
  1051. }
  1052.  
  1053. if (openTrades.Contains (t)) {
  1054. openTrades.Remove (t);
  1055. }
  1056. }
  1057.  
  1058. if (view.inventory.itemList.Count > 0) {
  1059. foreach (Item item in view.inventory.itemList.ToArray ()) {
  1060. if (item.position != -1) {
  1061. item.MoveToContainer (player.inventory.containerMain);
  1062. }
  1063. }
  1064. }
  1065.  
  1066. if (view.inventory.itemList.Count > 0) {
  1067. foreach (Item item in view.inventory.itemList.ToArray ()) {
  1068. if (item.position != -1) {
  1069. item.MoveToContainer (player.inventory.containerBelt);
  1070. }
  1071. }
  1072. }
  1073.  
  1074. if (player.inventory.loot.entitySource != null) {
  1075. player.inventory.loot.Invoke ("SendUpdate", 0.1f);
  1076. view.SendMessage ("PlayerStoppedLooting", player, SendMessageOptions.DontRequireReceiver);
  1077. player.SendConsoleCommand ("inventory.endloot", null);
  1078. }
  1079.  
  1080. player.inventory.loot.entitySource = null;
  1081. player.inventory.loot.itemSource = null;
  1082. player.inventory.loot.containers = new List<ItemContainer> ();
  1083.  
  1084. view._inventory = new ItemContainer ();
  1085.  
  1086. onlinePlayer.Clear ();
  1087.  
  1088. view.Kill ();
  1089.  
  1090. if (onlinePlayers.Values.Count (p => p.View != null) <= 0) {
  1091. UnsubscribeAll ();
  1092. }
  1093. }
  1094.  
  1095. bool CanPlayerTrade (BasePlayer player, string perm)
  1096. {
  1097. if (!permission.UserHasPermission (player.UserIDString, perm)) {
  1098. SendReply (player, GetMsg ("Denied: Permission", player));
  1099. return false;
  1100. }
  1101.  
  1102. if (!player.CanBuild ()) {
  1103. SendReply (player, GetMsg ("Denied: Privilege", player));
  1104. return false;
  1105. }
  1106.  
  1107. if (radiationMax > 0 && player.radiationLevel > radiationMax) {
  1108. SendReply (player, GetMsg ("Denied: Irradiated", player));
  1109. return false;
  1110. }
  1111.  
  1112. if (player.IsSwimming ()) {
  1113. SendReply (player, GetMsg ("Denied: Swimming", player));
  1114. return false;
  1115. }
  1116.  
  1117. if (!player.IsOnGround () || player.IsFlying || player.isInAir) {
  1118. SendReply (player, GetMsg ("Denied: Falling", player));
  1119. return false;
  1120. }
  1121.  
  1122. if (player.isMounted) {
  1123. SendReply (player, GetMsg ("Denied: Mounted", player));
  1124. return false;
  1125. }
  1126.  
  1127. if (player.IsWounded ()) {
  1128. SendReply (player, GetMsg ("Denied: Wounded", player));
  1129. return false;
  1130. }
  1131.  
  1132. if (player.GetComponentInParent<CargoShip> ()) {
  1133. SendReply (player, GetMsg ("Denied: Ship", player));
  1134. return false;
  1135. }
  1136.  
  1137. if (player.GetComponentInParent<HotAirBalloon> ()) {
  1138. SendReply (player, GetMsg ("Denied: Balloon", player));
  1139. return false;
  1140. }
  1141.  
  1142. if (player.GetComponentInParent<Lift> ()) {
  1143. SendReply (player, GetMsg ("Denied: Lift", player));
  1144. return false;
  1145. }
  1146.  
  1147. if (!allowSafeZone && player.InSafeZone ()) {
  1148. SendReply (player, GetMsg ("Denied: Safe Zone", player));
  1149. return false;
  1150. }
  1151.  
  1152. var canTrade = Interface.Call ("CanTrade", player);
  1153. if (canTrade != null) {
  1154. if (canTrade is string) {
  1155. SendReply (player, Convert.ToString (canTrade));
  1156. } else {
  1157. SendReply (player, GetMsg ("Denied: Generic", player));
  1158. }
  1159. return false;
  1160. }
  1161.  
  1162. return true;
  1163. }
  1164.  
  1165. #endregion
  1166.  
  1167. #region HelpText
  1168. private void SendHelpText (BasePlayer player)
  1169. {
  1170. var sb = new StringBuilder ()
  1171. .Append ("Trade by <color=#ce422b>http://rustservers.io</color>\n")
  1172. .Append (" ").Append ("<color=\"#ffd479\">/trade \"Player Name\"</color> - Send trade request").Append ("\n")
  1173. .Append (" ").Append ("<color=\"#ffd479\">/trade accept</color> - Accept trade request").Append ("\n");
  1174. player.ChatMessage (sb.ToString ());
  1175. }
  1176. #endregion
  1177.  
  1178. #region Helper methods
  1179.  
  1180. private bool IsTradeBox (BaseNetworkable entity)
  1181. {
  1182. foreach (KeyValuePair<BasePlayer, OnlinePlayer> kvp in onlinePlayers) {
  1183. if (kvp.Value.View != null && kvp.Value.View.net != null && entity.net != null && kvp.Value.View.net.ID == entity.net.ID) {
  1184. return true;
  1185. }
  1186. }
  1187.  
  1188. return false;
  1189. }
  1190.  
  1191. bool hasAccess (BasePlayer player, string permissionname)
  1192. {
  1193. if (player.IsAdmin) return true;
  1194. return permission.UserHasPermission (player.UserIDString, permissionname);
  1195. }
  1196.  
  1197. private BasePlayer FindPlayerByPartialName (string name)
  1198. {
  1199. if (string.IsNullOrEmpty (name))
  1200. return null;
  1201. BasePlayer player = null;
  1202. name = name.ToLower ();
  1203. var awakePlayers = BasePlayer.activePlayerList;
  1204. foreach (var p in awakePlayers) {
  1205. if (p.net == null || p.net.connection == null)
  1206. continue;
  1207.  
  1208. if (p.displayName == name) {
  1209. if (player != null)
  1210. return null;
  1211. player = p;
  1212. }
  1213. }
  1214.  
  1215. if (player != null)
  1216. return player;
  1217. foreach (var p in awakePlayers) {
  1218. if (p.net == null || p.net.connection == null)
  1219. continue;
  1220.  
  1221. if (p.displayName.ToLower ().IndexOf (name) >= 0) {
  1222. if (player != null)
  1223. return null;
  1224. player = p;
  1225. }
  1226. }
  1227.  
  1228. return player;
  1229. }
  1230.  
  1231. private T GetConfig<T> (string name, T defaultValue)
  1232. {
  1233. if (Config [name] == null) {
  1234. return defaultValue;
  1235. }
  1236.  
  1237. return (T)Convert.ChangeType (Config [name], typeof (T));
  1238. }
  1239.  
  1240. private T GetConfig<T> (string name, string name2, T defaultValue)
  1241. {
  1242. if (Config [name, name2] == null) {
  1243. return defaultValue;
  1244. }
  1245.  
  1246. return (T)Convert.ChangeType (Config [name, name2], typeof (T));
  1247. }
  1248.  
  1249. string GetMsg (string key, BasePlayer player = null)
  1250. {
  1251. return lang.GetMessage (key, this, player == null ? null : player.UserIDString);
  1252. }
  1253.  
  1254. private string CleanName (string name)
  1255. {
  1256. return JsonConvert.ToString (name.Trim ()).Replace ("\"", "");
  1257. }
  1258.  
  1259. #endregion
  1260. }
  1261. }
Add Comment
Please, Sign In to add comment