Decompiled source of Marketplace v1.5.0

Marketplace.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Managers;
using Jotunn.Utils;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace Marketplace;

public class ItemRecord
{
	private const int Version = 1;

	public string Prefab = "";

	public int Stack = 1;

	public int Quality = 1;

	public int Variant;

	public float Durability;

	public long CrafterID;

	public string CrafterName = "";

	public int WorldLevel;

	public Dictionary<string, string> CustomData = new Dictionary<string, string>();

	public void Write(ZPackage pkg)
	{
		pkg.Write(1);
		pkg.Write(Prefab);
		pkg.Write(Stack);
		pkg.Write(Quality);
		pkg.Write(Variant);
		pkg.Write(Durability);
		pkg.Write(CrafterID);
		pkg.Write(CrafterName ?? "");
		pkg.Write(WorldLevel);
		pkg.Write(CustomData.Count);
		foreach (KeyValuePair<string, string> customDatum in CustomData)
		{
			pkg.Write(customDatum.Key);
			pkg.Write(customDatum.Value ?? "");
		}
	}

	public static ItemRecord Read(ZPackage pkg)
	{
		ItemRecord itemRecord = new ItemRecord();
		pkg.ReadInt();
		itemRecord.Prefab = pkg.ReadString();
		itemRecord.Stack = pkg.ReadInt();
		itemRecord.Quality = pkg.ReadInt();
		itemRecord.Variant = pkg.ReadInt();
		itemRecord.Durability = pkg.ReadSingle();
		itemRecord.CrafterID = pkg.ReadLong();
		itemRecord.CrafterName = pkg.ReadString();
		itemRecord.WorldLevel = pkg.ReadInt();
		int num = pkg.ReadInt();
		for (int i = 0; i < num; i++)
		{
			string key = pkg.ReadString();
			itemRecord.CustomData[key] = pkg.ReadString();
		}
		return itemRecord;
	}

	public ItemRecord Clone(int stack)
	{
		ItemRecord itemRecord = (ItemRecord)MemberwiseClone();
		itemRecord.CustomData = new Dictionary<string, string>(CustomData);
		itemRecord.Stack = stack;
		return itemRecord;
	}

	public static ItemRecord FromItem(ItemData item, int amount)
	{
		ItemRecord itemRecord = new ItemRecord();
		itemRecord.Prefab = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : "");
		itemRecord.Stack = amount;
		itemRecord.Quality = item.m_quality;
		itemRecord.Variant = item.m_variant;
		itemRecord.Durability = item.m_durability;
		itemRecord.CrafterID = item.m_crafterID;
		itemRecord.CrafterName = item.m_crafterName ?? "";
		itemRecord.WorldLevel = item.m_worldLevel;
		if (item.m_customData != null)
		{
			itemRecord.CustomData = new Dictionary<string, string>(item.m_customData);
		}
		return itemRecord;
	}

	public static bool PrefabExists(string prefab)
	{
		if ((Object)(object)ObjectDB.instance == (Object)null || string.IsNullOrEmpty(prefab))
		{
			return false;
		}
		GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefab);
		if ((Object)(object)itemPrefab != (Object)null)
		{
			return (Object)(object)itemPrefab.GetComponent<ItemDrop>() != (Object)null;
		}
		return false;
	}

	public ItemData ToItemData()
	{
		if ((Object)(object)ObjectDB.instance == (Object)null)
		{
			return null;
		}
		GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(Prefab);
		if ((Object)(object)itemPrefab == (Object)null)
		{
			return null;
		}
		ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
		if ((Object)(object)component == (Object)null)
		{
			return null;
		}
		ItemData val = component.m_itemData.Clone();
		val.m_dropPrefab = itemPrefab;
		val.m_stack = Stack;
		val.m_quality = Quality;
		val.m_variant = Variant;
		val.m_durability = Durability;
		val.m_crafterID = CrafterID;
		val.m_crafterName = CrafterName;
		val.m_worldLevel = WorldLevel;
		val.m_customData = new Dictionary<string, string>(CustomData);
		val.m_equipped = false;
		return val;
	}
}
public class Listing
{
	public long Id;

	public long SellerId;

	public string SellerName = "";

	public int Price;

	public long CreatedTicks;

	public ItemRecord Item;

	public void Write(ZPackage pkg)
	{
		pkg.Write(Id);
		pkg.Write(SellerId);
		pkg.Write(SellerName ?? "");
		pkg.Write(Price);
		pkg.Write(CreatedTicks);
		Item.Write(pkg);
	}

	public static Listing Read(ZPackage pkg)
	{
		Listing listing = new Listing();
		listing.Id = pkg.ReadLong();
		listing.SellerId = pkg.ReadLong();
		listing.SellerName = pkg.ReadString();
		listing.Price = pkg.ReadInt();
		listing.CreatedTicks = pkg.ReadLong();
		listing.Item = ItemRecord.Read(pkg);
		return listing;
	}
}
public enum HistoryKind
{
	Deposit,
	Withdraw,
	Sale,
	Purchase,
	OrderEscrow,
	OrderRefund,
	OrderSale,
	OrderFilled,
	BountyEscrow,
	BountyRefund,
	BountyReward,
	BountyPaid,
	GuildPayment,
	GuildPayout
}
public class HistoryEntry
{
	public HistoryKind Kind;

	public long Ticks;

	public long Amount;

	public long Fee;

	public string Prefab = "";

	public int Stack;

	public string Other = "";

	public string Text = "";

	public void Write(ZPackage pkg)
	{
		pkg.Write((int)Kind);
		pkg.Write(Ticks);
		pkg.Write(Amount);
		pkg.Write(Fee);
		pkg.Write(Prefab ?? "");
		pkg.Write(Stack);
		pkg.Write(Other ?? "");
		pkg.Write(Text ?? "");
	}

	public static HistoryEntry Read(ZPackage pkg, bool hasText)
	{
		HistoryEntry historyEntry = ReadCore(pkg);
		if (hasText)
		{
			historyEntry.Text = pkg.ReadString();
		}
		return historyEntry;
	}

	private static HistoryEntry ReadCore(ZPackage pkg)
	{
		HistoryEntry historyEntry = new HistoryEntry();
		historyEntry.Kind = (HistoryKind)pkg.ReadInt();
		historyEntry.Ticks = pkg.ReadLong();
		historyEntry.Amount = pkg.ReadLong();
		historyEntry.Fee = pkg.ReadLong();
		historyEntry.Prefab = pkg.ReadString();
		historyEntry.Stack = pkg.ReadInt();
		historyEntry.Other = pkg.ReadString();
		return historyEntry;
	}
}
public class Account
{
	public const int MaxHistory = 50;

	public string Name = "";

	public long Balance;

	public List<ItemRecord> Items = new List<ItemRecord>();

	public List<HistoryEntry> History = new List<HistoryEntry>();

	public bool IsEmpty
	{
		get
		{
			if (Balance <= 0 && Items.Count == 0)
			{
				return History.Count == 0;
			}
			return false;
		}
	}

	public void AddHistory(HistoryEntry h)
	{
		h.Ticks = DateTime.UtcNow.Ticks;
		History.Insert(0, h);
		if (History.Count > 50)
		{
			History.RemoveRange(50, History.Count - 50);
		}
	}
}
public class BuyOrder
{
	public long Id;

	public long BuyerId;

	public string BuyerName = "";

	public string Prefab = "";

	public int Remaining;

	public int Delivered;

	public int Price;

	public long CreatedTicks;

	public long Escrow => (long)Remaining * (long)Price;

	public void Write(ZPackage pkg)
	{
		pkg.Write(Id);
		pkg.Write(BuyerId);
		pkg.Write(BuyerName ?? "");
		pkg.Write(Prefab ?? "");
		pkg.Write(Remaining);
		pkg.Write(Delivered);
		pkg.Write(Price);
		pkg.Write(CreatedTicks);
	}

	public static BuyOrder Read(ZPackage pkg)
	{
		BuyOrder buyOrder = new BuyOrder();
		buyOrder.Id = pkg.ReadLong();
		buyOrder.BuyerId = pkg.ReadLong();
		buyOrder.BuyerName = pkg.ReadString();
		buyOrder.Prefab = pkg.ReadString();
		buyOrder.Remaining = pkg.ReadInt();
		buyOrder.Delivered = pkg.ReadInt();
		buyOrder.Price = pkg.ReadInt();
		buyOrder.CreatedTicks = pkg.ReadLong();
		return buyOrder;
	}
}
public enum BountyState
{
	Open,
	Claimed,
	Submitted
}
public enum BountyAction
{
	Claim,
	Abandon,
	Submit,
	Confirm,
	Reject,
	Cancel
}
public class Bounty
{
	public const int MaxTitle = 60;

	public const int MaxDescription = 400;

	public long Id;

	public long OwnerId;

	public string OwnerName = "";

	public string Title = "";

	public string Description = "";

	public int Reward;

	public long CreatedTicks;

	public BountyState State;

	public long ContractorId;

	public string ContractorName = "";

	public long StateTicks;

	public void Write(ZPackage pkg)
	{
		pkg.Write(Id);
		pkg.Write(OwnerId);
		pkg.Write(OwnerName ?? "");
		pkg.Write(Title ?? "");
		pkg.Write(Description ?? "");
		pkg.Write(Reward);
		pkg.Write(CreatedTicks);
		pkg.Write((int)State);
		pkg.Write(ContractorId);
		pkg.Write(ContractorName ?? "");
		pkg.Write(StateTicks);
	}

	public static Bounty Read(ZPackage pkg)
	{
		Bounty bounty = new Bounty();
		bounty.Id = pkg.ReadLong();
		bounty.OwnerId = pkg.ReadLong();
		bounty.OwnerName = pkg.ReadString();
		bounty.Title = pkg.ReadString();
		bounty.Description = pkg.ReadString();
		bounty.Reward = pkg.ReadInt();
		bounty.CreatedTicks = pkg.ReadLong();
		bounty.State = (BountyState)pkg.ReadInt();
		bounty.ContractorId = pkg.ReadLong();
		bounty.ContractorName = pkg.ReadString();
		bounty.StateTicks = pkg.ReadLong();
		return bounty;
	}
}
public class PriceStat
{
	public string Prefab = "";

	public int Avg;

	public int Min;

	public int Max;

	public int Trades;

	public int Volume;

	public long LastTicks;

	public void Write(ZPackage pkg)
	{
		pkg.Write(Prefab ?? "");
		pkg.Write(Avg);
		pkg.Write(Min);
		pkg.Write(Max);
		pkg.Write(Trades);
		pkg.Write(Volume);
		pkg.Write(LastTicks);
	}

	public static PriceStat Read(ZPackage pkg)
	{
		PriceStat priceStat = new PriceStat();
		priceStat.Prefab = pkg.ReadString();
		priceStat.Avg = pkg.ReadInt();
		priceStat.Min = pkg.ReadInt();
		priceStat.Max = pkg.ReadInt();
		priceStat.Trades = pkg.ReadInt();
		priceStat.Volume = pkg.ReadInt();
		priceStat.LastTicks = pkg.ReadLong();
		return priceStat;
	}
}
public enum ItemCategory
{
	All,
	Weapons,
	Armor,
	Tools,
	Materials,
	Food,
	Potions,
	Other
}
internal static class Categories
{
	public const int Count = 8;

	public static readonly string[] Keys = new string[8] { "cat_all", "cat_weapons", "cat_armor", "cat_tools", "cat_materials", "cat_food", "cat_potions", "cat_other" };

	public static ItemCategory Of(ItemData item)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Expected I4, but got Unknown
		if (item == null || item.m_shared == null)
		{
			return ItemCategory.Other;
		}
		SharedData shared = item.m_shared;
		ItemType itemType = shared.m_itemType;
		switch (itemType - 1)
		{
		case 2:
		case 3:
		case 8:
		case 13:
		case 14:
		case 19:
		case 21:
		case 22:
			return ItemCategory.Weapons;
		case 4:
		case 5:
		case 6:
		case 10:
		case 11:
		case 16:
		case 17:
		case 23:
			return ItemCategory.Armor;
		case 18:
			return ItemCategory.Tools;
		case 0:
		case 20:
			return ItemCategory.Materials;
		case 1:
			if (!(shared.m_food > 0f) && !(shared.m_foodStamina > 0f) && !(shared.m_foodEitr > 0f))
			{
				return ItemCategory.Potions;
			}
			return ItemCategory.Food;
		default:
			return ItemCategory.Other;
		}
	}

	public static bool Matches(ItemData item, ItemCategory filter)
	{
		if (filter != ItemCategory.All)
		{
			return Of(item) == filter;
		}
		return true;
	}
}
internal static class Rpc
{
	public const string RequestData = "Marketplace_RequestData";

	public const string Create = "Marketplace_Create";

	public const string Buy = "Marketplace_Buy";

	public const string Cancel = "Marketplace_Cancel";

	public const string Claim = "Marketplace_Claim";

	public const string Deposit = "Marketplace_Deposit";

	public const string Withdraw = "Marketplace_Withdraw";

	public const string OrderCreate = "Marketplace_OrderCreate";

	public const string OrderFill = "Marketplace_OrderFill";

	public const string OrderCancel = "Marketplace_OrderCancel";

	public const string BountyCreate = "Marketplace_BountyCreate";

	public const string BountyAct = "Marketplace_BountyAction";

	public const string Data = "Marketplace_Data";

	public const string Result = "Marketplace_Result";

	public const string Deliver = "Marketplace_Deliver";

	public const string Changed = "Marketplace_Changed";

	public const string CoinsPrefab = "Coins";

	public const string CoinsName = "$item_coins";

	public static void Register()
	{
		ZRoutedRpc instance = ZRoutedRpc.instance;
		if (instance != null)
		{
			instance.Register("Marketplace_RequestData", (Action<long>)MarketServer.RPC_RequestData);
			instance.Register<ZPackage>("Marketplace_Create", (Action<long, ZPackage>)MarketServer.RPC_Create);
			instance.Register<ZPackage>("Marketplace_Buy", (Action<long, ZPackage>)MarketServer.RPC_Buy);
			instance.Register<long>("Marketplace_Cancel", (Action<long, long>)MarketServer.RPC_Cancel);
			instance.Register("Marketplace_Claim", (Action<long>)MarketServer.RPC_Claim);
			instance.Register<int>("Marketplace_Deposit", (Action<long, int>)MarketServer.RPC_Deposit);
			instance.Register<int>("Marketplace_Withdraw", (Action<long, int>)MarketServer.RPC_Withdraw);
			instance.Register<ZPackage>("Marketplace_OrderCreate", (Action<long, ZPackage>)MarketServer.RPC_OrderCreate);
			instance.Register<ZPackage>("Marketplace_OrderFill", (Action<long, ZPackage>)MarketServer.RPC_OrderFill);
			instance.Register<long>("Marketplace_OrderCancel", (Action<long, long>)MarketServer.RPC_OrderCancel);
			instance.Register<ZPackage>("Marketplace_BountyCreate", (Action<long, ZPackage>)MarketServer.RPC_BountyCreate);
			instance.Register<ZPackage>("Marketplace_BountyAction", (Action<long, ZPackage>)MarketServer.RPC_BountyAction);
			instance.Register<ZPackage>("Marketplace_Data", (Action<long, ZPackage>)MarketClient.RPC_Data);
			instance.Register<ZPackage>("Marketplace_Result", (Action<long, ZPackage>)MarketClient.RPC_Result);
			instance.Register<ZPackage>("Marketplace_Deliver", (Action<long, ZPackage>)MarketClient.RPC_Deliver);
			instance.Register("Marketplace_Changed", (Action<long>)MarketClient.RPC_Changed);
		}
	}

