using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("MagicTree", "https://topplugin.ru / https://discord.com/invite/5DPTsRmd3G", "1.3.1")]
public class MagicTree : RustPlugin
{
#region Configuration
public class Seed
{
public string shortname;
public string name;
public ulong skinId;
}
public class Wood
{
[JsonProperty("UID Дерева")]
public uint woodId;
[JsonProperty("Осталось времени")]
public int NeedTime;
[JsonProperty("Оставшееся время до разрушения")]
public int NeedTimeToDestroy = -1;
[JsonProperty("Этап")]
public int CurrentStage;
[JsonProperty("Позиция")]
public Vector3 woodPos;
[JsonProperty("Ящики")]
public List<uint> BoxListed = new List<uint>();
[JsonIgnore] public List<BaseEntity> boxes = new List<BaseEntity>();
}
public class BoxItemsList
{
[JsonProperty("Shortname предмета")]
public string ShortName;
[JsonProperty("Минимальное количество")]
public int MinAmount;
[JsonProperty("Максимальное количество")]
public int MaxAmount;
[JsonProperty("Шанс что предмет будет добавлен (максимально 100%)")]
public int Change;
[JsonProperty("SkinID предмета")]
public ulong SkinID;
[JsonProperty("Имя предмета при создании (Оставьте поле пустым чтобы использовать стандартное название итема)")]
public string Name;
[JsonProperty("Это чертеж")]
public bool IsBlueprnt;
}
public Dictionary<ulong, Dictionary<uint, Wood>> WoodsList = new Dictionary<ulong, Dictionary<uint, Wood>>();
public Dictionary<string, string> Messages = new Dictionary<string, string>()
{
{"CmdError", "Неправильно ввели команду." },
{"DisablePlantSeed", "Семена разрешено садить только в землю" },
{"CountError", "Неверное кол-во!" },
{"Permission", "У вас нет прав!" },
{"SeedGived", "Вам выпала семечка магического дерева!\nПосадите ее и у вас выростет необычное дерево на каком растут ящики с ценными предметами!" },
{"Wood", "Вы посадили магическое дерево\nСкоро оно вырастет, и даст плоды!" },
{"InfoTextFull", "<size=25><b>Магическое дерево</b></size>\n<size=17>\nПЛОДЫ ДОЗРЕЛИ, ВЫ МОЖЕТЕ ИХ СОБРАТЬ</size>"},
{"InfoDdraw", "<size=25><b>Магическое дерево</b></size>\n<size=17>Этап созревания дерева: {0}/{1}\n\nВремя до полного созревания: {2}</size>" }
};
private PluginConfig config;
protected override void LoadDefaultConfig()
{
PrintWarning("Благодарим за покупку плагина на сайте TopPlugin.ru. Если вы передадите этот плагин сторонним лицам знайте - это лишает вас гарантированных обновлений!");
config = PluginConfig.DefaultConfig();
}
protected override void LoadConfig()
{
base.LoadConfig();
config = Config.ReadObject<PluginConfig>();
if (config.PluginVersion < Version)
UpdateConfigValues();
Config.WriteObject(config, true);
}
private void UpdateConfigValues()
{
PluginConfig baseConfig = PluginConfig.DefaultConfig();
if (config.PluginVersion < Version)
{
PrintWarning("Config update detected! Updating config values...");
PrintWarning("Config update completed!");
}
config.PluginVersion = Version;
}
protected override void SaveConfig()
{
Config.WriteObject(config);
}
private class PluginConfig
{
[JsonProperty("Время роста дерева в секундах")]
public int Time;
[JsonProperty("Время существования дерева после полного созревания")]
public int TimetoDestroy = 3600;
[JsonProperty("Посадка деревьев разрешена только в земле (запрещены плантации и прочее)")]
public bool PlanterBoxDisable = true;
[JsonProperty("Множитель добычи при финальной срубке магического дерева")]
public int Bonus = 1;
[JsonProperty("Количество вещей в ящике")]
public int ItemsCount;
[JsonProperty("Кол-во ящиков на дереве")]
public int BoxCount;
[JsonProperty("Список префабов этапов дерева")]
public List<string> Stages;
[JsonProperty("Права на выдачу")]
public string Permission = "seed.perm";
[JsonProperty("Тип ящика")]
public string CrateBasic = "assets/bundled/prefabs/radtown/crate_basic.prefab";
[JsonProperty("Шанс выпадения зерна с дерева (макс-100)")]
public int Chance;
[JsonProperty("Настройка лута в ящиках")]
public List<BoxItemsList> casesItems;
[JsonProperty("Ссылка на удачный эффект")]
public string SucEffect;
[JsonProperty("Ссылка на эффект ошибки")]
public string ErrorEffect;
[JsonProperty("Настройка зерна")]
public Seed seed;
[JsonProperty("Версия конфигурации")]
public VersionNumber PluginVersion = new VersionNumber();
public static PluginConfig DefaultConfig()
{
return new PluginConfig()
{
PluginVersion = new VersionNumber(),
ItemsCount = 2,
Permission = "MagicTree.perm",
CrateBasic = "assets/bundled/prefabs/radtown/crate_underwater_basic.prefab",
BoxCount = 4,
Chance = 5,
Time = 10,
seed = new Seed()
{
shortname = "seed.hemp",
name = "Семена магического дерева",
skinId = 1787823357
},
casesItems = new List<BoxItemsList>()
{
new BoxItemsList
{
ShortName = "stones",
MinAmount = 300,
MaxAmount = 1000,
Change = 100,
Name = "",
SkinID = 0,
IsBlueprnt = false
},
},
SucEffect = "assets/prefabs/misc/xmas/candy cane club/effects/hit.prefab",
ErrorEffect = "assets/prefabs/locks/keypad/effects/lock.code.denied.prefab",
Stages = new List<string>()
{
"assets/prefabs/plants/hemp/hemp.entity.prefab",
"assets/bundled/prefabs/autospawn/resource/v3_temp_field/birch_tiny_temp.prefab",
"assets/bundled/prefabs/autospawn/resource/v3_temp_field/american_beech_e.prefab",
"assets/bundled/prefabs/autospawn/resource/v3_temp_field/oak_e.prefab",
"assets/bundled/prefabs/autospawn/resource/v3_temp_field_large/oak_b.prefab"
},
};
}
}
#endregion
#region Oxide
void LoadData()
{
try
{
WoodsList = Interface.GetMod().DataFileSystem.ReadObject<Dictionary<ulong, Dictionary<uint, Wood>>>($"MagiсTree_Players");
if (WoodsList == null)
WoodsList = new Dictionary<ulong, Dictionary<uint, Wood>>();
}
catch
{
WoodsList = new Dictionary<ulong, Dictionary<uint, Wood>>();
}
}
void SaveData()
{
if (WoodsList != null)
Interface.Oxide.DataFileSystem.WriteObject($"MagiсTree_Players", WoodsList);
}
public static MagicTree ins;
void OnEntityKill(TreeEntity entity)
{
if (entity == null || entity?.net.ID == null || entity.OwnerID == 0) return;
if (entity.GetComponent<TreeEntity>() != null && entity.GetComponent<TreeConponent>() != null)
{
var tree = entity.GetComponent<TreeEntity>();
if (WoodsList.ContainsKey(tree.OwnerID) && WoodsList[tree.OwnerID].ContainsKey(tree.net.ID))
WoodsList[tree.OwnerID].Remove(tree.net.ID);
}
}
void OnServerSave()
{
SaveData();
}
void Loaded()
{
ins = this;
permission.RegisterPermission(config.Permission, this);
lang.RegisterMessages(Messages, this, "en");
Messages = lang.GetMessages("en", this);
LoadData();
}
private void OnServerInitialized()
{
foreach (var tree in WoodsList)
{
foreach (var entity in tree.Value.Keys)
{
BaseNetworkable entitys = BaseNetworkable.serverEntities.Find(entity);
if (entitys != null && entitys is TreeEntity)
AddOrRemoveComponent("add", null, entitys.GetComponent<TreeEntity>(), entitys.GetComponent<TreeEntity>().OwnerID);
else if (entitys != null && entitys is GrowableEntity)
AddOrRemoveComponent("add", null, entitys.GetComponent<GrowableEntity>(), entitys.GetComponent<GrowableEntity>().OwnerID);
else
NextTick(() => { tree.Value.Remove(entity); });
}
}
}
private List<TreeConponent> treeConponents = new List<TreeConponent>();
void AddOrRemoveComponent(string type = "", TreeConponent component = null, BaseEntity tree = null, ulong playerid = 4815872)
{
if (!WoodsList.ContainsKey(playerid)) return;
switch (type)
{
case "add":
if (!WoodsList[playerid].ContainsKey(tree.net.ID)) return;
var data = WoodsList[playerid][tree.net.ID];
if (tree != null && data != null)
{
if (WoodsList[playerid][tree.net.ID].CurrentStage > 2 && WoodsList[playerid][tree.net.ID].BoxListed.Count > 0)
{
GameObject treeObject = new GameObject();
treeObject.transform.position = tree.transform.position;
treeObject.AddComponent<TreeConponent>().Init(WoodsList[playerid][tree.net.ID], tree);
treeConponents.Add(treeObject.GetComponent<TreeConponent>());
SpawnBox(data, WoodsList[playerid][tree.net.ID].BoxListed.Count, tree, playerid, WoodsList[playerid][tree.net.ID].BoxListed.Count);
return;
}
else
{
GameObject treeObject = new GameObject();
treeObject.transform.position = tree.transform.position;
treeObject.AddComponent<TreeConponent>().Init(WoodsList[playerid][tree.net.ID], tree);
treeConponents.Add(treeObject.GetComponent<TreeConponent>());
}
}
break;
case "remove":
if (component == null || component.IsDestroyed) return;
if (WoodsList[playerid][tree.net.ID].BoxListed.Count > 0)
{
foreach (var ent in WoodsList[playerid][tree.net.ID].boxes)
{
if (ent != null && !ent.IsDestroyed)
ent.Kill();
}
WoodsList[playerid][tree.net.ID].boxes.Clear();
if (component != null)
component.DestroyComponent();
}
else
{
if (component != null)
component.DestroyComponent();
}
break;
}
}
void OnEntityBuilt(Planner planner, GameObject gameobject, Vector3 Pos)
{
if (planner == null || gameobject == null) return;
var player = planner.GetOwnerPlayer();
if (player == null) return;
BaseEntity entity = gameobject.ToBaseEntity();
if (entity == null) return;
if (entity.skinID == config.seed.skinId)
{
NextTick(() =>
{
if (entity != null && !entity.IsDestroyed)
{
if (config.PlanterBoxDisable && entity.GetParentEntity() != null)
{
if (player == null) return;
SendReply(player, Messages["DisablePlantSeed"]);
AddSeed(player, 1, false);
NextTick(() => entity.Kill());
return;
}
SpawnWood(player.userID, entity.transform.position, null, entity, null);
SendReply(player, string.Format(Messages["Wood"]));
}
});
}
}
object OnDispenserBonus(ResourceDispenser dispenser, BasePlayer player, Item item)
{
if (dispenser == null || player == null || item == null) return null;
switch (item.info.shortname)
{
case "wood":
if (UnityEngine.Random.Range(0f, 100f) < config.Chance)
{
var activeitem = player.GetActiveItem();
if (activeitem != null && !activeitem.info.shortname.Contains("chainsaw"))
AddSeed(player, 1);
}
if (config.Bonus > 1)
{
TreeEntity wood1 = dispenser.GetComponentInParent<TreeEntity>();
if (wood1 == null) return null;
if (!treeConponents.Any(p => p.tree == wood1)) return null;
var treeComponent = treeConponents.Find(p => p.tree == wood1);
if (wood1 != null && treeComponent != null)
item.amount = item.amount * config.Bonus;
}
break;
}
return null;
}
object OnDispenserGather(ResourceDispenser dispenser, BaseEntity entity, Item item)
{
if (dispenser == null || item == null) return null;
BasePlayer player = entity?.ToPlayer();
if (player == null) return null;
TreeEntity wood1 = dispenser.GetComponentInParent<TreeEntity>();
if (wood1 == null || wood1.OwnerID == 0 || !treeConponents.Any(p => p.tree == wood1)) return null;
switch (item.info.shortname)
{
case "wood":
var treeComponent = treeConponents.Find(p => p.tree == wood1);
if (wood1 != null && treeComponent != null)
{
var component = treeComponent;
if (component.data.boxes.Count > 0 && component.data.boxes.Count < 5)
{
var box = component.data.boxes.Last();
if (box != null && component.data.CurrentStage == config.Stages.Count)
{
box.SetFlag(BaseEntity.Flags.Busy, false, true);
box.gameObject.AddComponent<RigidbodyChecker>();
component.data.BoxListed.Remove(box.net.ID);
component.data.boxes.Remove(box);
}
return false;
}
else if (component.data.BoxListed.Count > 5 && component.data.CurrentStage == config.Stages.Count)
{
foreach (var box in component.data.boxes)
{
if (box != null)
{
box.SetFlag(BaseEntity.Flags.Busy, false, true);
box.gameObject.AddComponent<RigidbodyChecker>();
component.data.BoxListed.Remove(box.net.ID);
component.data.boxes.Remove(box);
}
}
component.data.BoxListed.Clear();
component.data.boxes.Clear();
return false;
}
else
{
if (component.data.CurrentStage == config.Stages.Count)
{
dispenser.AssignFinishBonus(player, 1);
HitInfo hitInfo = new HitInfo(player, wood1, Rust.DamageType.Generic, wood1.Health(), wood1.transform.position);
wood1.OnAttacked(hitInfo);
return false;
}
}
}
break;
}
return null;
}
public class RigidbodyChecker : MonoBehaviour
{
BaseEntity check;
public void Awake()
{
check = GetComponent<BaseEntity>();
InvokeRepeating(nameof(Untie), 0f, 2f);
}
public void Untie()
{
check.SetFlag(BaseEntity.Flags.Reserved8, false, false);
var body = check.GetComponent<Rigidbody>();
if (body != null)
{
body.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
if (!IsOnGround(check.transform.position) && body.detectCollisions)
{
body.isKinematic = true;
}
else
{
Destroy(this);
return;
}
body.isKinematic = false;
body.mass = 10f;
}
}
bool IsOnGround(Vector3 pos)
{
float y = TerrainMeta.HeightMap.GetHeight(pos);
if ((pos.y - y) < 0.1f)
return true;
return false;
}
}
void Unload()
{
foreach (var tree in treeConponents)
AddOrRemoveComponent("remove", tree, tree.tree, tree.tree.OwnerID);
SaveData();
}
#endregion
#region MyMethods
public void SpawnWood(ulong player, Vector3 pos, BaseEntity tree, BaseEntity seed, TreeConponent oldComponent)
{
if (tree == null)
{
if (!WoodsList.ContainsKey(player))
WoodsList.Add(player, new Dictionary<uint, Wood>()
{
[seed.net.ID] = new Wood() { woodId = seed.net.ID, CurrentStage = 0, NeedTime = config.Time / config.Stages.Count, woodPos = seed.transform.position }
});
else
WoodsList[player].Add(seed.net.ID, new Wood() { woodId = seed.net.ID, CurrentStage = 0, NeedTime = config.Time / config.Stages.Count, woodPos = seed.transform.position });
GameObject treeObject = new GameObject();
treeObject.transform.position = seed.transform.position;
treeObject.AddComponent<TreeConponent>().Init(WoodsList[player][seed.net.ID], seed);
treeConponents.Add(treeObject.GetComponent<TreeConponent>());
}
else
{
if (tree == null) return;
var old = WoodsList[player][tree.net.ID];
var current = ++old.CurrentStage;
TreeEntity Wood = GameManager.server.CreateEntity(config.Stages[current], pos) as TreeEntity;
Wood.Spawn();
Wood.GetComponent<TreeEntity>().OwnerID = player;
if (WoodsList[player].ContainsKey(tree.net.ID)) WoodsList[player].Remove(tree.net.ID);
WoodsList[player].Add(Wood.net.ID, new Wood() { woodId = Wood.net.ID, CurrentStage = current, NeedTime = config.Time / config.Stages.Count, woodPos = Wood.transform.position });
GameObject treeObject = new GameObject();
treeObject.transform.position = tree.transform.position;
treeObject.AddComponent<TreeConponent>().Init(WoodsList[player][Wood.net.ID], Wood);
Wood.SendNetworkUpdateImmediate();
treeConponents.Add(treeObject.GetComponent<TreeConponent>());
tree.KillMessage();
if (oldComponent != null && treeConponents.Contains(oldComponent))
treeConponents.Remove(oldComponent);
}
}