C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
/* Changes 1.1.8
* Fixed an issue where reset commands wouldn't run when there wasn't a BasePlayer.* Added stack handling config option
* Updated to prevent Skill Tree's Ration perk from affecting tokens.* Added console command: givetoken <target> <tier> [optional: amount]
* Updated dictionaries to be case insensitive for keys.* Added console command: removevip <target> <tier>
* Patched for October update.*/
namespace Oxide.Plugins {
[Info("VIPToken", "https://discord.gg/TrJ7jnS233", "1.1.8")]
[Description("Monetary VIP token")]
class VIPToken : RustPlugin {
#region Config
public Configuration config;
const string prefix = "<color=#00F7FF>[VIP Token]:</color>";
List<ulong> tokenskins = new List<ulong>();
public class Configuration {
[JsonProperty("Maximum VIP days that a player can accumulate per tier")]
public int max_vip_days = 90;
[JsonProperty("Sound effect when purchasing or consuming a token")]
public string purchase_effect = "assets/prefabs/deployable/vendingmachine/effects/vending-machine-purchase-human.prefab";
[JsonProperty("Sound effect when consuming a token.Set it to nothing if you do not want an effect")]
public string consume_effect = "assets/prefabs/misc/easter/painted eggs/effects/gold_open.prefab";
[JsonProperty("Password for command verification (make one up)")]
public string verification_password = "ChangeThisPassword124u5109123";
[JsonProperty("Chat commands for opening the menu.")]
public string[] chat_commands = {
"tokenmenu", "tokenbalance", "storetoken", "redeemtoken"
}
;
[JsonProperty("Date time format (case sensitive): dd == day.MM == month.yyyy == year")]
public string date_format = "dd-MM-yyyy";
[JsonProperty("Prevent players from consuming a token if they are already in the group?")]
public bool prevent_redemption = true;
[JsonProperty("How often should the plugin check to see if a player has run out of VIP [seconds]?")]
public float check_rate = 3600f;
[JsonProperty("Should VIPTokens handle stacking of VIP token items? [set to false if using a stacks plugin]")]
public bool handle_stacking = true;
[JsonProperty("Add your vip tiers and commands here.Use {id} in place of a players userid and {name} in place of their name.")]
public Dictionary<string, VIPInfo> vip_levels = new Dictionary<string, VIPInfo>(StringComparer.InvariantCultureIgnoreCase) {
}
;
public class VIPInfo {
public string name;
public string time_type = "day";
public int time_to_add;
public string vip_group;
public string vip_description;
public bool remove_tokens_on_wipe = false;
public TokenInfo token_item;
public CommandInfo _command;
public List<CommandInfo> _commands;
public List<CommandInfo> remove_commands;
}
public class TokenInfo {
public string name;
public ulong skin;
public string item_shortname;
}
public class CommandInfo {
public string command;
public string message;
public string public_message;
public bool hook = false;
}
public string ToJson() => JsonConvert.SerializeObject(this);
public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson());
}
protected override void LoadDefaultConfig() {
Puts("Loading a new config.");
config = new Configuration();
config.vip_levels.Add("vip", new Configuration.VIPInfo() {
name = "vip tier 1", time_type = "day", time_to_add = 30, vip_group = "vip", vip_description = "This is an example of a group only VIP token", token_item = new Configuration.TokenInfo() {
name = "VIP Token - 30 days", skin = 2529344523, item_shortname = "radiationresisttea.pure"
}
}
);
config.vip_levels.Add("command_1", new Configuration.VIPInfo() {
name = "Command Token 1", vip_description = "This is an example of a command only vip token", token_item = new Configuration.TokenInfo() {
name = "Day token", skin = 2546992444, item_shortname = "radiationresisttea.pure"
}
, _command = new Configuration.CommandInfo() {
command = "env.time 11", message = "You made it day.", public_message = "{name} has redeemed a token, turning night to day.", hook = true
}
}
);
config.vip_levels.Add("command_2", new Configuration.VIPInfo() {
name = "Give Item", time_type = "hour", time_to_add = 30, vip_group = "vip2", vip_description = "This is an example of a group and command token.\nYou can also add new lines like this.", token_item = new Configuration.TokenInfo() {
name = "Item Giver", skin = 2546992685, item_shortname = "radiationresisttea.pure"
}
, _command = new Configuration.CommandInfo() {
command = "inventory.giveto {id} scrap 1000", message = "You received some scrap.", public_message = "{name} has redeemed a token and give themselves some scrap.ID: {id}", hook = true
}
, _commands = new List<Configuration.CommandInfo>() {
new Configuration.CommandInfo() {
command = "inventory.giveto {id} scrap 1000", message = "You received some scrap.", public_message = "{name} has redeemed a token and give themselves some scrap.ID: {id}", hook = true
}
, new Configuration.CommandInfo() {
command = "inventory.giveto {id} wood 1000", message = "You received some wood.", public_message = "{name} has redeemed a token and give themselves some wood.ID: {id}", hook = true
}
}
, remove_commands = new List<Configuration.CommandInfo>() {
new Configuration.CommandInfo() {
command = "inventory.giveto {id} scrap 1000", message = "You received some scrap when your vip ended.", public_message = "{name} has redeemed a token and give themselves some scrap.ID: {id}", hook = true
}
}
}
);
}
protected override void LoadConfig() {
base.LoadConfig();
try {
config = Config.ReadObject<Configuration>();
if (config == null) {
throw new JsonException();
}
if (!config.ToDictionary().Keys.SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys)) {
Puts("Configuration appears to be outdated; updating and saving");
SaveConfig();
}
}
catch {
Puts($"Configuration file {Name}.json is invalid; using defaults");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void SaveConfig() {
Puts($"Configuration changes saved to {Name}.json");
Config.WriteObject(config, true);
}
#endregion
#region Data
PlayerInfo pcdData;
private DynamicConfigFile PCDDATA;
void Init() {
permission.RegisterPermission("viptoken.admin", this);
PCDDATA = Interface.Oxide.DataFileSystem.GetFile("VIPToken");
cmd.AddConsoleCommand("addtoken", this, nameof(AddToken));
if (!config.handle_stacking) {
Unsubscribe(nameof(CanStackItem));
Unsubscribe(nameof(CanCombineDroppedItem));
}
}
void Unload() {
SaveData();
foreach (var player in BasePlayer.activePlayerList) {
CuiHelper.DestroyUi(player, "VIP_Tokens");
CuiHelper.DestroyUi(player, "VIP_Tokeninfo");
}
TokenSkins.Clear();
}
void Loaded() {
LoadConfig();
LoadData();
foreach (KeyValuePair<string, Configuration.VIPInfo> kvp in config.vip_levels) {
if (!permission.GroupExists(kvp.Value.vip_group)) permission.CreateGroup(kvp.Value.vip_group, "", 1);
VIPTiers.Add(kvp.Key);
}
}
void SaveData() {
PCDDATA.WriteObject(pcdData);
}
void LoadData() {
try {
pcdData = Interface.GetMod().DataFileSystem.ReadObject<PlayerInfo>("VIPToken");
}
catch {
Puts("Couldn't load player data, creating new Playerfile");
pcdData = new PlayerInfo();
}
}
class PlayerInfo {
public Dictionary<ulong, PCDInfo> pentiy = new Dictionary<ulong, PCDInfo>();
public PlayerInfo() {
}
}
class PCDInfo {
public Dictionary<string, int> Redemptions = new Dictionary<string, int>(StringComparer.InvariantCultureIgnoreCase);
public Dictionary<string, VIPData> vip_levels = new Dictionary<string, VIPData>(StringComparer.InvariantCultureIgnoreCase);
}
class VIPData {
public DateTime start_time;
public DateTime end_date;
public string token_key;
}
#endregion
#region Hooks
void OnNewSave(string filename) {
foreach (KeyValuePair<ulong, PCDInfo> kvp in pcdData.pentiy) {
foreach (KeyValuePair<string, Configuration.VIPInfo> ckvp in config.vip_levels) {
if (kvp.Value.Redemptions.ContainsKey(ckvp.Key) && ckvp.Value.remove_tokens_on_wipe) {
kvp.Value.Redemptions[ckvp.Key] = 0;
}
}
}
}
object OnPlayerDeath(BasePlayer player, HitInfo info) {
if (player != null && player.net?.connection?.authLevel >= 0) {
CuiHelper.DestroyUi(player, "VIP_Tokens");
CuiHelper.DestroyUi(player, "VIP_Tokeninfo");
}
return null;
}
object CanCombineDroppedItem(DroppedItem item, DroppedItem targetItem) {
if (item == null) return null;
if (tokenskins.Contains(item.item.skin) || tokenskins.Contains(targetItem.item.skin)) {
return false;
}
return null;
}
object CanStackItem(Item item, Item targetItem) {
if (item == null) return null;
if (tokenskins.Contains(item.skin)) {
return false;
}
return null;
}
DateTime CalculateEndDate(DateTime date, double add, string type) {
Puts($"date: {date} - add {add} - type: {type}");
if (type.ToLower().Contains("second")) return date.AddSeconds(add);
if (type.ToLower().Contains("minute")) return date.AddMinutes(add);
if (type.ToLower().Contains("hour")) return date.AddHours(add);
if (type.ToLower().Contains("day")) return date.AddDays(add);
Puts($"Returning date: {date.ToString()}");
return date;
}
object OnPlayerAddModifiers(BasePlayer player, Item item, ItemModConsumable consumable) {
if (player == null || item == null || !TokenSkins.Contains(item.skin)) return null;
foreach (KeyValuePair<string, Configuration.VIPInfo> kvp in config.vip_levels) {
if (kvp.Value.token_item.skin == item.skin) {
if (kvp.Value.vip_group != null) {
if (!pcdData.pentiy.ContainsKey(player.userID)) pcdData.pentiy.Add(player.userID, new PCDInfo());
var playerData = pcdData.pentiy[player.userID];
if (config.prevent_redemption && permission.UserHasGroup(player.UserIDString, kvp.Value.vip_group) && (!playerData.vip_levels.ContainsKey(kvp.Key) || playerData.vip_levels[kvp.Key].end_date < DateTime.Now)) {
PrintToChat(player, $"{prefix} You are already in this group.Wait until {kvp.Value.name.TitleCase()} expires before consuming this token.Type /{config.chat_commands.First()} to open the menu.");
LogToFile("VIPWallet", $"[{DateTime.Now}] {player.displayName} tried to consume token {kvp.Key} but they are already part of the group.", this, true);
CreateToken(player, kvp.Key.ToLower());
return false;
}
if (!permission.UserHasGroup(player.UserIDString, kvp.Value.vip_group)) permission.AddUserGroup(player.UserIDString, kvp.Value.vip_group);
if (!playerData.vip_levels.ContainsKey(kvp.Value.vip_group.ToLower())) playerData.vip_levels.Add(kvp.Value.vip_group.ToLower(), new VIPData());
var VIPData = playerData.vip_levels[kvp.Value.vip_group.ToLower()];
if (VIPData.start_time.Year < 2000) VIPData.start_time = DateTime.Now;
if (VIPData.end_date.Year < 2000) {
VIPData.end_date = CalculateEndDate(DateTime.Now, kvp.Value.time_to_add, kvp.Value.time_type);
VIPData.token_key = kvp.Key;
SaveData();
PrintToChat(player, $"{prefix} You have redeemed {kvp.Value.time_to_add} {kvp.Value.time_type}s of {kvp.Value.vip_group.ToUpper()} for {kvp.Value.name}.New end date: {VIPData.end_date.ToString($" {
config.date_format
}
HH-mm")}");
EffectNetwork.Send(new Effect(config.consume_effect, player.transform.position, player.transform.position), player.net.connection);
LogToFile("VIPTokens", $"[{DateTime.Now}] Added {player.displayName}[{player.UserIDString}] to VIPKey: {kvp.Key}.Expires: {VIPData.end_date.ToString($" {
config.date_format
}
HH-mm")}.", this, true);
if (kvp.Value._command == null && kvp.Value._commands == null) return false;
}
else {
var endDate = CalculateEndDate(VIPData.end_date, kvp.Value.time_to_add, kvp.Value.time_type);
if (endDate > DateTime.Now.AddDays(config.max_vip_days)) {
PrintToChat(player, $"{prefix} You cannot have more than {config.max_vip_days} days of {kvp.Value.name} using tokens.");
LogToFile("VIPWallet", $"[{DateTime.Now}] {player.displayName} tried to consume token {kvp.Key} but failed the max vip days check.Spawning them a new one.", this, true);
CreateToken(player, kvp.Key.ToLower());
return false;
}
else {
var calculateFrom = DateTime.Now;
if (VIPData.end_date > calculateFrom) calculateFrom = VIPData.end_date;
VIPData.end_date = CalculateEndDate(calculateFrom, kvp.Value.time_to_add, kvp.Value.time_type);
VIPData.token_key = kvp.Key;
SaveData();
PrintToChat(player, $"{prefix} You have redeemed {kvp.Value.time_to_add} {kvp.Value.time_type}s of VIP for {kvp.Value.name}.New end date: {VIPData.end_date.ToString($" {
config.date_format
}
HH-mm")}");
EffectNetwork.Send(new Effect(config.consume_effect, player.transform.position, player.transform.position), player.net.connection);
LogToFile("VIPTokens", $"[{DateTime.Now}] Extended {player.displayName}[{player.UserIDString}]'s VIPKey: {kvp.Key}.Expires: {VIPData.end_date.ToString($" {
config.date_format
}
HH-mm")}.", this, true);
if (kvp.Value._command == null && kvp.Value._commands == null) return false;
}
}
}
if (kvp.Value._command != null) {
if (kvp.Value._command.command != null) {
rust.RunServerCommand(CommandBuilder(player.UserIDString, player.displayName, kvp.Value._command.command));
if (kvp.Value._command.message != null) PrintToChat(player, CommandBuilder(player.UserIDString, player.displayName, kvp.Value._command.message));
if (kvp.Value._command.public_message != null) PrintToChat(CommandBuilder(player.UserIDString, player.displayName, kvp.Value._command.public_message));
if (config.consume_effect != null) EffectNetwork.Send(new Effect(config.consume_effect, player.transform.position, player.transform.position), player.net.connection);
}
if (kvp.Value._command.hook) Interface.CallHook("OnTokenConsumed", player, kvp.Key);
if (kvp.Value._commands == null) return false;
}
if (kvp.Value._commands != null) {
foreach (var command in kvp.Value._commands) {
if (command.command != null) {
rust.RunServerCommand(CommandBuilder(player.UserIDString, player.displayName, command.command));
if (command.message != null) PrintToChat(player, CommandBuilder(player.UserIDString, player.displayName, command.message));
if (command.public_message != null) PrintToChat(CommandBuilder(player.UserIDString, player.displayName, command.public_message));
if (config.consume_effect != null) EffectNetwork.Send(new Effect(config.consume_effect, player.transform.position, player.transform.position), player.net.connection);
}
if (command.hook) Interface.CallHook("OnTokenConsumed", player, kvp.Key);
}
return false;
}
}
}
return null;
}
List<ulong> TokenSkins = new List<ulong>();
void OnServerInitialized(bool initial) {
timer.Every(config.check_rate, () => {
CheckSubscriptions();
}
);
foreach (KeyValuePair<string, Configuration.VIPInfo> kvp in config.vip_levels) {
tokenskins.Add(kvp.Value.token_item.skin);
}
Puts($"Loaded {tokenskins.Count} skins.");
foreach (var command in config.chat_commands) {
cmd.AddChatCommand(command, this, "SendTokenMenu");
}
Puts($"Added {config.chat_commands.Length} commands.");
foreach (var kvp in config.vip_levels) if (!TokenSkins.Contains(kvp.Value.token_item.skin)) TokenSkins.Add(kvp.Value.token_item.skin);
}
#endregion
#region Check subscriptions
[ChatCommand("forcecheck")]
void ForceCheck(BasePlayer player) {
if (!permission.UserHasPermission(player.UserIDString, "viptoken.admin")) return;
CheckSubscriptions();
}
void CheckSubscriptions() {
Dictionary<ulong, List<string>> DeleteData = new Dictionary<ulong, List<string>>();
if (pcdData.pentiy.Count == 0) return;
foreach (KeyValuePair<ulong, PCDInfo> kvp in pcdData.pentiy) {
List<string> del_tiers = new List<string>();
if (kvp.Value.vip_levels == null) continue;
Puts($"Checking {kvp.Value.vip_levels.Count} entries for {kvp.Key}");
foreach (KeyValuePair<string, VIPData> vkvp in kvp.Value.vip_levels) {
if (vkvp.Value.end_date > DateTime.Now) continue;
var perm = vkvp.Key;
Puts($"Attempting to remove {kvp.Key} from permission group: {perm}");
if (permission.UserHasGroup(kvp.Key.ToString(), perm)) permission.RemoveUserGroup(kvp.Key.ToString(), perm);
LogToFile("VIPTokens", $"[{DateTime.Now}] Removed {kvp.Key} from VIP: {vkvp.Key} as it has expired ({vkvp.Value.end_date.ToString()}).", this, true);
Configuration.VIPInfo vipInfo;
if (config.vip_levels.TryGetValue(vkvp.Value.token_key, out vipInfo) && vipInfo.remove_commands != null && vipInfo.remove_commands.Count > 0) {
var useridString = kvp.Key.ToString();
var player = BasePlayer.Find(useridString);
foreach (var command in vipInfo.remove_commands) {
if (command.command != null) {
rust.RunServerCommand(CommandBuilder(useridString, player?.displayName ?? useridString, command.command));
if (command.message != null && player != null) PrintToChat(player, CommandBuilder(useridString, player?.displayName ?? useridString, command.message));
if (command.public_message != null) PrintToChat(CommandBuilder(useridString, player?.displayName ?? useridString, command.public_message));
if (config.consume_effect != null && player != null) EffectNetwork.Send(new Effect(config.consume_effect, player.transform.position, player.transform.position), player.net.connection);
}
if (command.hook) Interface.CallHook("OnVIPEndedCommandFired", player, vkvp.Value.token_key);
}
}
del_tiers.Add(vkvp.Key);
}
if (del_tiers.Count > 0) {
DeleteData.Add(kvp.Key, del_tiers);
}
}
if (DeleteData.Count > 0) {
foreach (KeyValuePair<ulong, List<string>> kvp in DeleteData) {
var playerData = pcdData.pentiy[kvp.Key];
foreach (var str in kvp.Value) {
playerData.vip_levels.Remove(str);
}
if (playerData.vip_levels.Count == 0 && playerData.Redemptions.Count == 0) pcdData.pentiy.Remove(kvp.Key);
}
SaveData();
}
}
#endregion
#region Token
private void AddRedemption(ulong id, string tier, int amount = 1) {
if (!pcdData.pentiy.ContainsKey(id)) pcdData.pentiy.Add(id, new PCDInfo());
var playerData = pcdData.pentiy[id];
if (!playerData.Redemptions.ContainsKey(tier)) playerData.Redemptions.Add(tier, 0);
playerData.Redemptions[tier] += amount;
SaveData();
var player = FindOnlinePlayerByID(id);
if (player != null) {
EffectNetwork.Send(new Effect(config.purchase_effect, player.transform.position, player.transform.position), player.net.connection);
PrintToChat(player, $"{prefix} You have successfully purchased {amount}x {config.vip_levels[tier].name} token.Type <color=#DFF008>/{config.chat_commands.First()}</color> to see more information.");
LogToFile("VIPPurchases", $"[{DateTime.Now}] Player: {player.displayName}[{id}] purchased {amount}x Tier:{tier} token from the website.", this, true);
}
else LogToFile("VIPPurchases", $"[{DateTime.Now}] Player {id} purchased {amount}x Tier:{tier} token from the website.", this, true);
}
void GiveToken(BasePlayer player, Item item) {
var dropped = false;
if (!player.inventory.containerBelt.IsFull()) item.MoveToContainer(player.inventory.containerBelt);
else if (!player.inventory.containerMain.IsFull()) item.MoveToContainer(player.inventory.containerMain);
else {
item.DropAndTossUpwards(player.transform.position, 3);
dropped = true;
}
LogToFile("VIPWallet", $"[{DateTime.Now}] created token for {player.displayName} - {item.name}.Dropped to floor: {dropped}", this, true);
}
void CreateToken(BasePlayer player, string token_type, int quantity = 1) {
if (!config.vip_levels.ContainsKey(token_type)) {
Puts($"Token type {token_type} does not exist in the config.");
LogToFile("VIPWallet", $"[{DateTime.Now}] {player.displayName} tried to redeem Token type: {token_type}, but it is not valid.", this, true);
return;
}
var TokenData = config.vip_levels[token_type].token_item;
var item = ItemManager.CreateByName(TokenData.item_shortname, quantity, TokenData.skin);
item.name = TokenData.name;
LogToFile("VIPWallet", $"[{DateTime.Now}] Redemption: confirmed check for {player.displayName} - tier {token_type}.", this, true);
//test
GiveToken(player, item);
}
#endregion
#region Chat command
[ChatCommand("givetoken")]
void GiveToken(BasePlayer player, string command, string[] args) {
if (!permission.UserHasPermission(player.UserIDString, "viptoken.admin")) return;
if (config.vip_levels.Count == 0) return;
var quantity = 1;
if (args.Length == 0 || args.Length > 2 || !VIPTiers.Contains(args[0].ToLower())) {
PrintToChat(player, $"{prefix} Usage: /givetoken <{string.Join(", ", VIPTiers)}>[Optional: <quantity>]");
return;
}
if (args.Length == 2 && args[1].IsNumeric()) quantity = Convert.ToInt32(args[1]);
CreateToken(player, args[0].ToLower(), quantity);
}
[ConsoleCommand("givetoken")]
void GiveTolenConsole(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player != null && !permission.UserHasPermission(player.UserIDString, "viptoken.admin")) return;
if (config.vip_levels.Count == 0) return;
var quantity = 1;
if (arg.Args.Length == 0 || arg.Args.Length > 3 || !VIPTiers.Contains(arg.Args[1].ToLower())) {
arg.ReplyWith($"{prefix} Usage: /givetoken <target> <{string.Join(", ", VIPTiers)}>[Optional: <quantity>]");
return;
}
var target = FindPlayer(arg.Args[0]);
if (target == null) {
arg.ReplyWith($"Could not find a player that matched: {arg.Args[0]}");
return;
}
var tier = arg.Args[1].ToLower();
if (arg.Args.Length > 2) int.TryParse(arg.Args[2], out quantity);
if (quantity < 1) quantity = 1;
CreateToken(target, tier, quantity);
}
BasePlayer FindPlayer(string nameOrID) {
var target = BasePlayer.Find(nameOrID);
if (target == null) target = BasePlayer.activePlayerList.FirstOrDefault(x => x.displayName.Contains(nameOrID));
return target;
}
[ChatCommand("removevip")]
void RemoveVIP(BasePlayer player, string command, string[] args) {
if (!permission.UserHasPermission(player.UserIDString, "viptoken.admin")) return;
if (args.Length != 2 || !VIPTiers.Contains(args[1].ToLower())) {
PrintToChat(player, $"Usage: /removevip <player name/ID> <{string.Join(", ", VIPTiers)}>");
return;
}
var tier = args[1].ToLower();
var id = 0ul;
if (args[0].IsSteamId()) {
id = Convert.ToUInt64(args[0]);
}
else {
var target = FindPlayerByName(args[0], player);
if (target == null) return;
id = target.userID;
}
if (!pcdData.pentiy.ContainsKey(id)) {
PrintToChat(player, "Player not found in the token data base.");
return;
}
var playerData = pcdData.pentiy[id];
if (playerData.vip_levels.ContainsKey(tier)) {
playerData.vip_levels.Remove(tier);
SaveData();
PrintToChat(player, $"Removed {tier} from {id}");
permission.RemoveUserGroup(id.ToString(), tier);
return;
}
else PrintToChat(player, $"ID: {id} does not have vip tier: {tier}");
}
[ConsoleCommand("removevip")]
void RemoveVIPConsole(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player != null && !permission.UserHasPermission(player.UserIDString, "viptoken.admin")) return;
if (arg.Args.Length != 2 || !VIPTiers.Contains(arg.Args[1].ToLower())) {
arg.ReplyWith($"Usage: /removevip <player name/ID> <{string.Join(", ", VIPTiers)}>");
return;
}
var tier = arg.Args[1].ToLower();
if (!ulong.TryParse(arg.Args[0], out var id)) {
var target = FindPlayer(arg.Args[0]);
if (target == null) {
arg.ReplyWith($"Found no player matching: {arg.Args[0]}");
return;
}
id = target.userID.Get();
}
if (!pcdData.pentiy.ContainsKey(id)) {
arg.ReplyWith("Player not found in the token data base.");
return;
}
var playerData = pcdData.pentiy[id];
if (playerData.vip_levels.ContainsKey(tier)) {
playerData.vip_levels.Remove(tier);
SaveData();
arg.ReplyWith($"Removed {tier} from {id}");
permission.RemoveUserGroup(id.ToString(), tier);
return;
}
else arg.ReplyWith($"ID: {id} does not have vip tier: {tier}");
}
#endregion
#region Console
private void AddToken(ConsoleSystem.Arg arg) {
if (arg == null) {
Puts("arg was null");
return;
}
if (arg.Args == null) {
Puts("arg.Args was null");
return;
}
if (arg.Args.Length < 3) {
Puts($"arg.Args length was < 3 [length = {arg.Args.Length}]");
return;
}
if (arg.Args[0] != config.verification_password) {
Puts($"Password mismatch: Given: {arg.Args[0]}.Real PW: {config.verification_password}");
return;
}
if (!arg.Args[1].IsSteamId()) {
Puts($"{arg.Args[1]} is not a valid steam ID.");
return;
}
var id = Convert.ToUInt64(arg.Args[1]);
var tier = arg.Args[2];
if (!config.vip_levels.ContainsKey(tier)) {
Puts($"Token type {tier} does not exist in the config.");
return;
}
var amount = 1;
if (arg.Args.Length == 4) {
amount = Math.Max(1, Convert.ToInt32(arg.Args[3]));
}
LogToFile("VIPTokens", $"[{DateTime.Now}] {id} purchased VIP Tier: {tier}.", this, true);
AddRedemption(id, tier, amount);
}
private void AddToken(ulong id, string tier) {
if (!id.IsSteamId()) {
Puts($"{id} is not a valid steam ID");
return;
}
if (!config.vip_levels.ContainsKey(tier)) {
Puts($"{tier} is not a valid token tier.");
return;
}
AddRedemption(id, tier);
}
#endregion
#region Helper
public List<string> VIPTiers = new List<string>();
private BasePlayer FindPlayerByName(string Playername, BasePlayer SearchingPlayer = null) {
var targetList = BasePlayer.allPlayerList.Where(x => x.displayName.ToLower().Contains(Playername.ToLower())).OrderBy(x => x.displayName.Length);
if (targetList.Count() == 1) return targetList.First();
if (targetList.Count() > 1) {
if (targetList.First().displayName.ToLower() == Playername.ToLower()) return targetList.First();
if (SearchingPlayer != null) PrintToChat(SearchingPlayer, $"More than one player found: {String.Join(", ", targetList.Select(x => x.displayName))}");
return null;
}
if (targetList.Count() == 0) {
if (SearchingPlayer != null) PrintToChat(SearchingPlayer, $"No player was found that matched: {Playername}");
return null;
}
return null;
}
BasePlayer FindOnlinePlayerByID(ulong id) {
foreach (var player in BasePlayer.activePlayerList) {
if (player.userID == id) return player;
}
return null;
}
string CommandBuilder(string userID, string name, string line) {
if (string.IsNullOrEmpty(line)) return line;
return line.Replace("{id}", userID).Replace("{name}", name);
}
#endregion
#region API
object STOnRationTrigger(BasePlayer player, Item item) {
if (!TokenSkins.Contains(item.skin)) return null;
return true;
}
#endregion
#region Menu
const string back_panel_offset_min_1 = "-160 100";
const string back_panel_offset_min_2 = "-160 30";
const string back_panel_offset_min_3 = "-160 -40";
const string back_panel_offset_min_4 = "-160 -110";
const string back_panel_offset_min_5 = "-160 -180";
class MenuCooldown {
public Timer Cooldown;
}
Dictionary<ulong, MenuCooldown> menu_cooldown = new Dictionary<ulong, MenuCooldown>();
void SendTokenMenu(BasePlayer player) {
CuiHelper.DestroyUi(player, "VIP_Tokeninfo");
if (config.vip_levels.Count == 0) return;
var sendNext = false;
List<string> vip_tiers = new List<string>();
foreach (KeyValuePair<string, Configuration.VIPInfo> kvp in config.vip_levels) {
if (vip_tiers.Count == 5) {
sendNext = true;
break;
}
vip_tiers.Add(kvp.Key);
}
VIP_Tokens(player, vip_tiers, sendNext);
}
string GetTokenBalance(BasePlayer player, string tier) {
if (!pcdData.pentiy.ContainsKey(player.userID)) return "0";
var playerData = pcdData.pentiy[player.userID];
if (!playerData.Redemptions.ContainsKey(tier)) return "0";
return playerData.Redemptions[tier].ToString();
}
string GetEndDate(BasePlayer player, string tier) {
if (!pcdData.pentiy.ContainsKey(player.userID)) return "NA";
var playerData = pcdData.pentiy[player.userID];
if (!playerData.vip_levels.ContainsKey(tier)) return "NA";
if (playerData.vip_levels[tier].end_date.Date == DateTime.Now.Date) {
var enddate = playerData.vip_levels[tier].end_date;
return $"{enddate.Hour}:{enddate.Minute}:{enddate.Second}";
}
return playerData.vip_levels[tier].end_date.ToString($"{config.date_format}");
}
bool WithDrawToken(BasePlayer player, string tier) {
if (!pcdData.pentiy.ContainsKey(player.userID)) {
PrintToChat(player, "You do not have any tokens.");
return false;
}
if (menu_cooldown.ContainsKey(player.userID) && menu_cooldown[player.userID].Cooldown != null) {
PrintToChat(player, "Please wait a few seconds before withdrawing more tokens.");
return false;
}
var playerData = pcdData.pentiy[player.userID];
if (!playerData.Redemptions.ContainsKey(tier) || playerData.Redemptions[tier] == 0) {
PrintToChat(player, $"You do not have any {config.vip_levels[tier].name} tokens.");
return false;
}
if (player.inventory.containerBelt.IsFull() && player.inventory.containerMain.IsFull()) {
PrintToChat(player, $"{prefix} You need 1 slot available in your inventory in order to redeem a token.");
return false;
}
if (!menu_cooldown.ContainsKey(player.userID)) menu_cooldown.Add(player.userID, new MenuCooldown());
var MenuData = menu_cooldown[player.userID];
MenuData.Cooldown = timer.Once(2f, () => {
if (!MenuData.Cooldown.Destroyed) MenuData.Cooldown.Destroy();
menu_cooldown.Remove(player.userID);
}
);
playerData.Redemptions[tier]--;
SaveData();
CreateToken(player, tier);
return true;
}
List<Item> AllItems(BasePlayer player) {
List<Item> result = Pool.Get<List<Item>>();
if (player.inventory.containerMain?.itemList != null) result.AddRange(player.inventory.containerMain.itemList);
if (player.inventory.containerBelt?.itemList != null) result.AddRange(player.inventory.containerBelt.itemList);
if (player.inventory.containerWear?.itemList != null) result.AddRange(player.inventory.containerWear.itemList);
return result;
}
bool DepositToken(BasePlayer player, string tier) {
if (menu_cooldown.ContainsKey(player.userID) && menu_cooldown[player.userID].Cooldown != null) {
PrintToChat(player, "Please wait a few seconds before depositing more tokens.");
return false;
}
var items = AllItems(player);
try {
if (items.Count == 0) {
PrintToChat(player, "You do not have any tokens in your inventory.");
return false;
}
}
finally {
Pool.FreeUnmanaged(ref items);
}
var skin = config.vip_levels[tier].token_item.skin;
foreach (var item in items) {
if (skin == item.skin) {
if (!menu_cooldown.ContainsKey(player.userID)) menu_cooldown.Add(player.userID, new MenuCooldown());
var MenuData = menu_cooldown[player.userID];
MenuData.Cooldown = timer.Once(2f, () => {
if (!MenuData.Cooldown.Destroyed) MenuData.Cooldown.Destroy();
menu_cooldown.Remove(player.userID);
}
);
if (!pcdData.pentiy.ContainsKey(player.userID)) pcdData.pentiy.Add(player.userID, new PCDInfo());
var playerData = pcdData.pentiy[player.userID];
if (!playerData.Redemptions.ContainsKey(tier)) playerData.Redemptions.Add(tier, item.amount);
else playerData.Redemptions[tier] += item.amount;
SaveData();
PrintToChat(player, $"Stored {item.amount}x token{(item.amount > 1 ? "s" : "")} into your wallet.");
item.Remove();
return true;
}
}
PrintToChat(player, "Token not found.");
return false;
}
[ConsoleCommand("withdrawviptoken")]
private void withdrawviptoken(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
if (arg.Args.Length == 0) return;
var tier = arg.Args[0];
if (WithDrawToken(player, tier)) SendTokenMenu(player);
}
[ConsoleCommand("depositviptoken")]
private void depositviptoken(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
if (arg.Args.Length == 0) return;
var tier = arg.Args[0];
if (DepositToken(player, tier)) SendTokenMenu(player);
}
[ConsoleCommand("vipclosemenu")]
private void vipclosemenu(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
CuiHelper.DestroyUi(player, "VIP_Tokens");
}
[ConsoleCommand("vipnextmenu")]
private void vipnextmenu(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
if (arg.Args.Length != 1) return;
var lastTier = arg.Args[0];
CuiHelper.DestroyUi(player, "VIP_Tokeninfo");
var sendNext = false;
List<string> vip_tiers = new List<string>();
var startAdding = false;
foreach (KeyValuePair<string, Configuration.VIPInfo> kvp in config.vip_levels) {
if (vip_tiers.Count == 5) {
sendNext = true;
break;
}
if (!startAdding) {
if (!lastTier.Equals(kvp.Key)) continue;
startAdding = true;
continue;
}
vip_tiers.Add(kvp.Key);
}
if (vip_tiers.Count == 0) return;
VIP_Tokens(player, vip_tiers, sendNext, true);
}
[ConsoleCommand("vipbackmenu")]
private void vipbackmenu(ConsoleSystem.Arg arg) {
var player = arg.Player();
if (player == null) return;
if (arg.Args.Length != 1) return;
CuiHelper.DestroyUi(player, "VIP_Tokeninfo");
var firstTier = arg.Args[0];
var sendNext = false;
var sendBack = false;
List<string> vip_tiers = new List<string>();
var firstKey = "";
foreach (KeyValuePair<string, Configuration.VIPInfo> kvp in config.vip_levels) {
if (string.IsNullOrEmpty(firstKey)) firstKey = kvp.Key;
if (vip_tiers.Count == 5) {
sendNext = true;
break;
}
if (kvp.Key.Equals(firstTier)) break;
vip_tiers.Add(kvp.Key);
}
if (vip_tiers.Count == 0) return;
if (!vip_tiers.Contains(firstKey)) sendBack = true;
VIP_Tokens(player, vip_tiers, sendNext, sendBack);
}
void VIP_Tokens(BasePlayer player, List<string> vip_tiers, bool sendnext = false, bool sendback = false) {
var BackPanelOffset = "";
if (vip_tiers.Count == 1) BackPanelOffset = back_panel_offset_min_1;
if (vip_tiers.Count == 2) BackPanelOffset = back_panel_offset_min_2;
if (vip_tiers.Count == 3) BackPanelOffset = back_panel_offset_min_3;
if (vip_tiers.Count == 4) BackPanelOffset = back_panel_offset_min_4;
if (vip_tiers.Count == 5) BackPanelOffset = back_panel_offset_min_5;
if (string.IsNullOrEmpty(BackPanelOffset)) return;
var configInstance = config.vip_levels;
var container = new CuiElementContainer();
container.Add(new CuiPanel {
CursorEnabled = true, Image = {
Color = "0 0 0 0.8823529"
}
, RectTransform = {
AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "-0.351 -0.328", OffsetMax = "0.349 0.332"
}
}
, "Overlay", "VIP_Tokens");
container.Add(new CuiPanel {
CursorEnabled = false, Image = {
Color = "0.245283 0 0 1"
}
, RectTransform = {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = BackPanelOffset, OffsetMax = "160 180"
}
}
, "VIP_Tokens", "vip_back_panel");
container.Add(new CuiPanel {
CursorEnabled = false, Image = {
Color = "0.3962264 0 0 1"
}
, RectTransform = {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-150 110", OffsetMax = "150 170"
}
}
, "VIP_Tokens", "vip_info_panel_1");
container.Add(new CuiElement {
Name = "vip_info_header_1", Parent = "vip_info_panel_1", Components = {
new CuiTextComponent {
Text = configInstance[vip_tiers[0]].name.ToUpper(), Font = "robotocondensed-regular.ttf", FontSize = 18, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1"
}
, new CuiOutlineComponent {
Color = "0 0 0 0.5", Distance = "1 -1"
}
, new CuiRectTransformComponent {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-150 0", OffsetMax = "150 30"
}
}
}
);
container.Add(new CuiElement {
Name = "vip_balance_1", Parent = "vip_info_panel_1", Components = {
new CuiTextComponent {
Text = $"Token Balance: {GetTokenBalance(player, vip_tiers[0])}", Font = "robotocondensed-regular.ttf", FontSize = 14, Align = TextAnchor.MiddleLeft, Color = "1 1 1 1"
}
, new CuiOutlineComponent {
Color = "0 0 0 0.5", Distance = "1 -1"
}
, new CuiRectTransformComponent {
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-140 -30", OffsetMax = "0 0"
}
}
}
);
container.Add(new CuiElement {
Name = "vip_end_da
// ... [Code preview truncated due to length. Full source in attached file] ...