	public static long ServerId()
	{
		if ((Object)(object)ZNet.instance == (Object)null)
		{
			return 0L;
		}
		if (ZNet.instance.IsServer())
		{
			return ZDOMan.GetSessionID();
		}
		return ZNet.instance.GetServerPeer()?.m_uid ?? 0;
	}
}
public enum MarketLanguage
{
	English,
	Polish
}
internal static class Loc
{
	private static readonly Dictionary<string, string> En = new Dictionary<string, string>
	{
		{ "title", "MARKETPLACE" },
		{ "tab_buy", "MARKETPLACE" },
		{ "tab_sell", "SELL" },
		{ "tab_mine", "MY LISTINGS" },
		{ "tab_mail", "MAILBOX" },
		{ "tab_bank", "BANK" },
		{ "hdr_inventory", "Inventory" },
		{ "hdr_balance", "Balance" },
		{ "connecting", "Connecting to the marketplace…" },
		{ "search", "Search item or seller…" },
		{ "sort_new", "Sort: newest" },
		{ "sort_cheap", "Sort: cheapest" },
		{ "sort_expensive", "Sort: most expensive" },
		{ "sort_name", "Sort: name" },
		{ "pick", "Select an entry from the list on the left." },
		{ "loading", "Loading listings…" },
		{ "no_match", "Nothing matches your search." },
		{ "no_listings", "There are no listings yet.\nList something in the SELL tab!" },
		{ "nothing_to_sell", "You have nothing that can be listed.\nEquipped items and coins are skipped." },
		{ "no_own", "You have no active listings." },
		{ "mail_empty_list", "Your mailbox is empty.\nItems from cancelled and expired listings end up here." },
		{ "no_history", "No transactions yet." },
		{ "your_listing", "Your listing" },
		{ "expires_in", "expires in {0}" },
		{ "trader_value", "Trader value: {0} / pc" },
		{ "seller", "Seller: {0}" },
		{ "price_each", "Price per piece: <color=#FFD27A>{0}</color> coins" },
		{ "available", "Available: {0} pcs" },
		{ "remaining", "Remaining: {0} pcs" },
		{ "listed_ago", "Listed {0} ago" },
		{ "expires_in_cap", "Expires in {0}" },
		{ "no_expiry", "Does not expire" },
		{ "in_inventory", "In inventory: {0} pcs" },
		{ "my_listings_count", "Your listings: {0} / {1}" },
		{ "expires_after", "Listing expires after {0} h" },
		{ "never_expires", "Listings never expire" },
		{ "qty", "Amount:" },
		{ "price", "Price / pc:" },
		{ "max", "Max" },
		{ "missing_mod", "<color=#FF7766>This item comes from a mod you do not have installed.</color>" },
		{ "quality", "lvl {0}" },
		{ "mail_title", "Your mailbox" },
		{ "mail_desc", "Items from cancelled and expired listings, and purchases that could not be delivered, wait here.\n\n\"Collect all\" moves them to your inventory. Whatever does not fit is dropped next to you." },
		{ "mail_items", "Items: {0}" },
		{ "total_buy", "Total: {0} coins" },
		{ "pay_split", "From balance {0}, from inventory {1}" },
		{ "total_sell", "You will receive: {0} coins" },
		{ "total_sell_fee", "Total {0}, fee {1}% – you receive {2}" },
		{ "btn_own", "This is your listing" },
		{ "btn_poor", "Not enough coins" },
		{ "btn_buy", "Buy for {0}" },
		{ "btn_need_price", "Enter a price" },
		{ "btn_max_price", "Max {0} / pc" },
		{ "btn_sell", "List for sale" },
		{ "btn_cancel", "Cancel listing" },
		{ "btn_collect", "Collect all" },
		{ "btn_wait", "Waiting for server…" },
		{ "bank_title", "Marketplace bank" },
		{ "bank_desc", "Coins in your balance are stored on the server, not in your inventory — you cannot lose them on death. Sale income lands here and purchases are paid from here first." },
		{ "bank_balance", "Balance: <color=#FFD27A>{0}</color> coins" },
		{ "bank_inventory", "In inventory: {0} coins" },
		{ "bank_amount", "Coins:" },
		{ "btn_deposit", "Deposit" },
		{ "btn_withdraw", "Withdraw" },
		{ "btn_deposit_all", "Deposit all" },
		{ "btn_withdraw_all", "Withdraw all" },
		{ "h_deposit", "Deposit" },
		{ "h_withdraw", "Withdrawal" },
		{ "h_sale", "Sale: {0}" },
		{ "h_purchase", "Purchase: {0}" },
		{ "h_to", "to {0}" },
		{ "h_from", "from {0}" },
		{ "h_fee", "fee {0}" },
		{ "ago", "{0} ago" },
		{ "span_d", "{0} d {1} h" },
		{ "span_h", "{0} h {1} min" },
		{ "span_m", "{0} min" },
		{ "inv_full_drop", "Inventory full — some items were dropped next to you" },
		{ "unknown_prefab", "Unknown item {0} — missing mod?" },
		{ "hud_prefix", "Marketplace: {0}" },
		{ "c_no_player", "No character." },
		{ "c_busy", "Wait for the server to respond." },
		{ "c_item_gone", "This item is no longer in your inventory." },
		{ "c_unequip", "Unequip this item first." },
		{ "c_cannot_list", "This item cannot be listed." },
		{ "c_bad_amount", "Invalid amount." },
		{ "c_need_price", "Enter a price (at least 1 coin)." },
		{ "c_max_price", "The maximum price is {0} coins per piece." },
		{ "c_total_high", "The total price is too high." },
		{ "c_remove_failed", "Could not take the item from your inventory." },
		{ "c_own", "This is your listing." },
		{ "c_poor", "Not enough coins ({0} needed)." },
		{ "c_no_coins_inv", "You do not have that many coins in your inventory." },
		{ "c_no_balance", "You do not have that many coins in your balance." },
		{ "c_no_space", "Not enough inventory space for {0} coins (room for {1})." },
		{ "s_raw", "{0}" },
		{ "s_unresolved_item", "The server could not identify your character — the item was returned." },
		{ "s_unresolved_coins", "The server could not identify your character — your coins were returned." },
		{ "s_bad_amount", "Invalid amount." },
		{ "s_unknown_item", "The server does not know this item (mod missing on the server?)." },
		{ "s_blocked", "This item cannot be listed on the marketplace." },
		{ "s_bad_price", "The price must be between 1 and {0} coins." },
		{ "s_total_high", "The total price is too high." },
		{ "s_limit", "Listing limit reached ({0})." },
		{ "s_create_failed", "{0} The item was returned to you." },
		{ "s_listed", "Listed: {0} for {1} coins each." },
		{ "s_gone", "This listing is no longer available." },
		{ "s_own", "You cannot buy your own listing." },
		{ "s_amount_changed", "The available amount has changed." },
		{ "s_price_changed", "The listing price has changed." },
		{ "s_no_funds", "Not enough coins: {0} needed, {1} in your balance." },
		{ "s_buy_failed_deposit", "{0} The coins you paid stay in your marketplace balance." },
		{ "s_bought", "Bought: {0} for {1} coins." },
		{ "s_sold", "{0} bought {1} from you. +{2} coins added to your marketplace balance." },
		{ "s_not_found", "Listing not found." },
		{ "s_cancelled", "Listing cancelled: {0}." },
		{ "s_mail_empty", "Your mailbox is empty." },
		{ "s_claimed", "Mailbox contents collected." },
		{ "s_expired", "Your listing expired: {0}. The item is waiting in your marketplace mailbox." },
		{ "s_deposited", "Deposited {0} coins. Balance: {1}." },
		{ "s_withdrawn", "Withdrew {0} coins. Balance: {1}." },
		{ "s_withdraw_too_much", "You only have {0} coins in your balance." },
		{ "tab_orders", "BUY ORDERS" },
		{ "tab_jobs", "JOBS" },
		{ "mode_orders", "All buy orders" },
		{ "mode_bounties", "All jobs" },
		{ "mode_new_order", "+ Post a buy order" },
		{ "mode_new_bounty", "+ Post a job" },
		{ "pick_search", "Search for the item you want to buy…" },
		{ "pick_hint", "Type at least 2 letters of the item name." },
		{ "pick_none", "No items match." },
		{ "pick_order_item", "Pick the item you want to buy from the list on the left." },
		{ "no_orders", "There are no buy orders yet.\nPost one with \"+ Post a buy order\"!" },
		{ "no_bounties", "No jobs posted yet.\nPost one with \"+ Post a job\"!" },
		{ "order_row", "Wanted: {0}" },
		{ "order_title", "Wanted: {0}" },
		{ "order_by", "Buyer: {0}" },
		{ "your_order", "Your buy order" },
		{ "order_remaining", "Still wanted: {0} pcs (delivered {1})" },
		{ "order_escrow", "Locked in the order: {0} coins" },
		{ "you_have", "You have: {0}" },
		{ "new_order_title", "Buy: {0}" },
		{ "my_orders_count", "Your buy orders: {0} / {1}" },
		{ "my_bounties_count", "Your jobs: {0} / {1}" },
		{ "order_lock", "Locks {0} coins until delivered" },
		{ "bounty_lock", "Locks {0} coins until you confirm the job" },
		{ "your_bounty", "Your job" },
		{ "bounty_by", "Posted by {0}" },
		{ "bounty_no_desc", "(no description)" },
		{ "bounty_reward", "Reward: <color=#FFD27A>{0}</color> coins" },
		{ "b_open", "Open — waiting for a contractor" },
		{ "b_claimed", "Taken by {0}" },
		{ "b_submitted", "{0} reports the job as done" },
		{ "bounty_timeout_in", "You can cancel if not reported within {0}" },
		{ "new_bounty_title", "New job" },
		{ "bounty_title_label", "Job title" },
		{ "bounty_desc_label", "Description (what exactly, where, by when)" },
		{ "bounty_title_ph", "e.g. Build a bridge over the river near spawn" },
		{ "bounty_reward_label", "Reward:" },
		{ "btn_cancel_order", "Cancel order (refund {0})" },
		{ "btn_no_items", "You don't have this item" },
		{ "btn_fill", "Sell {0} for {1}" },
		{ "btn_admin_cancel", "Admin: cancel" },
		{ "btn_admin_pay", "Admin: pay out" },
		{ "btn_cancel_bounty", "Cancel job (refund {0})" },
		{ "btn_claim", "Take this job" },
		{ "btn_submit", "Report as done" },
		{ "btn_abandon", "Abandon" },
		{ "btn_confirm", "Confirm & pay" },
		{ "btn_reject", "Reject" },
		{ "btn_cancel_inactive", "Cancel (no report)" },
		{ "btn_taken", "Already taken" },
		{ "btn_wait_confirm", "Waiting for confirmation" },
		{ "btn_post_order", "Post buy order" },
		{ "btn_post_bounty", "Post job" },
		{ "btn_need_title", "Enter a title" },
		{ "btn_need_reward", "Enter a reward" },
		{ "h_order_escrow", "Buy order posted: {0}" },
		{ "h_order_refund", "Buy order refund: {0}" },
		{ "h_order_sale", "Sold to buy order: {0}" },
		{ "h_order_filled", "Delivered to your order: {0}" },
		{ "h_bounty_escrow", "Job posted: {0}" },
		{ "h_bounty_refund", "Job refund: {0}" },
		{ "h_bounty_reward", "Job reward: {0}" },
		{ "h_bounty_paid", "Job completed: {0}" },
		{ "h_guild_out", "Paid to guild {0}" },
		{ "h_guild_in", "Payout from guild {0}" },
		{ "h_by", "by {0}" },
		{ "c_pick_item", "Pick an item first." },
		{ "c_order_own", "This is your own buy order." },
		{ "c_not_enough_items", "You don't have that many of this item." },
		{ "c_bounty_title", "Enter a job title." },
		{ "c_bounty_reward", "Enter a reward (at least 1 coin)." },
		{ "s_order_limit", "Buy order limit reached ({0})." },
		{ "s_order_created", "Buy order posted: {0} at {1} coins each. {2} coins locked." },
		{ "s_order_gone", "This buy order no longer exists." },
		{ "s_order_own", "You cannot sell to your own buy order." },
		{ "s_order_wrong_item", "These items do not match the order." },
		{ "s_order_sold", "Sold {0} to a buy order. +{1} coins added to your balance." },
		{ "s_order_filled", "{0} delivered {1} to your buy order. The items are waiting in your MAILBOX." },
		{ "s_order_cancelled", "Buy order cancelled: {0}. {1} coins returned to the balance." },
		{ "s_order_cancelled_admin", "An admin cancelled your buy order: {0}. {1} coins returned to your balance." },
		{ "s_order_expired", "Your buy order expired: {0}. {1} coins returned to your balance." },
		{ "s_bounty_no_title", "The job needs a title." },
		{ "s_bounty_too_long", "The title or description is too long." },
		{ "s_bounty_bad_reward", "The reward must be between 1 and {0} coins." },
		{ "s_bounty_limit", "Job limit reached ({0})." },
		{ "s_bounty_created", "Job posted: \"{0}\" — {1} coins locked until you confirm it is done." },
		{ "s_bounty_gone", "This job no longer exists." },
		{ "s_bounty_not_allowed", "This action is not available right now." },
		{ "s_bounty_claimed", "You took the job: \"{0}\". Report it as done in the JOBS tab when finished." },
		{ "s_bounty_claimed_owner", "{0} took your job: \"{1}\"." },
		{ "s_bounty_abandoned", "You abandoned the job: \"{0}\"." },
		{ "s_bounty_abandoned_owner", "{0} abandoned your job \"{1}\". It is open again." },
		{ "s_bounty_submitted", "You reported \"{0}\" as done. Waiting for confirmation." },
		{ "s_bounty_submitted_owner", "{0} reports your job \"{1}\" as done — check it and confirm or reject it in the JOBS tab." },
		{ "s_bounty_confirmed", "\"{0}\" confirmed — reward paid to {1}." },
		{ "s_bounty_paid", "{0} confirmed \"{1}\". +{2} coins added to your balance." },
		{ "s_bounty_confirmed_admin", "An admin confirmed your job \"{0}\" and paid the reward." },
		{ "s_bounty_rejected", "You rejected the report for \"{0}\". The contractor can continue the job." },
		{ "s_bounty_rejected_contractor", "{0} rejected your report for \"{1}\". Finish the job and report it again." },
		{ "s_bounty_cancelled", "Job cancelled: \"{0}\". {1} coins returned to the balance." },
		{ "s_bounty_cancelled_contractor", "The job \"{0}\" you were working on was cancelled." },
		{ "s_bounty_cancelled_admin", "An admin cancelled your job \"{0}\". {1} coins returned to your balance." },
		{ "s_bounty_expired", "Nobody took your job \"{0}\". {1} coins returned to your balance." },
		{ "price_stats", "Market price: avg <color=#FFD27A>{0}</color>, range {1}–{2} (trades: {3})" },
		{ "price_stats_one", "Market price: <color=#FFD27A>{0}</color> (trades: {1})" },
		{ "price_none", "Market price: no trades yet" },
		{ "avg_short", "market avg {0}" },
		{ "cat_all", "All categories" },
		{ "cat_weapons", "Weapons" },
		{ "cat_armor", "Armor" },
		{ "cat_tools", "Tools" },
		{ "cat_materials", "Materials" },
		{ "cat_food", "Food" },
		{ "cat_potions", "Potions" },
		{ "cat_other", "Other" },
		{ "hint_order_topic", "Buy order for this item" },
		{ "hint_order_better", "Someone pays at least your price" },
		{ "hint_order_text", "{0} is buying {1}× at {2} coins each." },
		{ "hint_click_order", "Click to open the order — you get paid instantly." },
		{ "hint_listing_topic", "This item is on the market" },
		{ "hint_listing_better", "Same or cheaper on the market" },
		{ "hint_listing_text", "{0} sells {1}× at {2} coins each." },
		{ "hint_click_listing", "Click to open the listing and buy it now." }
	};

	private static readonly Dictionary<string, string> Pl = new Dictionary<string, string>
	{
		{ "title", "TARGOWISKO" },
		{ "tab_buy", "TARGOWISKO" },
		{ "tab_sell", "SPRZEDAJ" },
		{ "tab_mine", "MOJE OFERTY" },
		{ "tab_mail", "SKRZYNKA" },
		{ "tab_bank", "BANK" },
		{ "hdr_inventory", "Ekwipunek" },
		{ "hdr_balance", "Saldo" },
		{ "connecting", "Łączenie z targowiskiem…" },
		{ "search", "Szukaj przedmiotu lub sprzedawcy…" },
		{ "sort_new", "Sortuj: najnowsze" },
		{ "sort_cheap", "Sortuj: najtańsze" },
		{ "sort_expensive", "Sortuj: najdroższe" },
		{ "sort_name", "Sortuj: nazwa" },
		{ "pick", "Wybierz pozycję z listy po lewej." },
		{ "loading", "Wczytywanie ofert…" },
		{ "no_match", "Nic nie pasuje do wyszukiwania." },
		{ "no_listings", "Na targowisku nie ma jeszcze żadnych ofert.\nWystaw coś w zakładce SPRZEDAJ!" },
		{ "nothing_to_sell", "Nie masz w ekwipunku nic, co można wystawić.\nZałożone przedmioty i monety są pomijane." },
		{ "no_own", "Nie masz aktywnych ofert." },
		{ "mail_empty_list", "Skrzynka jest pusta.\nTrafiają tu przedmioty z wycofanych i wygasłych ofert." },
		{ "no_history", "Brak transakcji." },
		{ "your_listing", "Twoja oferta" },
		{ "expires_in", "wygasa za {0}" },
		{ "trader_value", "Wartość u kupca: {0} / szt." },
		{ "seller", "Sprzedawca: {0}" },
		{ "price_each", "Cena za sztukę: <color=#FFD27A>{0}</color> monet" },
		{ "available", "Dostępne: {0} szt." },
		{ "remaining", "Pozostało: {0} szt." },
		{ "listed_ago", "Wystawiono {0} temu" },
		{ "expires_in_cap", "Wygasa za {0}" },
		{ "no_expiry", "Bez terminu ważności" },
		{ "in_inventory", "W ekwipunku: {0} szt." },
		{ "my_listings_count", "Twoje oferty: {0} / {1}" },
		{ "expires_after", "Oferta wygasa po {0} h" },
		{ "never_expires", "Oferty nie wygasają" },
		{ "qty", "Ilość:" },
		{ "price", "Cena / szt.:" },
		{ "max", "Max" },
		{ "missing_mod", "<color=#FF7766>Ten przedmiot pochodzi z moda, którego nie masz zainstalowanego.</color>" },
		{ "quality", "poz. {0}" },
		{ "mail_title", "Twoja skrzynka" },
		{ "mail_desc", "Czekają tu przedmioty z wycofanych i wygasłych ofert oraz zakupy, których nie dało się dostarczyć.\n\n„Odbierz wszystko” przenosi je do ekwipunku. Czego nie zmieścisz, wyląduje na ziemi obok Ciebie." },
		{ "mail_items", "Przedmioty: {0}" },
		{ "total_buy", "Razem: {0} monet" },
		{ "pay_split", "Z salda {0}, z ekwipunku {1}" },
		{ "total_sell", "Za całość dostaniesz: {0} monet" },
		{ "total_sell_fee", "Razem {0}, prowizja {1}% – dostaniesz {2}" },
		{ "btn_own", "To Twoja oferta" },
		{ "btn_poor", "Za mało monet" },
		{ "btn_buy", "Kup za {0}" },
		{ "btn_need_price", "Podaj cenę" },
		{ "btn_max_price", "Maks. {0} / szt." },
		{ "btn_sell", "Wystaw na sprzedaż" },
		{ "btn_cancel", "Wycofaj ofertę" },
		{ "btn_collect", "Odbierz wszystko" },
		{ "btn_wait", "Czekam na serwer…" },
		{ "bank_title", "Bank targowiska" },
		{ "bank_desc", "Monety na saldzie są przechowywane na serwerze, nie w ekwipunku — nie stracisz ich po śmierci. Tu trafia zarobek ze sprzedaży i stąd w pierwszej kolejności płacisz za zakupy." },
		{ "bank_balance", "Saldo: <color=#FFD27A>{0}</color> monet" },
		{ "bank_inventory", "W ekwipunku: {0} monet" },
		{ "bank_amount", "Monety:" },
		{ "btn_deposit", "Wpłać" },
		{ "btn_withdraw", "Wypłać" },
		{ "btn_deposit_all", "Wpłać wszystko" },
		{ "btn_withdraw_all", "Wypłać wszystko" },
		{ "h_deposit", "Wpłata" },
		{ "h_withdraw", "Wypłata" },
		{ "h_sale", "Sprzedaż: {0}" },
		{ "h_purchase", "Zakup: {0}" },
		{ "h_to", "dla {0}" },
		{ "h_from", "od {0}" },
		{ "h_fee", "prowizja {0}" },
		{ "ago", "{0} temu" },
		{ "span_d", "{0} d {1} h" },
		{ "span_h", "{0} h {1} min" },
		{ "span_m", "{0} min" },
		{ "inv_full_drop", "Ekwipunek pełny — część rzeczy leży na ziemi obok Ciebie" },
		{ "unknown_prefab", "Nie znam przedmiotu {0} — brak moda?" },
		{ "hud_prefix", "Targowisko: {0}" },
		{ "c_no_player", "Brak postaci." },
		{ "c_busy", "Poczekaj na odpowiedź serwera." },
		{ "c_item_gone", "Tego przedmiotu nie ma już w ekwipunku." },
		{ "c_unequip", "Najpierw zdejmij ten przedmiot." },
		{ "c_cannot_list", "Tego przedmiotu nie da się wystawić." },
		{ "c_bad_amount", "Nieprawidłowa ilość." },
		{ "c_need_price", "Podaj cenę (co najmniej 1 moneta)." },
		{ "c_max_price", "Maksymalna cena to {0} monet za sztukę." },
		{ "c_total_high", "Łączna cena jest zbyt wysoka." },
		{ "c_remove_failed", "Nie udało się zabrać przedmiotu z ekwipunku." },
		{ "c_own", "To Twoja oferta." },
		{ "c_poor", "Masz za mało monet (potrzeba {0})." },
		{ "c_no_coins_inv", "Nie masz tylu monet w ekwipunku." },
		{ "c_no_balance", "Nie masz tylu monet na saldzie." },
		{ "c_no_space", "Brak miejsca w ekwipunku na {0} monet (zmieści się {1})." },
		{ "s_unresolved_item", "Serwer nie rozpoznał Twojej postaci — przedmiot wrócił do ekwipunku." },
		{ "s_unresolved_coins", "Serwer nie rozpoznał Twojej postaci — monety wróciły." },
		{ "s_bad_amount", "Nieprawidłowa ilość." },
		{ "s_unknown_item", "Serwer nie zna tego przedmiotu (brak moda na serwerze?)." },
		{ "s_blocked", "Tego przedmiotu nie można wystawić na targowisku." },
		{ "s_bad_price", "Cena musi wynosić od 1 do {0} monet." },
		{ "s_total_high", "Łączna cena jest zbyt wysoka." },
		{ "s_limit", "Osiągnięto limit ofert ({0})." },
		{ "s_create_failed", "{0} Przedmiot wrócił do Ciebie." },
		{ "s_listed", "Wystawiono: {0} po {1} monet/szt." },
		{ "s_gone", "Ta oferta jest już nieaktualna." },
		{ "s_own", "Nie możesz kupić własnej oferty." },
		{ "s_amount_changed", "Dostępna ilość się zmieniła." },
		{ "s_price_changed", "Cena oferty się zmieniła." },
		{ "s_no_funds", "Za mało monet: potrzeba {0}, na saldzie masz {1}." },
		{ "s_buy_failed_deposit", "{0} Wpłacone monety zostały na Twoim saldzie targowiska." },
		{ "s_bought", "Kupiono: {0} za {1} monet." },
		{ "s_sold", "{0} kupił(a) od Ciebie {1}. +{2} monet na Twoim saldzie targowiska." },
		{ "s_not_found", "Nie znaleziono tej oferty." },
		{ "s_cancelled", "Wycofano ofertę: {0}." },
		{ "s_mail_empty", "Skrzynka jest pusta." },
		{ "s_claimed", "Odebrano zawartość skrzynki." },
		{ "s_expired", "Twoja oferta wygasła: {0}. Przedmiot czeka w skrzynce targowiska." },
		{ "s_deposited", "Wpłacono {0} monet. Saldo: {1}." },
		{ "s_withdrawn", "Wypłacono {0} monet. Saldo: {1}." },
		{ "s_withdraw_too_much", "Na saldzie masz tylko {0} monet." },
		{ "tab_orders", "SKUP" },
		{ "tab_jobs", "ZADANIA" },
		{ "mode_orders", "Wszystkie zlecenia kupna" },
		{ "mode_bounties", "Wszystkie zadania" },
		{ "mode_new_order", "+ Dodaj zlecenie kupna" },
		{ "mode_new_bounty", "+ Dodaj zadanie" },
		{ "pick_search", "Wyszukaj przedmiot, który chcesz kupić…" },
		{ "pick_hint", "Wpisz co najmniej 2 litery nazwy przedmiotu." },
		{ "pick_none", "Brak pasujących przedmiotów." },
		{ "pick_order_item", "Wybierz z listy po lewej przedmiot, który chcesz kupić." },
		{ "no_orders", "Nie ma jeszcze zleceń kupna.\nDodaj własne przyciskiem „+ Dodaj zlecenie kupna”!" },
		{ "no_bounties", "Nie ma jeszcze żadnych zadań.\nDodaj własne przyciskiem „+ Dodaj zadanie”!" },
		{ "order_row", "Kupię: {0}" },
		{ "order_title", "Kupię: {0}" },
		{ "order_by", "Kupujący: {0}" },
		{ "your_order", "Twoje zlecenie kupna" },
		{ "order_remaining", "Jeszcze potrzeba: {0} szt. (dostarczono {1})" },
		{ "order_escrow", "Zablokowane w zleceniu: {0} monet" },
		{ "you_have", "Masz: {0}" },
		{ "new_order_title", "Kupię: {0}" },
		{ "my_orders_count", "Twoje zlecenia kupna: {0} / {1}" },
		{ "my_bounties_count", "Twoje zadania: {0} / {1}" },
		{ "order_lock", "Zablokuje {0} monet do czasu dostawy" },
		{ "bounty_lock", "Zablokuje {0} monet do potwierdzenia wykonania" },
		{ "your_bounty", "Twoje zadanie" },
		{ "bounty_by", "Zleceniodawca: {0}" },
		{ "bounty_no_desc", "(brak opisu)" },
		{ "bounty_reward", "Nagroda: <color=#FFD27A>{0}</color> monet" },
		{ "b_open", "Otwarte — czeka na wykonawcę" },
		{ "b_claimed", "W realizacji: {0}" },
		{ "b_submitted", "{0} zgłasza wykonanie" },
		{ "bounty_timeout_in", "Anulujesz, jeśli wykonawca nie zgłosi wykonania w ciągu {0}" },
		{ "new_bounty_title", "Nowe zadanie" },
		{ "bounty_title_label", "Tytuł zadania" },
		{ "bounty_desc_label", "Opis (co dokładnie, gdzie, do kiedy)" },
		{ "bounty_title_ph", "np. Zbuduj most nad rzeką przy spawnie" },
		{ "bounty_reward_label", "Nagroda:" },
		{ "btn_cancel_order", "Anuluj zlecenie (zwrot {0})" },
		{ "btn_no_items", "Nie masz tego przedmiotu" },
		{ "btn_fill", "Sprzedaj {0} szt. za {1}" },
		{ "btn_admin_cancel", "Admin: anuluj" },
		{ "btn_admin_pay", "Admin: wypłać" },
		{ "btn_cancel_bounty", "Anuluj zadanie (zwrot {0})" },
		{ "btn_claim", "Przyjmij zlecenie" },
		{ "btn_submit", "Zgłoś wykonanie" },
		{ "btn_abandon", "Zrezygnuj" },
		{ "btn_confirm", "Potwierdź i zapłać" },
		{ "btn_reject", "Odrzuć" },
		{ "btn_cancel_inactive", "Anuluj (brak zgłoszenia)" },
		{ "btn_taken", "Zlecenie zajęte" },
		{ "btn_wait_confirm", "Czeka na potwierdzenie" },
		{ "btn_post_order", "Dodaj zlecenie kupna" },
		{ "btn_post_bounty", "Dodaj zadanie" },
		{ "btn_need_title", "Podaj tytuł" },
		{ "btn_need_reward", "Podaj nagrodę" },
		{ "h_order_escrow", "Zlecenie kupna: {0}" },
		{ "h_order_refund", "Zwrot ze zlecenia: {0}" },
		{ "h_order_sale", "Sprzedaż do zlecenia: {0}" },
		{ "h_order_filled", "Dostawa do Twojego zlecenia: {0}" },
		{ "h_bounty_escrow", "Dodane zadanie: {0}" },
		{ "h_bounty_refund", "Zwrot za zadanie: {0}" },
		{ "h_bounty_reward", "Nagroda za zadanie: {0}" },
		{ "h_bounty_paid", "Zadanie wykonane: {0}" },
		{ "h_guild_out", "Wpłata do gildii {0}" },
		{ "h_guild_in", "Wypłata z gildii {0}" },
		{ "h_by", "wykonawca: {0}" },
		{ "c_pick_item", "Najpierw wybierz przedmiot." },
		{ "c_order_own", "To Twoje zlecenie kupna." },
		{ "c_not_enough_items", "Nie masz tylu sztuk tego przedmiotu." },
		{ "c_bounty_title", "Podaj tytuł zadania." },
		{ "c_bounty_reward", "Podaj nagrodę (co najmniej 1 moneta)." },
		{ "s_order_limit", "Osiągnięto limit zleceń kupna ({0})." },
		{ "s_order_created", "Dodano zlecenie kupna: {0} po {1} monet/szt. Zablokowano {2} monet." },
		{ "s_order_gone", "To zlecenie kupna już nie istnieje." },
		{ "s_order_own", "Nie możesz sprzedać do własnego zlecenia." },
		{ "s_order_wrong_item", "Te przedmioty nie pasują do zlecenia." },
		{ "s_order_sold", "Sprzedano {0} do zlecenia. +{1} monet na saldzie." },
		{ "s_order_filled", "{0} dostarczył(a) {1} do Twojego zlecenia. Przedmioty czekają w SKRZYNCE." },
		{ "s_order_cancelled", "Anulowano zlecenie kupna: {0}. {1} monet wróciło na saldo." },
		{ "s_order_cancelled_admin", "Admin anulował Twoje zlecenie kupna: {0}. {1} monet wróciło na saldo." },
		{ "s_order_expired", "Twoje zlecenie kupna wygasło: {0}. {1} monet wróciło na saldo." },
		{ "s_bounty_no_title", "Zadanie musi mieć tytuł." },
		{ "s_bounty_too_long", "Tytuł lub opis jest za długi." },
		{ "s_bounty_bad_reward", "Nagroda musi wynosić od 1 do {0} monet." },
		{ "s_bounty_limit", "Osiągnięto limit zadań ({0})." },
		{ "s_bounty_created", "Dodano zadanie: „{0}” — {1} monet zablokowano do potwierdzenia wykonania." },
		{ "s_bounty_gone", "To zadanie już nie istnieje." },
		{ "s_bounty_not_allowed", "Ta akcja nie jest teraz dostępna." },
		{ "s_bounty_claimed", "Przyjąłeś zadanie: „{0}”. Po wykonaniu zgłoś to w zakładce ZADANIA." },
		{ "s_bounty_claimed_owner", "{0} przyjął(a) Twoje zadanie: „{1}”." },
		{ "s_bounty_abandoned", "Zrezygnowałeś z zadania: „{0}”." },
		{ "s_bounty_abandoned_owner", "{0} zrezygnował(a) z Twojego zadania „{1}”. Jest znowu otwarte." },
		{ "s_bounty_submitted", "Zgłosiłeś wykonanie „{0}”. Czekasz na potwierdzenie." },
		{ "s_bounty_submitted_owner", "{0} zgłasza wykonanie zadania „{1}” — sprawdź i potwierdź lub odrzuć w zakładce ZADANIA." },
		{ "s_bounty_confirmed", "Potwierdzono „{0}” — nagroda wypłacona dla {1}." },
		{ "s_bounty_paid", "{0} potwierdził(a) „{1}”. +{2} monet na Twoim saldzie." },
		{ "s_bounty_confirmed_admin", "Admin potwierdził Twoje zadanie „{0}” i wypłacił nagrodę." },
		{ "s_bounty_rejected", "Odrzuciłeś zgłoszenie „{0}”. Wykonawca może kontynuować pracę." },
		{ "s_bounty_rejected_contractor", "{0} odrzucił(a) Twoje zgłoszenie „{1}”. Dokończ zadanie i zgłoś je ponownie." },
		{ "s_bounty_cancelled", "Anulowano zadanie: „{0}”. {1} monet wróciło na saldo." },
		{ "s_bounty_cancelled_contractor", "Zadanie „{0}”, nad którym pracowałeś, zostało anulowane." },
		{ "s_bounty_cancelled_admin", "Admin anulował Twoje zadanie „{0}”. {1} monet wróciło na saldo." },
		{ "s_bounty_expired", "Nikt nie przyjął Twojego zadania „{0}”. {1} monet wróciło na saldo." },
		{ "price_stats", "Cena rynkowa: średnio <color=#FFD27A>{0}</color>, od {1} do {2} (transakcji: {3})" },
		{ "price_stats_one", "Cena rynkowa: <color=#FFD27A>{0}</color> (transakcji: {1})" },
		{ "price_none", "Cena rynkowa: brak transakcji" },
		{ "avg_short", "rynek śr. {0}" },
		{ "cat_all", "Wszystkie kategorie" },
		{ "cat_weapons", "Broń" },
		{ "cat_armor", "Pancerz" },
		{ "cat_tools", "Narzędzia" },
		{ "cat_materials", "Surowce" },
		{ "cat_food", "Jedzenie" },
		{ "cat_potions", "Mikstury" },
		{ "cat_other", "Inne" },
		{ "hint_order_topic", "Zlecenie kupna na ten przedmiot" },
		{ "hint_order_better", "Ktoś płaci co najmniej Twoją cenę" },
		{ "hint_order_text", "{0} kupuje {1}× po {2} monet/szt." },
		{ "hint_click_order", "Kliknij, aby otworzyć zlecenie — zapłatę dostajesz od razu." },
		{ "hint_listing_topic", "Ten przedmiot jest na targu" },
		{ "hint_listing_better", "Na targu jest tak samo lub taniej" },
		{ "hint_listing_text", "{0} sprzedaje {1}× po {2} monet/szt." },
		{ "hint_click_listing", "Kliknij, aby otworzyć ofertę i kupić od razu." }
	};

	public static string T(string key)
	{
		Dictionary<string, string> dictionary = ((Plugin.Language != null && Plugin.Language.Value == MarketLanguage.Polish) ? Pl : En);
		if (dictionary.TryGetValue(key, out var value))
		{
			return value;
		}
		if (En.TryGetValue(key, out value))
		{
			return value;
		}
		return key;
	}

	public static string T(string key, params object[] args)
	{
		string text = T(key);
		if (args == null || args.Length == 0)
		{
			return text;
		}
		object[] array = new object[args.Length];
		for (int i = 0; i < args.Length; i++)
		{
			string text2 = args[i] as string;
			array[i] = ((text2 != null && text2.Length > 1 && text2[0] == '@') ? Nested(text2.Substring(1)) : args[i]);
		}
		try
		{
			return string.Format(text, array);
		}
		catch
		{
			return text;
		}
	}

	private static string Nested(string spec)
	{
		string[] array = spec.Split('|');
		if (array.Length == 1)
		{
			return T(array[0]);
		}
		object[] array2 = new object[array.Length - 1];
		for (int i = 1; i < array.Length; i++)
		{
			array2[i - 1] = array[i];
		}
		return T(array[0], array2);
	}
}
internal class ListingView
{
	public Listing Data;

	public ItemData Item;

	public string DisplayName;
}
internal class HistoryView
{
	public HistoryEntry Data;

	public ItemData Item;

	public string ItemName;
}
internal static class MarketClient
{
	public static bool HasData;

	public static long MyId;

	public static int FeePercent;

	public static int MaxListings;

	public static int ExpireHours;

	public static int MaxPrice = 100000;

	public static long ServerNowTicks;

	public static float ReceivedAt;

	public static readonly List<ListingView> Listings = new List<ListingView>();

	public static long Balance;

	public static readonly List<ListingView> MailItems = new List<ListingView>();

	public static readonly List<HistoryView> History = new List<HistoryView>();

	public static float BusyUntil;

	public static readonly List<OrderView> Orders = new List<OrderView>();

	public static readonly List<Bounty> Bounties = new List<Bounty>();

	public static bool IsAdmin;

	public static int MaxOrders = 10;

	public static int MaxBounties = 10;

	public static int BountyTimeoutHours = 72;

	public static readonly Dictionary<string, PriceStat> Prices = new Dictionary<string, PriceStat>();

	public static bool Busy => Time.time < BusyUntil;

	public static void Reset()
	{
		HasData = false;
		Listings.Clear();
		MailItems.Clear();
		History.Clear();
		Orders.Clear();
		Bounties.Clear();
		Prices.Clear();
		Balance = 0L;
		BusyUntil = 0f;
	}

	private static void Send(string method, params object[] args)
	{
		long num = Rpc.ServerId();
		if (ZRoutedRpc.instance != null && num != 0)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(num, method, args);
		}
	}

	public static void RequestData()
	{
		Send("Marketplace_RequestData");
	}

	public static string DisplayName(ItemData item, string fallback)
	{
		if (item == null)
		{
			return fallback;
		}
		string text = Localization.instance.Localize(item.m_shared.m_name);
		if (item.m_shared.m_maxQuality > 1)
		{
			text = text + " · " + Loc.T("quality", item.m_quality);
		}
		return text;
	}

	private static ListingView MakeView(Listing l)
	{
		ListingView listingView = new ListingView();
		listingView.Data = l;
		listingView.Item = l.Item.ToItemData();
		listingView.DisplayName = DisplayName(listingView.Item, l.Item.Prefab);
		return listingView;
	}

	public static int CountCoins()
	{
		Player localPlayer = Player.m_localPlayer;
		if (!((Object)(object)localPlayer == (Object)null))
		{
			return ((Humanoid)localPlayer).GetInventory().CountItems("$item_coins", -1, false);
		}
		return 0;
	}

	public static long CoinCapacity()
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || (Object)(object)ObjectDB.instance == (Object)null)
		{
			return 0L;
		}
		GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Coins");
		if ((Object)(object)itemPrefab == (Object)null)
		{
			return 0L;
		}
		int num = Math.Max(1, itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_maxStackSize);
		Inventory inventory = ((Humanoid)localPlayer).GetInventory();
		long num2 = (long)inventory.GetEmptySlots() * (long)num;
		foreach (ItemData allItem in inventory.GetAllItems())
		{
			if (allItem.m_shared.m_name == "$item_coins" && allItem.m_stack < num)
			{
				num2 += num - allItem.m_stack;
			}
		}
		return num2;
	}

	public static string CreateListing(ItemData item, int amount, int price)
	{
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Expected O, but got Unknown
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return Loc.T("c_no_player");
		}
		if (Busy)
		{
			return Loc.T("c_busy");
		}
		Inventory inventory = ((Humanoid)localPlayer).GetInventory();
		if (item == null || !inventory.ContainsItem(item))
		{
			return Loc.T("c_item_gone");
		}
		if (((Humanoid)localPlayer).IsItemEquiped(item))
		{
			return Loc.T("c_unequip");
		}
		if ((Object)(object)item.m_dropPrefab == (Object)null)
		{
			return Loc.T("c_cannot_list");
		}
		if (amount < 1 || amount > item.m_stack)
		{
			return Loc.T("c_bad_amount");
		}
		if (price < 1)
		{
			return Loc.T("c_need_price");
		}
		if (price > MaxPrice)
		{
			return Loc.T("c_max_price", MaxPrice);
		}
		if ((long)price * (long)amount > int.MaxValue)
		{
			return Loc.T("c_total_high");
		}
		ItemRecord itemRecord = ItemRecord.FromItem(item, amount);
		if (!((amount >= item.m_stack) ? inventory.RemoveItem(item) : inventory.RemoveItem(item, amount)))
		{
			return Loc.T("c_remove_failed");
		}
		ZPackage val = new ZPackage();
		itemRecord.Write(val);
		val.Write(price);
		BusyUntil = Time.time + 5f;
		Send("Marketplace_Create", val);
		return null;
	}

	public static string Buy(ListingView v, int amount)
	{
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Expected O, but got Unknown
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return Loc.T("c_no_player");
		}
		if (Busy)
		{
			return Loc.T("c_busy");
		}
		if (v.Data.SellerId == MyId)
		{
			return Loc.T("c_own");
		}
		if (amount < 1 || amount > v.Data.Item.Stack)
		{
			return Loc.T("c_bad_amount");
		}
		long num = (long)v.Data.Price * (long)amount;
		if (num > int.MaxValue)
		{
			return Loc.T("c_total_high");
		}
		int num2 = CountCoins();
		if (Balance + num2 < num)
		{
			return Loc.T("c_poor", num);
		}
		int num3 = (int)Math.Max(0L, num - Balance);
		if (num3 > 0)
		{
			((Humanoid)localPlayer).GetInventory().RemoveItem("$item_coins", num3, -1, false);
		}
		ZPackage val = new ZPackage();
		val.Write(v.Data.Id);
		val.Write(amount);
		val.Write(v.Data.Price);
		val.Write(num3);
		BusyUntil = Time.time + 5f;
		Send("Marketplace_Buy", val);
		return null;
	}

	public static string Cancel(ListingView v)
	{
		if (Busy)
		{
			return Loc.T("c_busy");
		}
		BusyUntil = Time.time + 5f;
		Send("Marketplace_Cancel", v.Data.Id);
		return null;
	}

	public static string Claim()
	{
		if (Busy)
		{
			return Loc.T("c_busy");
		}
		BusyUntil = Time.time + 5f;
		Send("Marketplace_Claim");
		return null;
	}

	public static string Deposit(int amount)
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return Loc.T("c_no_player");
		}
		if (Busy)
		{
			return Loc.T("c_busy");
		}
		if (amount < 1)
		{
			return Loc.T("c_bad_amount");
		}
		if (CountCoins() < amount)
		{
			return Loc.T("c_no_coins_inv");
		}
		((Humanoid)localPlayer).GetInventory().RemoveItem("$item_coins", amount, -1, false);
		BusyUntil = Time.time + 5f;
		Send("Marketplace_Deposit", amount);
		return null;
	}

	public static string Withdraw(int amount)
	{
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			return Loc.T("c_no_player");
		}
		if (Busy)
		{
			return Loc.T("c_busy");
		}
		if (amount < 1)
		{
			return Loc.T("c_bad_amount");
		}
		if (Balance < amount)
		{
			return Loc.T("c_no_balance");
		}
		long num = CoinCapacity();
		if (num < amount)
		{
			return Loc.T("c_no_space", amount, num);
		}
		BusyUntil = Time.time + 5f;
		Send("Marketplace_Withdraw", amount);
		return null;
	}

	public static void RPC_Data(long sender, ZPackage pkg)
	{
		try
		{
			MyId = pkg.ReadLong();
			FeePercent = pkg.ReadInt();
			MaxListings = pkg.ReadInt();
			ExpireHours = pkg.ReadInt();
			MaxPrice = pkg.ReadInt();
			ServerNowTicks = pkg.ReadLong();
			ReceivedAt = Time.realtimeSinceStartup;
			Listings.Clear();
			int num = pkg.ReadInt();
			for (int i = 0; i < num; i++)
			{
				Listings.Add(MakeView(Listing.Read(pkg)));
			}
			Balance = pkg.ReadLong();
			MailItems.Clear();
			int num2 = pkg.ReadInt();
			for (int j = 0; j < num2; j++)
			{
				Listing listing = new Listing();
				listing.Id = -1 - j;
				listing.Item = ItemRecord.Read(pkg);
				MailItems.Add(MakeView(listing));
			}
			History.Clear();
			int num3 = pkg.ReadInt();
			for (int k = 0; k < num3; k++)
			{
				HistoryView historyView = new HistoryView();
				historyView.Data = HistoryEntry.Read(pkg, hasText: true);
				if (!string.IsNullOrEmpty(historyView.Data.Prefab))
				{
					ItemRecord itemRecord = new ItemRecord();
					itemRecord.Prefab = historyView.Data.Prefab;
					itemRecord.Stack = historyView.Data.Stack;
					historyView.Item = itemRecord.ToItemData();
					historyView.ItemName = ((historyView.Item != null) ? Localization.instance.Localize(historyView.Item.m_shared.m_name) : historyView.Data.Prefab) + " x" + historyView.Data.Stack;
				}
				History.Add(historyView);
			}
			ReadOrders(pkg);
			ReadPrices(pkg);
			HasData = true;
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("Error reading marketplace data: " + ex));
		}
		BusyUntil = 0f;
		MarketUI.OnDataChanged();
	}

	public static void RPC_Result(long sender, ZPackage pkg)
	{
		BusyUntil = 0f;
		bool ok = pkg.ReadBool();
		string key = pkg.ReadString();
		int num = pkg.ReadInt();
		object[] array = new object[num];
		for (int i = 0; i < num; i++)
		{
			array[i] = pkg.ReadString();
		}
		string text = Localization.instance.Localize(Loc.T(key, array));
		MarketUI.SetStatus(text, ok);
		if ((Object)(object)MessageHud.instance != (Object)null && !MarketUI.IsOpen)
		{
			MessageHud.instance.ShowMessage((MessageType)1, Loc.T("hud_prefix", text), 0, (Sprite)null, false, true);
		}
	}

	public static void RPC_Changed(long sender)
	{
		if (MarketUI.IsOpen)
		{
			RequestData();
		}
	}

	public static void RPC_Deliver(long sender, ZPackage pkg)
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			Plugin.Log.LogError((object)"Marketplace delivery without a character — items lost!");
			return;
		}
		bool flag = false;
		long num = pkg.ReadLong();
		if (num > 0)
		{
			flag |= GiveCoins(localPlayer, num);
		}
		int num2 = pkg.ReadInt();
		for (int i = 0; i < num2; i++)
		{
			ItemRecord itemRecord = ItemRecord.Read(pkg);
			ItemData val = itemRecord.ToItemData();
			if (val == null)
			{
				Plugin.Log.LogError((object)("Unknown marketplace prefab: " + itemRecord.Prefab + " x" + itemRecord.Stack));
				MarketUI.SetStatus(Loc.T("unknown_prefab", itemRecord.Prefab), ok: false);
			}
			else
			{
				flag |= Give(localPlayer, val);
			}
		}
		if (flag)
		{
			MessageHud.instance.ShowMessage((MessageType)2, Loc.T("inv_full_drop"), 0, (Sprite)null, false, true);
		}
		MarketUI.OnInventoryChanged();
	}

	private static bool GiveCoins(Player p, long amount)
	{
		GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Coins");
		if ((Object)(object)itemPrefab == (Object)null)
		{
			return false;
		}
		ItemData itemData = itemPrefab.GetComponent<ItemDrop>().m_itemData;
		int num = Math.Max(1, itemData.m_shared.m_maxStackSize);
		bool flag = false;
		while (amount > 0)
		{
			int num2 = (int)Math.Min(amount, num);
			ItemData val = itemData.Clone();
			val.m_dropPrefab = itemPrefab;
			val.m_stack = num2;
			flag |= Give(p, val);
			amount -= num2;
		}
		return flag;
	}

	private static bool Give(Player p, ItemData item)
	{
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		int num = Math.Max(1, item.m_shared.m_maxStackSize);
		bool result = false;
		int num2 = item.m_stack;
		while (num2 > 0)
		{
			ItemData val = ((num2 > num) ? item.Clone() : item);
			val.m_stack = Math.Min(num2, num);
			num2 -= val.m_stack;
			Inventory inventory = ((Humanoid)p).GetInventory();
			if (!inventory.CanAddItem(val, -1) || !inventory.AddItem(val))
			{
				Vector3 val2 = ((Component)p).transform.position + ((Component)p).transform.forward * 0.7f + Vector3.up;
				ItemDrop.DropItem(val, val.m_stack, val2, Quaternion.identity);
				result = true;
			}
		}
		return result;
	}

	private static void ReadOrders(ZPackage pkg)
	{
		Orders.Clear();
		Bounties.Clear();
		pkg.ReadLong();
		int num = pkg.ReadInt();
		for (int i = 0; i < num; i++)
		{
			OrderView orderView = new OrderView();
			orderView.Data = BuyOrder.Read(pkg);
			ItemRecord itemRecord = new ItemRecord();
			itemRecord.Prefab = orderView.Data.Prefab;
			itemRecord.Stack = 1;
			orderView.Item = itemRecord.ToItemData();
			orderView.DisplayName = ((orderView.Item != null) ? Localization.instance.Localize(orderView.Item.m_shared.m_name) : orderView.Data.Prefab);
			Orders.Add(orderView);
		}
		pkg.ReadLong();
		int num2 = pkg.ReadInt();
		for (int j = 0; j < num2; j++)
		{
			Bounties.Add(Bounty.Read(pkg));
		}
		IsAdmin = pkg.ReadBool();
		MaxOrders = pkg.ReadInt();
		MaxBounties = pkg.ReadInt();
		BountyTimeoutHours = pkg.ReadInt();
	}

	public static List<ItemData> MatchingItems(string prefab)
	{
		List<ItemData> list = new List<ItemData>();
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return list;
		}
		foreach (ItemData allItem in ((Humanoid)localPlayer).GetInventory().GetAllItems())
		{
			if (!((Object)(object)allItem.m_dropPrefab == (Object)null) && !(((Object)allItem.m_dropPrefab).name != prefab) && !allItem.m_equipped && !((Humanoid)localPlayer).IsItemEquiped(allItem))
			{
				list.Add(allItem);
			}
		}
		return list;
	}

	public static int CountMatching(string prefab)
	{
		int num = 0;
		foreach (ItemData item in MatchingItems(prefab))
		{
			num += item.m_stack;
		}
		return num;
	}

	private static int TakeShortfall(long needed)
	{
		int num = (int)Math.Max(0L, needed - Balance);
		if (num > 0)
		{
			((Humanoid)Player.m_localPlayer).GetInventory().RemoveItem("$item_coins", num, -1, false);
		}
		return num;
	}

	public static string CreateOrder(string prefab, int amount, int price)
	{
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Expected O, but got Unknown
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			return Loc.T("c_no_player");
		}
		if (Busy)
		{
			return Loc.T("c_busy");
		}
		if (string.IsNullOrEmpty(prefab))
		{
			return Loc.T("c_pick_item");
		}
		if (amount < 1)
		{
			return Loc.T("c_bad_amount");
		}
		if (price < 1)
		{
			return Loc.T("c_need_price");
		}
		if (price > MaxPrice)
		{
			return Loc.T("c_max_price", MaxPrice);
		}
		long num = (long)amount * (long)price;
		if (num > int.MaxValue)
		{
			return Loc.T("c_total_high");
		}
		if (Balance + CountCoins() < num)
		{
			return Loc.T("c_poor", num);
		}
		int num2 = TakeShortfall(num);
		ZPackage val = new ZPackage();
		val.Write(prefab);
		val.Write(amount);
		val.Write(price);
		val.Write(num2);
		BusyUntil = Time.time + 5f;
		Send("Marketplace_OrderCreate", val);
		return null;
	}

	public static string FillOrder(OrderView v, int amount)
	{
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_011d: Expected O, but got Unknown
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return Loc.T("c_no_player");
		}
		if (Busy)
		{
			return Loc.T("c_busy");
		}
		if (v.Data.BuyerId == MyId)
		{
			return Loc.T("c_order_own");
		}
		if (amount < 1 || amount > v.Data.Remaining)
		{
			return Loc.T("c_bad_amount");
		}
		if (CountMatching(v.Data.Prefab) < amount)
		{
			return Loc.T("c_not_enough_items");
		}
		Inventory inventory = ((Humanoid)localPlayer).GetInventory();
		List<ItemRecord> list = new List<ItemRecord>();
		int num = amount;
		foreach (ItemData item in MatchingItems(v.Data.Prefab))
		{
			if (num > 0)
			{
				int num2 = Math.Min(num, item.m_stack);
				list.Add(ItemRecord.FromItem(item, num2));
				if (num2 >= item.m_stack)
				{
					inventory.RemoveItem(item);
				}
				else
				{
					inventory.RemoveItem(item, num2);
				}
				num -= num2;
				continue;
			}
			break;
		}
		ZPackage val = new ZPackage();
		val.Write(v.Data.Id);
		val.Write(v.Data.Price);
		val.Write(list.Count);
		foreach (ItemRecord item2 in list)
		{
			item2.Write(val);
		}
		BusyUntil = Time.time + 5f;
		Send("Marketplace_OrderFill", val);
		return null;
	}

	public static string CancelOrder(OrderView v)
	{
		if (Busy)
		{
			return Loc.T("c_busy");
		}
		BusyUntil = Time.time + 5f;
		Send("Marketplace_OrderCancel", v.Data.Id);
		return null;
	}

	public static string CreateBounty(string title, string desc, int reward)
	{
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Expected O, but got Unknown
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			return Loc.T("c_no_player");
		}
		if (Busy)
		{
			return Loc.T("c_busy");
		}
		title = (title ?? "").Trim();
		desc = (desc ?? "").Trim();
		if (title.Length == 0)
		{
			return Loc.T("c_bounty_title");
		}
		if (reward < 1)
		{
			return Loc.T("c_bounty_reward");
		}
		if (Balance + CountCoins() < reward)
		{
			return Loc.T("c_poor", reward);
		}
		int num = TakeShortfall(reward);
		ZPackage val = new ZPackage();
		val.Write(title);
		val.Write(desc);
		val.Write(reward);
		val.Write(num);
		BusyUntil = Time.time + 5f;
		Send("Marketplace_BountyCreate", val);
		return null;
	}

	public static string BountyAct(Bounty b, BountyAction action)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Expected O, but got Unknown
		if (Busy)
		{
			return Loc.T("c_busy");
		}
		ZPackage val = new ZPackage();
		val.Write(b.Id);
		val.Write((int)action);
		BusyUntil = Time.time + 5f;
		Send("Marketplace_BountyAction", val);
		return null;
	}

	private static void ReadPrices(ZPackage pkg)
	{
		Prices.Clear();
		int num = pkg.ReadInt();
		for (int i = 0; i < num; i++)
		{
			PriceStat priceStat = PriceStat.Read(pkg);
			Prices[priceStat.Prefab] = priceStat;
		}
	}

	public static PriceStat GetStat(string prefab)
	{
		if (prefab == null || !Prices.TryGetValue(prefab, out var value))
		{
			return null;
		}
		return value;
	}

	public static string PriceLine(string prefab)
	{
		PriceStat stat = GetStat(prefab);
		if (stat == null || stat.Trades == 0)
		{
			return Loc.T("price_none");
		}
		if (stat.Min == stat.Max)
		{
			return Loc.T("price_stats_one", stat.Avg, stat.Trades);
		}
		return Loc.T("price_stats", stat.Avg, stat.Min, stat.Max, stat.Trades);
	}

	public static OrderView BestOrderFor(string prefab)
	{
		OrderView orderView = null;
		foreach (OrderView order in Orders)
		{
			if (!(order.Data.Prefab != prefab) && order.Data.BuyerId != MyId && order.Data.Remaining > 0 && (orderView == null || order.Data.Price > orderView.Data.Price))
			{
				orderView = order;
			}
		}
		return orderView;
	}

	public static ListingView CheapestListingFor(string prefab)
	{
		ListingView listingView = null;
		foreach (ListingView listing in Listings)
		{
			if (!(listing.Data.Item.Prefab != prefab) && listing.Data.SellerId != MyId && (listingView == null || listing.Data.Price < listingView.Data.Price))
			{
				listingView = listing;
			}
		}
		return listingView;
	}
}
internal class OrderView
{
	public BuyOrder Data;

	public ItemData Item;

	public string DisplayName;
}
public static class MarketplaceApi
{
	public static bool IsReady()
	{
		return MarketServer.ApiReady;
	}

	public static long GetBalance(long playerId)
	{
		return MarketServer.ApiBalance(playerId);
	}

	public static bool TryDebit(long playerId, string playerName, long amount, string note)
	{
		return MarketServer.ApiChange(playerId, playerName, -amount, note);
	}

	public static void Credit(long playerId, string playerName, long amount, string note)
	{
		MarketServer.ApiChange(playerId, playerName, amount, note);
	}
}
internal static class MarketServer
{
	private class Trade
	{
		public long Ticks;

		public int Price;

		public int Qty;
	}

	private const int SaveVersion = 4;

	private const int MaxDeposit = 10000000;

	private const int MaxOrderAmount = 100000;

	private const int MaxBountyReward = 10000000;

	private const int TradesKept = 30;

	private const int TradesInStat = 20;

	private static bool _loaded;

	private static string _path;

	private static string _logPath;

	private static long _nextId = 1L;

	private static readonly List<Listing> Listings = new List<Listing>();

	private static readonly Dictionary<long, Account> Accounts = new Dictionary<long, Account>();

	private static long _nextOrderId = 1L;

	private static long _nextBountyId = 1L;

	private static readonly List<BuyOrder> Orders = new List<BuyOrder>();

	private static readonly List<Bounty> Bounties = new List<Bounty>();

	private static readonly Dictionary<string, List<Trade>> Trades = new Dictionary<string, List<Trade>>();

	internal static bool ApiReady
	{
		get
		{
			if (_loaded)
			{
				return IsServer;
			}
			return false;
		}
	}

	private static bool IsServer
	{
		get
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				return ZNet.instance.IsServer();
			}
			return false;
		}
	}

	internal static long ApiBalance(long pid)
	{
		if (!ApiReady)
		{
			return 0L;
		}
		if (!Accounts.TryGetValue(pid, out var value))
		{
			return 0L;
		}
		return value.Balance;
	}

	internal static bool ApiChange(long pid, string name, long delta, string note)
	{
		if (!ApiReady || pid == 0 || delta == 0)
		{
			return false;
		}
		Account account = Acc(pid, name);
		if (delta < 0 && account.Balance < -delta)
		{
			return false;
		}
		account.Balance += delta;
		HistoryEntry historyEntry = new HistoryEntry();
		historyEntry.Kind = ((delta < 0) ? HistoryKind.GuildPayment : HistoryKind.GuildPayout);
		historyEntry.Amount = delta;
		historyEntry.Text = note ?? "";
		account.AddHistory(historyEntry);
		Save();
		Audit(name, pid, ((delta < 0) ? "API DEBIT " : "API CREDIT ") + Math.Abs(delta) + " (" + note + ") | balance " + account.Balance);
		long num = FindPeerOf(pid);
		if (num != 0)
		{
			SendData(num, pid);
		}
		return true;
	}

	public static void EnsureLoaded()
	{
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f2: Expected O, but got Unknown
		if (_loaded || !IsServer)
		{
			return;
		}
		_loaded = true;
		Listings.Clear();
		Accounts.Clear();
		ClearOrders();
		ClearPrices();
		_nextId = 1L;
		string text = ZNet.instance.GetWorldName();
		if (string.IsNullOrEmpty(text))
		{
			text = "world";
		}
		char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
		foreach (char oldChar in invalidFileNameChars)
		{
			text = text.Replace(oldChar, '_');
		}
		string text2 = Path.Combine(Paths.ConfigPath, "Marketplace");
		Directory.CreateDirectory(text2);
		_path = Path.Combine(text2, text + ".dat");
		_logPath = Path.Combine(text2, text + ".log");
		if (!File.Exists(_path))
		{
			Plugin.Log.LogInfo((object)("New marketplace for world " + text));
			return;
		}
		try
		{
			ZPackage val = new ZPackage(File.ReadAllBytes(_path));
			int num = val.ReadInt();
			_nextId = val.ReadLong();
			int num2 = val.ReadInt();
			for (int j = 0; j < num2; j++)
			{
				Listings.Add(Listing.Read(val));
			}
			int num3 = val.ReadInt();
			for (int k = 0; k < num3; k++)
			{
				long key = val.ReadLong();
				Account account = new Account();
				account.Name = val.ReadString();
				account.Balance = val.ReadLong();
				int num4 = val.ReadInt();
				for (int l = 0; l < num4; l++)
				{
					account.Items.Add(ItemRecord.Read(val));
				}
				if (num >= 2)
				{
					int num5 = val.ReadInt();
					for (int m = 0; m < num5; m++)
					{
						account.History.Add(HistoryEntry.Read(val, num >= 3));
					}
				}
				Accounts[key] = account;
			}
			if (num >= 3)
			{
				ReadOrders(val);
			}
			if (num >= 4)
			{
				ReadPrices(val);
			}
			Plugin.Log.LogInfo((object)("Marketplace loaded: " + Listings.Count + " listings, " + Accounts.Count + " accounts."));
		}
		catch (Exception ex)
		{
			string text3 = _path + ".broken-" + DateTime.Now.ToString("yyyyMMdd-HHmmss");
			Plugin.Log.LogError((object)("Could not load " + _path + " — copy saved as " + text3 + ". " + ex));
			try
			{
				File.Copy(_path, text3, overwrite: true);
			}
			catch
			{
			}
		}
	}

	private static void Save()
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Expected O, but got Unknown
		if (!_loaded || string.IsNullOrEmpty(_path))
		{
			return;
		}
		try
		{
			ZPackage val = new ZPackage();
			val.Write(4);
			val.Write(_nextId);
			val.Write(Listings.Count);
			foreach (Listing listing in Listings)
			{
				listing.Write(val);
			}
			int num = 0;
			foreach (KeyValuePair<long, Account> account in Accounts)
			{
				if (!account.Value.IsEmpty)
				{
					num++;
				}
			}
			val.Write(num);
			foreach (KeyValuePair<long, Account> account2 in Accounts)
			{
				Account value = account2.Value;
				if (value.IsEmpty)
				{
					continue;
				}
				val.Write(account2.Key);
				val.Write(value.Name ?? "");
				val.Write(value.Balance);
				val.Write(value.Items.Count);
				foreach (ItemRecord item in value.Items)
				{
					item.Write(val);
				}
				val.Write(value.History.Count);
				foreach (HistoryEntry item2 in value.History)
				{
					item2.Write(val);
				}
			}
			WriteOrders(val);
			WritePrices(val);
			string text = _path + ".tmp";
			File.WriteAllBytes(text, val.GetArray());
			if (File.Exists(_path))
			{
				File.Copy(_path, _path + ".bak", overwrite: true);
			}
			File.Copy(text, _path, overwrite: true);
			File.Delete(text);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("Marketplace save error: " + ex));
		}
	}

	private static void Audit(string who, long pid, string what)
	{
		if (string.IsNullOrEmpty(_logPath))
		{
			return;
		}
		try
		{
			File.AppendAllText(_logPath, DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + "Z | " + who + " (" + pid + ") | " + what + Environment.NewLine);
		}
		catch
		{
		}
	}

	public static void Unload()
	{
		if (_loaded)
		{
			Save();
		}
		_loaded = false;
		Listings.Clear();
		Accounts.Clear();
		ClearOrders();
		ClearPrices();
	}

	private static Account Acc(long pid, string name)
	{
		if (!Accounts.TryGetValue(pid, out var value))
		{
			value = new Account();
			Accounts[pid] = value;
		}
		if (!string.IsNullOrEmpty(name))
		{
			value.Name = name;
		}
		return value;
	}

	private static bool Resolve(long sender, out long pid, out string name)
	{
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		pid = 0L;
		name = "";
		if (sender == ZDOMan.GetSessionID())
		{
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return false;
			}
			pid = Player.m_localPlayer.GetPlayerID();
			name = Player.m_localPlayer.GetPlayerName();
			return pid != 0;
		}
		ZNetPeer peer = ZNet.instance.GetPeer(sender);
		if (peer == null)
		{
			return false;
		}
		ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID);
		if (zDO != null)
		{
			pid = zDO.GetLong(ZDOVars.s_playerID, 0L);
		}
		if (pid == 0)
		{
			pid = peer.m_playerID;
		}
		name = peer.m_playerName ?? "";
		return pid != 0;
	}

	private static long FindPeerOf(long pid)
	{
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)Player.m_localPlayer != (Object)null && Player.m_localPlayer.GetPlayerID() == pid)
		{
			return ZDOMan.GetSessionID();
		}
		foreach (ZNetPeer peer in ZNet.instance.GetPeers())
		{
			long num = 0L;
			ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID);
			if (zDO != null)
			{
				num = zDO.GetLong(ZDOVars.s_playerID, 0L);
			}
			if (num == 0)
			{
				num = peer.m_playerID;
			}
			if (num == pid)
			{
				return peer.m_uid;
			}
		}
		return 0L;
	}

	private static void Result(long target, bool ok, string key, params object[] args)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Expected O, but got Unknown
		ZPackage val = new ZPackage();
		val.Write(ok);
		val.Write(key);
		val.Write(args.Length);
		foreach (object obj in args)
		{
			val.Write((obj == null) ? "" : obj.ToString());
		}
		ZRoutedRpc.instance.InvokeRoutedRPC(target, "Marketplace_Result", new object[1] { val });
	}

	private static void BroadcastChanged()
	{
		ZRoutedRpc.instance.InvokeRoutedRPC(0L, "Marketplace_Changed", new object[0]);
	}

	private static void SendBack(long target, long coins, ItemRecord item)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Expected O, but got Unknown
		ZPackage val = new ZPackage();
		val.Write(coins);
		val.Write((item != null) ? 1 : 0);
		item?.Write(val);
		ZRoutedRpc.instance.InvokeRoutedRPC(target, "Marketplace_Deliver", new object[1] { val });
	}

	private static void DeliverItems(long pid, long target)
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Expected O, but got Unknown
		if (!Accounts.TryGetValue(pid, out var value) || value.Items.Count == 0)
		{
			return;
		}
		ZPackage val = new ZPackage();
		val.Write(0L);
		val.Write(value.Items.Count);
		foreach (ItemRecord item in value.Items)
		{
			item.Write(val);
		}
		value.Items.Clear();
		Save();
		ZRoutedRpc.instance.InvokeRoutedRPC(target, "Marketplace_Deliver", new object[1] { val });
	}

	private static bool IsBlocked(string prefab)
	{
		if (prefab == "Coins")
		{
			return true;
		}
		string value = Plugin.BlockedItems.Value;
		if (string.IsNullOrEmpty(value))
		{
			return false;
		}
		string[] array = value.Split(',');
		foreach (string text in array)
		{
			if (string.Equals(text.Trim(), prefab, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
		}
		return false;
	}

	private static int CountListings(long pid)
	{
		int num = 0;
		foreach (Listing listing in Listings)
		{
			if (listing.SellerId == pid)
			{
				num++;
			}
		}
		return num;
	}

	private static Listing Find(long id)
	{
		foreach (Listing listing in Listings)
		{
			if (listing.Id == id)
			{
				return listing;
			}
		}
		return null;
	}

	private static string Label(ItemRecord r)
	{
		string text = r.Prefab;
		if ((Object)(object)ObjectDB.instance != (Object)null)
		{
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(r.Prefab);
			if ((Object)(object)itemPrefab != (Object)null && (Object)(object)itemPrefab.GetComponent<ItemDrop>() != (Object)null)
			{
				text = itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_name;
			}
		}
		return text + " x" + r.Stack;
	}

	public static void RPC_RequestData(long sender)
	{
		if (IsServer)
		{
			EnsureLoaded();
			if (Resolve(sender, out var pid, out var _))
			{
				SendData(sender, pid);
			}
		}
	}

	private static void SendData(long target, long pid)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Expected O, but got Unknown
		ZPackage val = new ZPackage();
		val.Write(pid);
		val.Write(Plugin.FeePercent.Value);
		val.Write(Plugin.MaxListingsPerPlayer.Value);
		val.Write(Plugin.ExpireHours.Value);
		val.Write(Plugin.MaxPricePerUnit.Value);
		val.Write(DateTime.UtcNow.Ticks);
		val.Write(Listings.Count);
		foreach (Listing listing in Listings)
		{
			listing.Write(val);
		}
		Accounts.TryGetValue(pid, out var value);
		val.Write(value?.Balance ?? 0);
		val.Write(value?.Items.Count ?? 0);
		if (value != null)
		{
			foreach (ItemRecord item in value.Items)
			{
				item.Write(val);
			}
		}
		val.Write(value?.History.Count ?? 0);
		if (value != null)
		{
			foreach (HistoryEntry item2 in value.History)
			{
				item2.Write(val);
			}
		}
		WriteOrders(val);
		val.Write(IsAdminSender(target));
		val.Write(Plugin.MaxOrdersPerPlayer.Value);
		val.Write(Plugin.MaxBountiesPerPlayer.Value);
		val.Write(Plugin.BountyClaimTimeoutHours.Value);
		WriteStats(val);
		ZRoutedRpc.instance.InvokeRoutedRPC(target, "Marketplace_Data", new object[1] { val });
	}

	public static void RPC_Create(long sender, ZPackage pkg)
	{
		if (!IsServer)
		{
			return;
		}
		EnsureLoaded();
		ItemRecord itemRecord;
		int num;
		try
		{
			itemRecord = ItemRecord.Read(pkg);
			num = pkg.ReadInt();
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Corrupted listing packet from " + sender + ": " + ex.Message));
			return;
		}
		if (!Resolve(sender, out var pid, out var name))
		{
			SendBack(sender, 0L, itemRecord);
			Result(sender, false, "s_unresolved_item");
			return;
		}
		string text = null;
		if (itemRecord.Stack < 1)
		{
			text = "@s_bad_amount";
		}
		else if (!ItemRecord.PrefabExists(itemRecord.Prefab))
		{
			text = "@s_unknown_item";
		}
		else if (IsBlocked(itemRecord.Prefab))
		{
			text = "@s_blocked";
		}
		else if (num < 1 || num > Plugin.MaxPricePerUnit.Value)
		{
			text = "@s_bad_price|" + Plugin.MaxPricePerUnit.Value;
		}
		else if ((long)num * (long)itemRecord.Stack > int.MaxValue)
		{
			text = "@s_total_high";
		}
		else if (CountListings(pid) >= Plugin.MaxListingsPerPlayer.Value)
		{
			text = "@s_limit|" + Plugin.MaxListingsPerPlayer.Value;
		}
		if (text != null)
		{
			Acc(pid, name).Items.Add(itemRecord);
			DeliverItems(pid, sender);
			Result(sender, false, "s_create_failed", text);
			SendData(sender, pid);
			return;
		}
		Listing listing = new Listing();
		listing.Id = _nextId++;
		listing.SellerId = pid;
		listing.SellerName = name;
		listing.Price = num;
		listing.CreatedTicks = DateTime.UtcNow.Ticks;
		listing.Item = itemRecord;
		Listings.Add(listing);
		Save();
		Audit(name, pid, "LIST #" + listing.Id + " " + itemRecord.Prefab + " x" + itemRecord.Stack + " @ " + num);
		Result(sender, true, "s_listed", Label(itemRecord), num);
		SendData(sender, pid);
		BroadcastChanged();
	}

	public static void RPC_Buy(long sender, ZPackage pkg)
	{
		if (!IsServer)
		{
			return;
		}
		EnsureLoaded();
		long id;
		int num;
		int num2;
		int num3;
		try
		{
			id = pkg.ReadLong();
			num = pkg.ReadInt();
			num2 = pkg.ReadInt();
			num3 = pkg.ReadInt();
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Corrupted buy packet from " + sender + ": " + ex.Message));
			return;
		}
		if (num3 < 0)
		{
			num3 = 0;
		}
		if (!Resolve(sender, out var pid, out var name))
		{
			if (num3 > 0)
			{
				SendBack(sender, num3, null);
			}
			Result(sender, false, "s_unresolved_coins");
			return;
		}
		Account account = Acc(pid, name);
		if (num3 > 0)
		{
			account.Balance += num3;
			HistoryEntry historyEntry = new HistoryEntry();
			historyEntry.Kind = HistoryKind.Deposit;
			historyEntry.Amount = num3;
			account.AddHistory(historyEntry);
			Audit(name, pid, "DEPOSIT " + num3 + " (for purchase) | balance " + account.Balance);
		}
		Listing listing = Find(id);
		string text = null;
		if (listing == null)
		{
			text = "@s_gone";
		}
		else if (listing.SellerId == pid)
		{
			text = "@s_own";
		}
		else if (num < 1 || num > listing.Item.Stack)
		{
			text = "@s_amount_changed";
		}
		else if (listing.Price != num2)
		{
			text = "@s_price_changed";
		}
		else if (account.Balance < (long)listing.Price * (long)num)
		{
			text = "@s_no_funds|" + (long)listing.Price * (long)num + "|" + account.Balance;
		}
		if (text != null)
		{
			Save();
			if (num3 > 0)
			{
				Result(sender, false, "s_buy_failed_deposit", text);
			}
			else
			{
				Result(sender, false, "s_raw", text);
			}
			SendData(sender, pid);
			return;
		}
		long num4 = (long)listing.Price * (long)num;
		ItemRecord itemRecord = listing.Item.Clone(num);
		RecordTrade(itemRecord.Prefab, listing.Price, num);
		listing.Item.Stack -= num;
		if (listing.Item.Stack <= 0)
		{
			Listings.Remove(listing);
		}
		long num5 = (long)Math.Floor((double)num4 * ((double)Plugin.FeePercent.Value / 100.0));
		account.Balance -= num4;
		account.Items.Add(itemRecord);
		HistoryEntry historyEntry2 = new HistoryEntry();
		historyEntry2.Kind = HistoryKind.Purchase;
		historyEntry2.Amount = -num4;
		historyEntry2.Prefab = itemRecord.Prefab;
		historyEntry2.Stack = num;
		historyEntry2.Other = listing.SellerName;
		account.AddHistory(historyEntry2);
		Account account2 = Acc(listing.SellerId, listing.SellerName);
		account2.Balance += num4 - num5;
		HistoryEntry historyEntry3 = new HistoryEntry();
		historyEntry3.Kind = HistoryKind.Sale;
		historyEntry3.Amount = num4 - num5;
		historyEntry3.Fee = num5;
		historyEntry3.Prefab = itemRecord.Prefab;
		historyEntry3.Stack = num;
		historyEntry3.Other = name;
		account2.AddHistory(historyEntry3);
		Save();
		Audit(name, pid, "BUY #" + listing.Id + " " + itemRecord.Prefab + " x" + num + " from " + listing.SellerName + " (" + listing.SellerId + ") for " + num4 + " fee " + num5 + " | buyer balance " + account.Balance + " | seller balance " + account2.Balance);
		DeliverItems(pid, sender);
		Result(sender, true, "s_bought", Label(itemRecord), num4);
		SendData(sender, pid);
		long num6 = FindPeerOf(listing.SellerId);
		if (num6 != 0)
		{
			Result(num6, true, "s_sold", name, Label(itemRecord), num4 - num5);
			if (num6 != sender)
			{
				SendData(num6, listing.SellerId);
			}
		}
		BroadcastChanged();
	}

	public static void RPC_Cancel(long sender, long id)
	{
		if (!IsServer)
		{
			return;
		}
		EnsureLoaded();
		if (Resolve(sender, out var pid, out var name))
		{
			Listing listing = Find(id);
			if (listing == null || listing.SellerId != pid)
			{
				Result(sender, false, "s_not_found");
				SendData(sender, pid);
				return;
			}
			Listings.Remove(listing);
			Acc(pid, name).Items.Add(listing.Item);
			Save();
			Audit(name, pid, "CANCEL #" + listing.Id + " " + listing.Item.Prefab + " x" + listing.Item.Stack);
			DeliverItems(pid, sender);
			Result(sender, true, "s_cancelled", Label(listing.Item));
			SendData(sender, pid);
			BroadcastChanged();
		}
	}

	public static void RPC_Claim(long sender)
	{
		if (!IsServer)
		{
			return;
		}
		EnsureLoaded();
		if (Resolve(sender, out var pid, out var _))
		{
			if (!Accounts.TryGetValue(pid, out var value) || value.Items.Count == 0)
			{
				Result(sender, false, "s_mail_empty");
			}
			else
			{
				DeliverItems(pid, sender);
				Result(sender, true, "s_claimed");
			}
			SendData(sender, pid);
		}
	}

	public static void RPC_Deposit(long sender, int amount)
	{
		if (!IsServer)
		{
			return;
		}
		EnsureLoaded();
		if (amount > 0)
		{
			if (!Resolve(sender, out var pid, out var name))
			{
				SendBack(sender, amount, null);
				Result(sender, false, "s_unresolved_coins");
				return;
			}
			if (amount > 10000000)
			{
				SendBack(sender, amount, null);
				Result(sender, false, "s_bad_amount");
				return;
			}
			Account account = Acc(pid, name);
			account.Balance += amount;
			HistoryEntry historyEntry = new HistoryEntry();
			historyEntry.Kind = HistoryKind.Deposit;
			historyEntry.Amount = amount;
			account.AddHistory(historyEntry);
			Save();
			Audit(name, pid, "DEPOSIT " + amount + " | balance " + account.Balance);
			Result(sender, true, "s_deposited", amount, account.Balance);
			SendData(sender, pid);
		}
	}

	public static void RPC_Withdraw(long sender, int amount)
	{
		if (!IsServer)
		{
			return;
		}
		EnsureLoaded();
		if (amount > 0 && Resolve(sender, out var pid, out var name))
		{
			Account account = Acc(pid, name);
			if (account.Balance < amount)
			{
				Result(sender, false, "s_withdraw_too_much", account.Balance);
				SendData(sender, pid);
				return;
			}
			account.Balance -= amount;
			HistoryEntry historyEntry = new HistoryEntry();
			historyEntry.Kind = HistoryKind.Withdraw;
			historyEntry.Amount = -amount;
			account.AddHistory(historyEntry);
			Save();
			Audit(name, pid, "WITHDRAW " + amount + " | balance " + account.Balance);
			SendBack(sender, amount, null);
			Result(sender, true, "s_withdrawn", amount, account.Balance);
			SendData(sender, pid);
		}
	}

	public static void Tick()
	{
		if (!_loaded || Plugin.ExpireHours.Value <= 0)
		{
			return;
		}
		long num = DateTime.UtcNow.Ticks - TimeSpan.FromHours(Plugin.ExpireHours.Value).Ticks;
		bool flag = ExpireOrders(num);
		for (int num2 = Listings.Count - 1; num2 >= 0; num2--)
		{
			Listing listing = Listings[num2];
			if (listing.CreatedTicks <= num)
			{
				Acc(listing.SellerId, listing.SellerName).Items.Add(listing.Item);
				Listings.RemoveAt(num2);
				flag = true;
				Audit(listing.SellerName, listing.SellerId, "EXPIRE #" + listing.Id + " " + listing.Item.Prefab + " x" + listing.Item.Stack);
				long num3 = FindPeerOf(listing.SellerId);
				if (num3 != 0)
				{
					Result(num3, false, "s_expired", Label(listing.Item));
				}
			}
		}
		if (flag)
		{
			Save();
			BroadcastChanged();
		}
	}

	private static void ClearOrders()
	{
		Orders.Clear();
		Bounties.Clear();
		_nextOrderId = 1L;
		_nextBountyId = 1L;
	}

	private static void ReadOrders(ZPackage pkg)
	{
		_nextOrderId = pkg.ReadLong();
		int num = pkg.ReadInt();
		for (int i = 0; i < num; i++)
		{
			Orders.Add(BuyOrder.Read(pkg));
		}
		_nextBountyId = pkg.ReadLong();
		int num2 = pkg.ReadInt();
		for (int j = 0; j < num2; j++)
		{
			Bounties.Add(Bounty.Read(pkg));
		}
	}

	private static void WriteOrders(ZPackage pkg)
	{
		pkg.Write(_nextOrderId);
		pkg.Write(Orders.Count);
		foreach (BuyOrder order in Orders)
		{
			order.Write(pkg);
		}
		pkg.Write(_nextBountyId);
		pkg.Write(Bounties.Count);
		foreach (Bounty bounty in Bounties)
		{
			bounty.Write(pkg);
		}
	}

	private static bool IsAdminSender(long sender)
	{
		try
		{
			if (sender == ZDOMan.GetSessionID())
			{
				return ZNet.instance.LocalPlayerIsAdminOrHost();
			}
			ZNetPeer peer = ZNet.instance.GetPeer(sender);
			return peer != null && peer.m_socket != null && ZNet.instance.IsAdmin(peer.m_socket.GetHostName());
		}
		catch
		{
			return false;
		}
	}

	private static void History(Account a, HistoryKind kind, long amount, long fee, string prefab, int stack, string other, string text)
	{
		HistoryEntry historyEntry = new HistoryEntry();
		historyEntry.Kind = kind;
		historyEntry.Amount = amount;
		historyEntry.Fee = fee;
		historyEntry.Prefab = prefab ?? "";
		historyEntry.Stack = stack;
		historyEntry.Other = other ?? "";
		historyEntry.Text = text ?? "";
		a.AddHistory(historyEntry);
	}

	private static void ApplyDeposit(Account a, long pid, string name, int deposit, string reason)
	{
		if (deposit > 0)
		{
			a.Balance += deposit;
			History(a, HistoryKind.Deposit, deposit, 0L, "", 0, "", "");
			Audit(name, pid, "DEPOSIT " + deposit + " (" + reason + ") | balance " + a.Balance);
		}
	}

	private static void Notify(long pid, bool ok, string key, params object[] args)
	{
		long num = FindPeerOf(pid);
		if (num != 0)
		{
			Result(num, ok, key, args);
			SendData(num, pid);
		}
	}

	private static void SendItemsBack(long target, List<ItemRecord> items)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Expected O, but got Unknown
		ZPackage val = new ZPackage();
		val.Write(0L);
		val.Write(items.Count);
		foreach (ItemRecord item in items)
		{
			item.Write(val);
		}
		ZRoutedRpc.instance.InvokeRoutedRPC(target, "Marketplace_Deliver", new object[1] { val });
	}

	private static string PrefabToken(string prefab)
	{
		ItemRecord itemRecord = new ItemRecord();
		itemRecord.Prefab = prefab;
		itemRecord.Stack = 1;
		string text = Label(itemRecord);
		return text.Substring(0, text.Length - 3);
	}

	private static BuyOrder FindOrder(long id)
	{
		foreach (BuyOrder order in Orders)
		{
			if (order.Id == id)
			{
				return order;
			}
		}
		return null;
	}

	private static Bounty FindBounty(long id)
	{
		foreach (Bounty bounty in Bounties)
		{
			if (bounty.Id == id)
			{
				return bounty;
			}
		}
		return null;
	}

	private static void FailWithDeposit(long sender, long pid, int deposit, string error)
	{
		Save();
		if (deposit > 0)
		{
			Result(sender, false, "s_buy_failed_deposit", error);
		}
		else
		{
			Result(sender, false, "s_raw", error);
		}
		SendData(sender, pid);
	}

	public static void RPC_OrderCreate(long sender, ZPackage pkg)
	{
		if (!IsServer)
		{
			return;
		}
		EnsureLoaded();
		string text;
		int num;
		int num2;
		int num3;
		try
		{
			text = pkg.ReadString();
			num = pkg.ReadInt();
			num2 = pkg.ReadInt();
			num3 = Math.Max(0, pkg.ReadInt());
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Corrupted order packet from " + sender + ": " + ex.Message));
			return;
		}
		if (!Resolve(sender, out var pid, out var name))
		{
			if (num3 > 0)
			{
				SendBack(sender, num3, null);
			}
			Result(sender, false, "s_unresolved_coins");
			return;
		}
		Account account = Acc(pid, name);
		ApplyDeposit(account, pid, name, num3, "for buy order");
		long num4 = (long)num * (long)num2;
		int num5 = 0;
		foreach (BuyOrder order in Orders)
		{
			if (order.BuyerId == pid)
			{
				num5++;
			}
		}
		string text2 = null;
		if (num < 1 || num > 100000)
		{
			text2 = "@s_bad_amount";
		}
		else if (!ItemRecord.PrefabExists(text))
		{
			text2 = "@s_unknown_item";
		}
		else if (IsBlocked(text))
		{
			text2 = "@s_blocked";
		}
		else if (num2 < 1 || num2 > Plugin.MaxPricePerUnit.Value)
		{
			text2 = "@s_bad_price|" + Plugin.MaxPricePerUnit.Value;
		}
		else if (num4 > int.MaxValue)
		{
			text2 = "@s_total_high";
		}
		else if (num5 >= Plugin.MaxOrdersPerPlayer.Value)
		{
			text2 = "@s_order_limit|" + Plugin.MaxOrdersPerPlayer.Value;
		}
		else if (account.Balance < num4)
		{
			text2 = "@s_no_funds|" + num4 + "|" + account.Balance;
		}
		if (text2 != null)
		{
			FailWithDeposit(sender, pid, num3, text2);
			return;
		}
		account.Balance -= num4;
		BuyOrder buyOrder = new BuyOrder();
		buyOrder.Id = _nextOrderId++;
		buyOrder.BuyerId = pid;
		buyOrder.BuyerName = name;
		buyOrder.Prefab = text;
		buyOrder.Remaining = num;
		buyOrder.Price = num2;
		buyOrder.CreatedTicks = DateTime.UtcNow.Ticks;
		Orders.Add(buyOrder);
		History(account, HistoryKind.OrderEscrow, -num4, 0L, text, num, "", "");
		Save();
		Audit(name, pid, "ORDER #" + buyOrder.Id + " want " + text + " x" + num + " @ " + num2 + " escrow " + num4 + " | balance " + account.Balance);
		Result(sender, true, "s_order_created", PrefabToken(text) + " x" + num, num2, num4);
		SendData(sender, pid);
		BroadcastChanged();
	}

	public static void RPC_OrderFill(long sender, ZPackage pkg)
	{
		if (!IsServer)
		{
			return;
		}
		EnsureLoaded();
		List<ItemRecord> list = new List<ItemRecord>();
		long id;
		int num;
		try
		{
			id = pkg.ReadLong();
			num = pkg.ReadInt();
			int num2 = pkg.ReadInt();
			for (int i = 0; i < num2; i++)
			{
				list.Add(ItemRecord.Read(pkg));
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Corrupted order fill packet from " + sender + ": " + ex.Message));
			return;
		}
		if (!Resolve(sender, out var pid, out var name))
		{
			SendItemsBack(sender, list);
			Result(sender, false, "s_unresolved_item");
			return;
		}
		BuyOrder buyOrder = FindOrder(id);
		int num3 = 0;
		bool flag = false;
		foreach (ItemRecord item in list)
		{
			num3 += Math.Max(0, item.Stack);
			if (buyOrder != null && item.Prefab != buyOrder.Prefab)
			{
				flag = true;
			}
		}
		string text = null;
		if (buyOrder == null)
		{
			text = "@s_order_gone";
		}
		else if (buyOrder.BuyerId == pid)
		{
			text = "@s_order_own";
		}
		else if (buyOrder.Price != num)
		{
			text = "@s_price_changed";
		}
		else if (flag || num3 < 1)
		{
			text = "@s_order_wrong_item";
		}
		else if (num3 > buyOrder.Remaining)
		{
			text = "@s_amount_changed";
		}
		Account account = Acc(pid, name);
		if (text != null)
		{
			account.Items.AddRange(list);
			DeliverItems(pid, sender);
			Result(sender, false, "s_create_failed", text);
			SendData(sender, pid);
			return;
		}
		long num4 = (long)num3 * (long)buyOrder.Price;
		long num5 = (long)Math.Floor((double)num4 * ((double)Plugin.FeePercent.Value / 100.0));
		account.Balance += num4 - num5;
		History(account, HistoryKind.OrderSale, num4 - num5, num5, buyOrder.Prefab, num3, buyOrder.BuyerName, "");
		Account account2 = Acc(buyOrder.BuyerId, buyOrder.BuyerName);
		account2.Items.AddRange(list);
		History(account2, HistoryKind.OrderFilled, 0L, 0L, buyOrder.Prefab, num3, name, "");
		RecordTrade(buyOrder.Prefab, buyOrder.Price, num3);
		buyOrder.Remaining -= num3;
		buyOrder.Delivered += num3;
		if (buyOrder.Remaining <= 0)
		{
			Orders.Remove(buyOrder);
		}
		Save();
		Audit(name, pid, "FILL ORDER #" + buyOrder.Id + " " + buyOrder.Prefab + " x" + num3 + " to " + buyOrder.BuyerName + " (" + buyOrder.BuyerId + ") for " + num4 + " fee " + num5 + " | seller balance " + account.Balance + " | order remaining " + buyOrder.Remaining);
		Result(sender, true, "s_order_sold", PrefabToken(buyOrder.Prefab) + " x" + num3, num4 - num5);
		SendData(sender, pid);
		Notify(buyOrder.BuyerId, true, "s_order_filled", name, PrefabToken(buyOrder.Prefab) + " x" + num3);
		BroadcastChanged();
	}

	public static void RPC_OrderCancel(long sender, long id)
	{
		if (!IsServer)
		{
			return;
		}
		EnsureLoaded();
		if (!Resolve(sender, out var pid, out var name))
		{
			return;
		}
		BuyOrder buyOrder = FindOrder(id);
		bool flag = IsAdminSender(sender);
		if (buyOrder == null || (buyOrder.BuyerId != pid && !flag))
		{
			Result(sender, false, "s_order_gone");
			SendData(sender, pid);
			return;
		}
		long escrow = buyOrder.Escrow;
		Orders.Remove(buyOrder);
		Account account = Acc(buyOrder.BuyerId, buyOrder.BuyerName);
		account.Balance += escrow;
		History(account, HistoryKind.OrderRefund, escrow, 0L, buyOrder.Prefab, buyOrder.Remaining, "", "");
		Save();
		Audit(name, pid, "CANCEL ORDER #" + buyOrder.Id + ((buyOrder.BuyerId != pid) ? " (admin)" : "") + " refund " + escrow + " to " + buyOrder.BuyerName + " | balance " + account.Balance);
		Result(sender, true, "s_order_cancelled", PrefabToken(buyOrder.Prefab), escrow);
		SendData(sender, pid);
		if (buyOrder.BuyerId != pid)
		{
			Notify(buyOrder.BuyerId, false, "s_order_cancelled_admin", PrefabToken(buyOrder.Prefab), escrow);
		}
		BroadcastChanged();
	}

	public static void RPC_BountyCreate(long sender, ZPackage pkg)
	{
		if (!IsServer)
		{
			return;
		}
		EnsureLoaded();
		string text;
		string text2;
		int num;
		int num2;
		try
		{
			text = (pkg.ReadString() ?? "").Trim();
			text2 = (pkg.ReadString() ?? "").Trim();
			num = pkg.ReadInt();
			num2 = Math.Max(0, pkg.ReadInt());
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Corrupted bounty packet from " + sender + ": " + ex.Message));
			return;
		}
		if (!Resolve(sender, out var pid, out var name))
		{
			if (num2 > 0)
			{
				SendBack(sender, num2, null);
			}
			Result(sender, false, "s_unresolved_coins");
			return;
		}
		Account account = Acc(pid, name);
		ApplyDeposit(account, pid, name, num2, "for bounty");
		int num3 = 0;
		foreach (Bounty bounty2 in Bounties)
		{
			if (bounty2.OwnerId == pid)
			{
				num3++;
			}
		}
		string text3 = null;
		if (text.Length == 0)
		{
			text3 = "@s_bounty_no_title";
		}
		else if (text.Length > 60 || text2.Length > 400)
		{
			text3 = "@s_bounty_too_long";
		}
		else if (num < 1 || num > 10000000)
		{
			text3 = "@s_bounty_bad_reward|" + 10000000;
		}
		else if (num3 >= Plugin.MaxBountiesPerPlayer.Value)
		{
			text3 = "@s_bounty_limit|" + Plugin.MaxBountiesPerPlayer.Value;
		}
		else if (account.Balance < num)
		{
			text3 = "@s_no_funds|" + num + "|" + account.Balance;
		}
		if (text3 != null)
		{
			FailWithDeposit(sender, pid, num2, text3);
			return;
		}
		account.Balance -= num;
		Bounty bounty = new Bounty();
		bounty.Id = _nextBountyId++;
		bounty.OwnerId = pid;
		bounty.OwnerName = name;
		bounty.Title = text;
		bounty.Description = text2;
		bounty.Reward = num;
		bounty.CreatedTicks = DateTime.UtcNow.Ticks;
		bounty.StateTicks = bounty.CreatedTicks;
		bounty.State = BountyState.Open;
		Bounties.Add(bounty);
		History(account, HistoryKind.BountyEscrow, -num, 0L, "", 0, "", text);
		Save();
		Audit(name, pid, "BOUNTY #" + bounty.Id + " \"" + text + "\" reward " + num + " | balance " + account.Balance);
		Result(sender, true, "s_bounty_created", text, num);
		SendData(sender, pid);
		BroadcastChanged();
	}

	public static void RPC_BountyAction(long sender, ZPackage pkg)
	{
		if (!IsServer)
		{
			return;
		}
		EnsureLoaded();
		long id;
		BountyAction bountyAction;
		try
		{
			id = pkg.ReadLong();
			bountyAction = (BountyAction)pkg.ReadInt();
		}
		catch
		{
			return;
		}
		if (!Resolve(sender, out var pid, out var name))
		{
			return;
		}
		bool flag = IsAdminSender(sender);
		Bounty bounty = FindBounty(id);
		if (bounty == null)
		{
			Result(sender, false, "s_bounty_gone");
			SendData(sender, pid);
			return;
		}
		bool flag2 = bounty.OwnerId == pid;
		bool flag3 = bounty.State != BountyState.Open && bounty.ContractorId == pid;
		long ticks = DateTime.UtcNow.Ticks;
		bool flag4 = Plugin.BountyClaimTimeoutHours.Value > 0 && ticks - bounty.StateTicks >= TimeSpan.FromHours(Plugin.BountyClaimTimeoutHours.Value).Ticks;
		switch (bountyAction)
		{
		case BountyAction.Claim:
			if (bounty.State == BountyState.Open && !flag2)
			{
				bounty.State = BountyState.Claimed;
				bounty.ContractorId = pid;
				bounty.ContractorName = name;
				bounty.StateTicks = ticks;
				Save();
				Audit(name, pid, "CLAIM BOUNTY #" + bounty.Id + " \"" + bounty.Title + "\"");
				Result(sender, true, "s_bounty_claimed", bounty.Title);
				Notify(bounty.OwnerId, true, "s_bounty_claimed_owner", name, bounty.Title);
				SendData(sender, pid);
				BroadcastChanged();
				return;
			}
			break;
		case BountyAction.Abandon:
			if (flag3)
			{
				Audit(name, pid, "ABANDON BOUNTY #" + bounty.Id);
				bounty.State = BountyState.Open;
				bounty.ContractorId = 0L;
				bounty.ContractorName = "";
				bounty.StateTicks = ticks;
				Save();
				Result(sender, true, "s_bounty_abandoned", bounty.Title);
				Notify(bounty.OwnerId, false, "s_bounty_abandoned_owner", name, bounty.Title);
				SendData(sender, pid);
				BroadcastChanged();
				return;
			}
			break;
		case BountyAction.Submit:
			if (flag3 && bounty.State == BountyState.Claimed)
			{
				bounty.State = BountyState.Submitted;
				bounty.StateTicks = ticks;
				Save();
				Audit(name, pid, "SUBMIT BOUNTY #" + bounty.Id);
				Result(sender, true, "s_bounty_submitted", bounty.Title);
				Notify(bounty.OwnerId, true, "s_bounty_submitted_owner", name, bounty.Title);
				SendData(sender, pid);
				BroadcastChanged();
				return;
			}
			break;
		case BountyAction.Confirm:
			if (bounty.State != BountyState.Open && (flag2 || flag))
			{
				long num = (long)Math.Floor((double)bounty.Reward * ((double)Plugin.FeePercent.Value / 100.0));
				Account account2 = Acc(bounty.ContractorId, bounty.ContractorName);
				account2.Balance += bounty.Reward - num;
				History(account2, HistoryKind.BountyReward, bounty.Reward - num, num, "", 0, bounty.OwnerName, bounty.Title);
				History(Acc(bounty.OwnerId, bounty.OwnerName), HistoryKind.BountyPaid, 0L, 0L, "", 0, bounty.ContractorName, bounty.Title);
				Bounties.Remove(bounty);
				Save();
				Audit(name, pid, "CONFIRM BOUNTY #" + bounty.Id + (flag2 ? "" : " (admin)") + " paid " + (bounty.Reward - num) + " fee " + num + " to " + bounty.ContractorName + " (" + bounty.ContractorId + ") | contractor balance " + account2.Balance);
				Result(sender, true, "s_bounty_confirmed", bounty.Title, bounty.ContractorName);
				Notify(bounty.ContractorId, true, "s_bounty_paid", bounty.OwnerName, bounty.Title, bounty.Reward - num);
				if (!flag2)
				{
					Notify(bounty.OwnerId, true, "s_bounty_confirmed_admin", bounty.Title);
				}
				SendData(sender, pid);
				BroadcastChanged();
				return;
			}
			break;
		case BountyAction.Reject:
			if (bounty.State == BountyState.Submitted && flag2)
			{
				bounty.State = BountyState.Claimed;
				bounty.StateTicks = ticks;
				Save();
				Audit(name, pid, "REJECT BOUNTY #" + bounty.Id + " submitted by " + bounty.ContractorName);
				Result(sender, true, "s_bounty_rejected", bounty.Title);
				Notify(bounty.ContractorId, false, "s_bounty_rejected_contractor", bounty.OwnerName, bounty.Title);
				SendData(sender, pid);
				BroadcastChanged();
				return;
			}
			break;
		case BountyAction.Cancel:
			if (flag || (flag2 && bounty.State == BountyState.Open) || (flag2 && bounty.State == BountyState.Claimed && flag4))
			{
				Account account = Acc(bounty.OwnerId, bounty.OwnerName);
				account.Balance += bounty.Reward;
				History(account, HistoryKind.BountyRefund, bounty.Reward, 0L, "", 0, "", bounty.Title);
				Bounties.Remove(bounty);
				Save();
				Audit(name, pid, "CANCEL BOUNTY #" + bounty.Id + (flag2 ? "" : " (admin)") + " refund " + bounty.Reward + " to " + bounty.OwnerName + " | balance " + account.Balance);
				Result(sender, true, "s_bounty_cancelled", bounty.Title, bounty.Reward);
				if (bounty.ContractorId != 0)
				{
					Notify(bounty.ContractorId, false, "s_bounty_cancelled_contractor", bounty.Title);
				}
				if (!flag2)
				{
					Notify(bounty.OwnerId, false, "s_bounty_cancelled_admin", bounty.Title, bounty.Reward);
				}
				SendData(sender, pid);
				BroadcastChanged();
				return;
			}
			break;
		}
		Result(sender, false, "s_bounty_not_allowed");
		SendData(sender, pid);
	}

	private static bool ExpireOrders(long limit)
	{
		bool result = false;
		for (int num = Orders.Count - 1; num >= 0; num--)
		{
			BuyOrder buyOrder = Orders[num];
			if (buyOrder.CreatedTicks <= limit)
			{
				Account account = Acc(buyOrder.BuyerId, buyOrder.BuyerName);
				long escrow = buyOrder.Escrow;
				account.Balance += escrow;
				History(account, HistoryKind.OrderRefund, escrow, 0L, buyOrder.Prefab, buyOrder.Remaining, "", "");
				Orders.RemoveAt(num);
				result = true;
				Audit(buyOrder.BuyerName, buyOrder.BuyerId, "EXPIRE ORDER #" + buyOrder.Id + " refund " + escrow + " | balance " + account.Balance);
				Notify(buyOrder.BuyerId, false, "s_order_expired", PrefabToken(buyOrder.Prefab), escrow);
			}
		}
		for (int num2 = Bounties.Count - 1; num2 >= 0; num2--)
		{
			Bounty bounty = Bounties[num2];
			if (bounty.State == BountyState.Open && bounty.StateTicks <= limit)
			{
				Account account2 = Acc(bounty.OwnerId, bounty.OwnerName);
				account2.Balance += bounty.Reward;
				History(account2, HistoryKind.BountyRefund, bounty.Reward, 0L, "", 0, "", bounty.Title);
				Bounties.RemoveAt(num2);
				result = true;
				Audit(bounty.OwnerName, bounty.OwnerId, "EXPIRE BOUNTY #" + bounty.Id + " refund " + bounty.Reward + " | balance " + account2.Balance);
				Notify(bounty.OwnerId, false, "s_bounty_expired", bounty.Title, bounty.Reward);
			}
		}
		return result;
	}

	private static void ClearPrices()
	{
		Trades.Clear();
	}

	private static void RecordTrade(string prefab, int price, int qty)
	{
		if (!string.IsNullOrEmpty(prefab) && price >= 1 && qty >= 1)
		{
			if (!Trades.TryGetValue(prefab, out var value))
			{
				value = new List<Trade>();
				Trades[prefab] = value;
			}
			Trade trade = new Trade();
			trade.Ticks = DateTime.UtcNow.Ticks;
			trade.Price = price;
			trade.Qty = qty;
			value.In