C#:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Core.Libraries.Covalence;
using Oxide.Game.Rust.Cui;
using UnityEngine;
using Newtonsoft.Json;
using Rust;
using Newtonsoft.Json.Converters;
namespace Oxide.Plugins {
[Info("ZLevels Remastered", "nivex", "3.3.1")]
[Description("Lets players level up as they harvest resources, fish, and craft")]
class ZLevelsRemastered : RustPlugin {
[PluginReference]
Plugin EventManager, CraftingController, ZoneManager, Economics, IQEconomic, ServerRewards, SkillTree;
private StoredData data = new();
private Dictionary<string, ItemDefinition> CraftItems;
private CraftData _craftData = new();
private StringBuilder _sb = new();
private bool newSaveDetected;
private bool bonusOn;
private int MaxB = 10001;
private int MinB = 10;
private HashSet<ulong> uiMovePlayers = new();
public enum Skills {
ACQUIRE, CRAFTING, MINING, SKINNING, WOODCUTTING, FISHING, ALL
}
[JsonConverter(typeof(StringEnumConverter))]
public enum ClientPanels {
None, Overall, Overlay, OverlayNonScaled, HudMenu, Hud, Contacts, Clans, Crafting, Inventory, Map, TechTree, Under, UnderNonScaled
}
private Skills[] AllSkills = new Skills[] {
Skills.ACQUIRE, Skills.CRAFTING, Skills.MINING, Skills.SKINNING, Skills.WOODCUTTING, Skills.FISHING
}
;
private class CraftData {
public Dictionary<string, CraftInfo> CraftList = new();
public CraftData() {
}
}
private class CraftInfo {
public int MaxBulkCraft;
public int MinBulkCraft;
public string shortName;
public bool Enabled;
public CraftInfo() {
}
}
private class StoredData {
public Hash<ulong, PlayerInfo> PlayerInfo = new();
[JsonProperty(ItemConverterType = typeof(Vector2Converter))]
public Dictionary<ulong, Vector2> UiPositions = new();
public StoredData() {
}
}
private class PlayerInfo : IEquatable<PlayerInfo> {
private double _acquireLevel = 1.0;
private double _acquirePoints = 10.0;
private double _craftingLevel = 1.0;
private double _craftingPoints = 10.0;
private double _miningLevel = 1.0;
private double _miningPoints = 10.0;
private double _skinningLevel = 1.0;
private double _skinningPoints = 10.0;
private double _woodcuttingLevel = 1.0;
private double _woodcuttingPoints = 10.0;
private double _fishingLevel = 1.0;
private double _fishingPoints = 10.0;
private double _lastDeath = 0.0;
private double _xpMultiplier = 100.0;
private double RoundValue(double value) => Math.Round(value, 2, MidpointRounding.AwayFromZero);
[JsonProperty(PropertyName = "AL")]
public double ACQUIRE_LEVEL {
get => RoundValue(_acquireLevel);
set => _acquireLevel = RoundValue(value);
}
[JsonProperty(PropertyName = "AP")]
public double ACQUIRE_POINTS {
get => RoundValue(_acquirePoints);
set => _acquirePoints = RoundValue(value);
}
[JsonProperty(PropertyName = "CL")]
public double CRAFTING_LEVEL {
get => RoundValue(_craftingLevel);
set => _craftingLevel = RoundValue(value);
}
[JsonProperty(PropertyName = "CP")]
public double CRAFTING_POINTS {
get => RoundValue(_craftingPoints);
set => _craftingPoints = RoundValue(value);
}
[JsonProperty(PropertyName = "ML")]
public double MINING_LEVEL {
get => RoundValue(_miningLevel);
set => _miningLevel = RoundValue(value);
}
[JsonProperty(PropertyName = "MP")]
public double MINING_POINTS {
get => RoundValue(_miningPoints);
set => _miningPoints = RoundValue(value);
}
[JsonProperty(PropertyName = "SL")]
public double SKINNING_LEVEL {
get => RoundValue(_skinningLevel);
set => _skinningLevel = RoundValue(value);
}
[JsonProperty(PropertyName = "SP")]
public double SKINNING_POINTS {
get => RoundValue(_skinningPoints);
set => _skinningPoints = RoundValue(value);
}
[JsonProperty(PropertyName = "WCL")]
public double WOODCUTTING_LEVEL {
get => RoundValue(_woodcuttingLevel);
set => _woodcuttingLevel = RoundValue(value);
}
[JsonProperty(PropertyName = "WCP")]
public double WOODCUTTING_POINTS {
get => RoundValue(_woodcuttingPoints);
set => _woodcuttingPoints = RoundValue(value);
}
[JsonProperty(PropertyName = "FL")]
public double FISHING_LEVEL {
get => RoundValue(_fishingLevel);
set => _fishingLevel = RoundValue(value);
}
[JsonProperty(PropertyName = "FP")]
public double FISHING_POINTS {
get => RoundValue(_fishingPoints);
set => _fishingPoints = RoundValue(value);
}
[JsonProperty(PropertyName = "LD")]
public double LAST_DEATH {
get => RoundValue(_lastDeath);
set => _lastDeath = RoundValue(value);
}
[JsonProperty(PropertyName = "XPM")]
public double XP_MULTIPLIER {
get => RoundValue(_xpMultiplier);
set => _xpMultiplier = RoundValue(value);
}
[JsonProperty(PropertyName = "CUI")]
public bool CUI = true;
[JsonProperty(PropertyName = "CUIMOVE")]
public bool CUIMOVE;
[JsonProperty(PropertyName = "ONOFF")]
public bool ENABLED = true;
public PlayerInfo() {
}
public bool Equals(PlayerInfo other) {
if (other == null) return false;
return ACQUIRE_LEVEL == other.ACQUIRE_LEVEL && ACQUIRE_POINTS == other.ACQUIRE_POINTS && CRAFTING_LEVEL == other.CRAFTING_LEVEL && CRAFTING_POINTS == other.CRAFTING_POINTS && MINING_LEVEL == other.MINING_LEVEL && MINING_POINTS == other.MINING_POINTS && SKINNING_LEVEL == other.SKINNING_LEVEL && SKINNING_POINTS == other.SKINNING_POINTS && WOODCUTTING_LEVEL == other.WOODCUTTING_LEVEL && WOODCUTTING_POINTS == other.WOODCUTTING_POINTS && FISHING_LEVEL == other.FISHING_LEVEL && FISHING_POINTS == other.FISHING_POINTS && XP_MULTIPLIER == other.XP_MULTIPLIER && CUI == other.CUI && CUIMOVE == other.CUIMOVE && ENABLED == other.ENABLED;
}
public override bool Equals(object obj) {
return obj is PlayerInfo other && Equals(other);
}
public override int GetHashCode() {
HashCode hash = new();
hash.Add(ACQUIRE_LEVEL);
hash.Add(ACQUIRE_POINTS);
hash.Add(CRAFTING_LEVEL);
hash.Add(CRAFTING_POINTS);
hash.Add(MINING_LEVEL);
hash.Add(MINING_POINTS);
hash.Add(SKINNING_LEVEL);
hash.Add(SKINNING_POINTS);
hash.Add(WOODCUTTING_LEVEL);
hash.Add(WOODCUTTING_POINTS);
hash.Add(FISHING_LEVEL);
hash.Add(FISHING_POINTS);
hash.Add(XP_MULTIPLIER);
hash.Add(CUI);
hash.Add(CUIMOVE);
hash.Add(ENABLED);
return hash.ToHashCode();
}
public static bool operator ==(PlayerInfo left, PlayerInfo right) {
if (ReferenceEquals(left, right)) return true;
if (left is null) return false;
return left.Equals(right);
}
public static bool operator !=(PlayerInfo left, PlayerInfo right) {
return !(left == right);
}
internal bool IsDefault(PlayerInfo other) => Equals(other);
}
#region Main
private void Init() {
Unsubscribe();
RegisterPermissions();
int index = 0;
foreach (Skills skill in AllSkills) {
if (IsSkillEnabled(skill)) {
skillIndex.Add(skill, ++index);
}
}
}
private void Unload() {
Clean();
SaveData();
foreach (var player in BasePlayer.activePlayerList) {
DestroyGUI(player);
}
if (TOD_Sky.Instance?.Components?.Time != null) {
TOD_Sky.Instance.Components.Time.OnSunrise -= OnTimeSunrise;
TOD_Sky.Instance.Components.Time.OnSunset -= OnTimeSunset;
}
}
protected new void Puts(string format, params object[] args) {
if (string.IsNullOrWhiteSpace(format)) return;
string message = Localize(format, null, args);
if (string.IsNullOrWhiteSpace(message)) return;
Interface.Oxide.LogInfo("[{0}] {1}", nameof(ZLevelsRemastered), message);
}
private void OnNewSave(string strFilename) {
if (config.generic.wipeDataOnNewSave) {
Puts("New save detected...");
newSaveDetected = true;
}
}
private void OnServerInitialized() {
LoadData();
if (config.nightbonus.enableNightBonus && TOD_Sky.Instance.IsNight) {
pointsPerHitCurrent = config.nightbonus.pointsPerHitAtNight;
resourceMultipliersCurrent = config.nightbonus.resourceMultipliersAtNight;
pointsPerHitPowerToolCurrent = config.nightbonus.pointsPerPowerToolAtNight;
bonusOn = true;
}
else {
pointsPerHitCurrent = config.settings.pointsPerHit;
resourceMultipliersCurrent = config.settings.resourceMultipliers;
pointsPerHitPowerToolCurrent = config.settings.pointsPerPowerTool;
}
foreach (var player in BasePlayer.allPlayerList) {
var pi = GetPlayerInfo(player);
if (pi != null && player.IsConnected) CreateGUI(player, pi);
}
Puts("Stats can be reset by > zl.reset <");
Subscribe();
SaveData();
RegisterCommands();
}
private void OnPlayerConnected(BasePlayer player) => OnPlayerSleepEnded(player);
private void OnPlayerDisconnected(BasePlayer player) {
if (!ReferenceEquals(player, null)) uiMovePlayers.Remove(player.userID);
if (player != null) GetPlayerInfo(player);
}
private void OnEntityKill(BasePlayer player) => OnPlayerSleep(player);
private void OnPlayerRespawned(BasePlayer player) => OnPlayerSleepEnded(player);
private void OnPlayerSleepEnded(BasePlayer player) => CreateGUI(player, GetPlayerInfo(player));
private void OnPlayerSleep(BasePlayer player) => DestroyGUI(player);
private void OnEntityDeath(BasePlayer player, HitInfo info) {
if (info == null || !IsValid(player) || isZoneExcluded(player)) return;
DestroyGUI(player);
if (!hasRights(player.UserIDString)) return;
var pi = GetPlayerInfo(player);
if (!pi.ENABLED || permission.UserHasPermission(player.UserIDString, config.generic.permissionNameXP)) return;
string L(string key, params object[] args) => Localize(key, player.UserIDString, args);
bool isSuicide = info.damageTypes.Has(DamageType.Suicide) && (player.IsAdmin || player.IsDeveloper || !player.CanSuicide());
if (!config.generic.penaltyOnSuicide && isSuicide) return;
if (!config.generic.penaltyOnDeath && !isSuicide) return;
if (EventManager?.Call("IsEventPlayer", player) != null) return;
if (Interface.CallHook("CanBePenalized", player) != null) return;
_sb.Clear();
foreach (Skills skill in AllSkills) {
if (IsSkillEnabled(skill)) {
var penalty = GetPenalty(player, pi, skill, isSuicide);
if (penalty > 0) {
_sb.AppendLine(L("* -{0} {1} XP.", penalty, L(skill + "Skill")));
removePoints(pi, skill, penalty);
}
}
}
if (_sb.Length > 0) {
Message(player, "PenaltyText", _sb.ToString());
}
pi.LAST_DEATH = ToEpochTime(DateTime.UtcNow);
}
private void OnDispenserGather(ResourceDispenser dispenser, BasePlayer player, Item item) {
if (item == null || dispenser == null || !hasRights(player)) return;
Skills? skill = dispenser.gatherType switch {
ResourceDispenser.GatherType.Tree => Skills.WOODCUTTING, ResourceDispenser.GatherType.Flesh => Skills.SKINNING, ResourceDispenser.GatherType.Ore => Skills.MINING, _ => null
}
;
if (skill == null || !IsSkillEnabled(skill.Value)) return;
var pi = GetPlayerInfo(player);
if (!pi.ENABLED) return;
Item activeItem = player.GetActiveItem();
if (permission.UserHasPermission(player.UserIDString, config.generic.BlockWeaponsGather) && activeItem?.info.category == ItemCategory.Weapon) return;
if (!config.functions.ShouldAllowGather(item.info.shortname, out bool grantXPOnly)) return;
int prevAmount = item.amount;
bool isPowerTool = false;
if (skill != Skills.SKINNING && activeItem != null) {
HeldEntity heldEntity = activeItem.GetHeldEntity() as HeldEntity;
if (heldEntity is Jackhammer || heldEntity is Chainsaw) {
if (skill == Skills.WOODCUTTING && !CanUseChainsaw(player)) {
return;
}
if (skill == Skills.MINING && !CanUseJackhammer(player)) {
return;
}
if (!config.functions.gibs && dispenser.GetComponent<ServerGib>() != null) {
return;
}
isPowerTool = true;
}
}
if (!config.functions.gibs && skill == Skills.MINING && dispenser.GetComponent<ServerGib>() != null) {
return;
}
int amount = levelHandler(pi, player, item.amount, skill.Value, dispenser.baseEntity, isPowerTool);
if (!grantXPOnly) {
item.amount = amount;
}
if (item.amount != prevAmount) {
Interface.CallHook("OnZLevelDispenserGather", dispenser, player, item, prevAmount, item.amount, isPowerTool);
}
}
private bool CanUseJackhammer(BasePlayer player) => permission.UserHasPermission(player.UserIDString, config.generic.AllowJackhammerGather);
private bool CanUseChainsaw(BasePlayer player) => permission.UserHasPermission(player.UserIDString, config.generic.AllowChainsawGather);
private void OnDispenserBonus(ResourceDispenser dispenser, BasePlayer player, Item item) => OnDispenserGather(dispenser, player, item);
private object OnCollectiblePickup(CollectibleEntity collectible, BasePlayer player) {
if (collectible == null || collectible.itemList.IsNullOrEmpty() || !hasRights(player)) return null;
var pi = GetPlayerInfo(player);
if (!pi.ENABLED) return null;
if (config.functions.enabledCollectibleEntity.TryGetValue(collectible.ShortPrefabName, out var enabled) && !enabled) return null;
for (int i = 0; i < collectible.itemList.Length; i++) {
ItemAmount itemAmount = collectible.itemList[i];
if (itemAmount?.itemDef?.shortname == null || !TryGetCollectibleSkill(itemAmount.itemDef.shortname, player, out Skills skill)) continue;
int prevAmount = (int)itemAmount.amount;
itemAmount.amount = levelHandler(pi, player, prevAmount, skill, collectible);
if (Interface.CallHook("OnZLevelCollectiblePickup", itemAmount, player, collectible, prevAmount, itemAmount.amount) != null) {
return true;
}
}
return null;
}
private void OnCollectiblePickedup(CollectibleEntity collectible, BasePlayer player, Item item) {
if (collectible == null || item?.info?.shortname == null || !hasRights(player)) return;
var pi = GetPlayerInfo(player);
if (!pi.ENABLED) return;
if (config.functions.enabledCollectibleEntity.TryGetValue(collectible.ShortPrefabName, out var enabled) && !enabled) return;
if (!TryGetCollectibleSkill(item.info.shortname, player, out Skills skill)) return;
int prevAmount = item.amount;
item.amount = levelHandler(pi, player, item.amount, skill, collectible);
Interface.CallHook("OnZLevelCollectiblePickedup", item, player, collectible, prevAmount, item.amount);
}
private bool TryGetCollectibleSkill(string shortname, BasePlayer player, out Skills skill) {
switch (shortname) {
case "black.berry" or "blue.berry" or "bone.fragments" or "clone.seed" or "clone.orchid" or "clone.rose" or "clone.sunflower" or "cloth" or "corn" or "diesel_barrel" or "green.berry" or "mushroom" or "orchid" or "potato" or "pumpkin" or "red.berry" or "rose" or "seed.black.berry" or "seed.blue.berry" or "seed.corn" or "seed.green.berry" or "seed.hemp" or "seed.orchid" or "seed.potato" or "seed.pumpkin" or "seed.red.berry" or "seed.rose" or "seed.sunflower" or "seed.wheat" or "seed.white.berry" or "seed.yellow.berry" or "sunflower" or "wheat" or "white.berry" or "yellow.berry" : skill = IsSkillEnabled(Skills.ACQUIRE) ? Skills.ACQUIRE : Skills.SKINNING;
break;
case "hq.metal.ore" or "metal.ore" or "sulfur.ore" or "stones" : skill = config.settings.MiningStonesOre ? Skills.MINING : Skills.ACQUIRE;
break;
case "wood" : skill = config.settings.AcquireWood ? Skills.ACQUIRE : Skills.WOODCUTTING;
break;
default : if (IsSkillEnabled(Skills.ACQUIRE)) skill = Skills.ACQUIRE;
else if (IsSkillEnabled(Skills.SKINNING)) skill = Skills.SKINNING;
else {
skill = default;
return false;
}
if (_warnings.Add(shortname)) {
Puts("WARNING: {0} picked up undefined item: {1}. Defaulting to {2} skill.", player, shortname, Localize(skill + "Skill"));
}
break;
}
return IsSkillEnabled(skill);
}
private HashSet<string> _warnings = new();
private void OnFishCatch(Item item, BaseFishingRod rod, BasePlayer player) {
if (item?.info == null || rod == null || !hasRights(player) || !IsSkillEnabled(Skills.FISHING)) return;
var pi = GetPlayerInfo(player);
if (!pi.ENABLED) return;
double level = getLevel(pi, Skills.FISHING);
if (!TryGainXP(pi, player, Skills.FISHING, rod, config.settings.fishingDetails.xpPerCatch)) return;
int prevAmount = item.amount;
bool actualFish = TryGetFishable(item.info, out ItemModFishable fishable) && !fishable.IsJunk;
double chance = GetFishingBonusChance(level);
bool extraFish = actualFish && (chance >= 100.0 || chance > 0.0 && UnityEngine.Random.Range(0f, 100f) < chance);
if (extraFish) {
item.amount++;
}
Interface.CallHook("OnZLevelFishCatch", item, rod, player, prevAmount, item.amount, extraFish);
}
private void OnTimeSunset() {
if (!config.nightbonus.enableNightBonus || bonusOn) {
return;
}
bonusOn = true;
pointsPerHitCurrent = config.nightbonus.pointsPerHitAtNight;
resourceMultipliersCurrent = config.nightbonus.resourceMultipliersAtNight;
pointsPerHitPowerToolCurrent = config.nightbonus.pointsPerPowerToolAtNight;
if (config.nightbonus.broadcastEnabledBonus) {
foreach (var player in BasePlayer.activePlayerList) Message(player, "NightBonusOn");
}
if (config.nightbonus.logEnabledBonusConsole) Puts("Nightbonus points enabled");
}
private void OnTimeSunrise() {
if (!config.nightbonus.enableNightBonus || !bonusOn) {
return;
}
bonusOn = false;
pointsPerHitCurrent = config.settings.pointsPerHit;
resourceMultipliersCurrent = config.settings.resourceMultipliers;
pointsPerHitPowerToolCurrent = config.settings.pointsPerPowerTool;
if (config.nightbonus.broadcastEnabledBonus) {
foreach (var player in BasePlayer.activePlayerList) Message(player, "NightBonusOff");
}
if (config.nightbonus.logEnabledBonusConsole) Puts("Nightbonus points disabled");
}
private object OnGrowableGathered(GrowableEntity plant, Item item, BasePlayer player) {
if (item == null || !hasRights(player)) return true;
var pi = GetPlayerInfo(player);
if (!pi.ENABLED) return true;
Skills skill;
if (IsSkillEnabled(Skills.ACQUIRE)) skill = Skills.ACQUIRE;
else skill = Skills.SKINNING;
var prevAmount = item.amount;
item.amount = levelHandler(pi, player, item.amount, skill, plant);
Interface.CallHook("OnZLevelGrowableGathered", plant, item, player, prevAmount, item.amount);
return null;
}
private object OnItemCraft(ItemCraftTask task, BasePlayer crafter, Item fromTempBlueprint) {
if (!hasRights(crafter)) return null;
var pi = GetPlayerInfo(crafter);
if (!pi.ENABLED) return null;
var bypassCraftLevelRequirement = CanInstantCraftNoLevelRequirement(crafter);
var craftingTime = task.blueprint.time;
if (bypassCraftLevelRequirement) {
craftingTime = 0;
}
else if (IsSkillEnabled(Skills.CRAFTING)) {
if (pi.CRAFTING_LEVEL > 1) craftingTime -= task.blueprint.time * (float)((pi.CRAFTING_LEVEL * config.settings.craftingDetails.percent) / 100);
}
else return null;
if (craftingTime <= 0) {
craftingTime = 0;
foreach (var craftInfo in _craftData.CraftList.Values) {
if (!craftInfo.Enabled || craftInfo.shortName != task.blueprint.targetItem.shortname) {
continue;
}
if (task.amount >= craftInfo.MinBulkCraft && task.amount <= craftInfo.MaxBulkCraft && task.amount > 0) {
var stacks = GetStacks(task.amount * task.blueprint.amountToCreate, task.blueprint.targetItem.stackable);
if (!HasPlace(crafter, stacks)) {
ReturnCraft(task, crafter);
return true;
}
if (bypassCraftLevelRequirement || CanInstantCraft(crafter)) {
if (!task.blueprint.name.Contains("(Clone)")) task.blueprint = UnityEngine.Object.Instantiate(task.blueprint);
task.blueprint.amountToCreate *= task.amount;
crafter.inventory.crafting.FinishCrafting(task);
task.cancelled = true;
return true;
}
}
}
}
if (!task.blueprint.name.Contains("(Clone)")) task.blueprint = UnityEngine.Object.Instantiate(task.blueprint);
task.blueprint.time = craftingTime;
return null;
}
private List<int> GetStacks(int amount, int maxStack) {
List<int> stacks = new();
while (amount > maxStack && amount > 0) {
amount -= maxStack;
stacks.Add(maxStack);
}
if (amount > 0) {
stacks.Add(amount);
}
return stacks;
}
private bool HasPlace(BasePlayer crafter, List<int> stacks) {
if (!config.settings.craftingDetails.slots) {
return true;
}
var capacity = crafter.inventory.containerMain.capacity + crafter.inventory.containerBelt.capacity;
var taken = crafter.inventory.containerMain.itemList.Count + crafter.inventory.containerBelt.itemList.Count;
var slots = capacity - taken;
if (slots - stacks.Count < 0) {
return false;
}
return slots > 0;
}
private bool CanInstantCraftNoLevelRequirement(BasePlayer crafter) {
return config.settings.craftingDetails.noLevelRequirement && permission.UserHasPermission(crafter.UserIDString, config.settings.craftingDetails.Permission);
}
private bool CanInstantCraft(BasePlayer crafter) {
if (config.settings.craftingDetails.usePermission) {
return permission.UserHasPermission(crafter.UserIDString, config.settings.craftingDetails.Permission);
}
return true;
}
private void ReturnCraft(ItemCraftTask task, BasePlayer crafter) {
task.cancelled = true;
Message(crafter, "NoSlots");
foreach (var item in task.takenItems) {
if (item.amount > 0) crafter.GiveItem(item);
}
}
private object OnItemCraftFinished(ItemCraftTask task, Item item, ItemCrafter craft) {
if (task == null) return null;
var crafter = craft?.owner;
if (crafter == null || !hasRights(crafter.UserIDString)) return null;
var bypassLevelRequirement = CanInstantCraftNoLevelRequirement(crafter);
if (!bypassLevelRequirement && !IsSkillEnabled(Skills.CRAFTING)) return null;
var pi = GetPlayerInfo(crafter);
var xpPercentBefore = getExperiencePercent(pi, Skills.CRAFTING);
if (task.blueprint == null) {
Puts("There is problem obtaining task.blueprint on 'OnItemCraftFinished' hook! This is usually caused by some incompatable plugins.");
return null;
}
var experienceGain = (task.blueprint.time + 0.99f) / config.settings.craftingDetails.time;
if (experienceGain == 0) return null;
double Level = pi.CRAFTING_LEVEL;
double Points = pi.CRAFTING_POINTS;
Points += experienceGain * config.settings.craftingDetails.xp;
if (Points >= getLevelPoints(Level + 1)) {
var levelCap = config.settings.levelCaps.CRAFTING;
var maxLevel = levelCap > 0 && Level + 1 > levelCap;
if (!maxLevel) {
Level = getPointsLevel(Points, Skills.CRAFTING);
string format = $"<color={config.settings.colors.CRAFTING}>{Localize("LevelUpText", crafter.UserIDString)}</color>";
string message = string.Format(format, Localize("CRAFTINGSkill", crafter.UserIDString), Level, Points, getLevelPoints(Level + 1), pi.CRAFTING_LEVEL * Convert.ToDouble(config.settings.craftingDetails.percent));
Player.Message(crafter, message, string.IsNullOrEmpty(config.generic.pluginPrefix) ? string.Empty : config.generic.pluginPrefix, config.generic.steamIDIcon);
GiveReward(crafter, Skills.CRAFTING, Level);
if (config.generic.enableLevelupBroadcast) {
foreach (var target in BasePlayer.activePlayerList) {
if (target != null && target.userID != crafter.userID && hasRights(target.UserIDString) && GetPlayerInfo(target).ENABLED) {
Message(target, "LevelUpTextBroadcast", crafter.displayName, Level, config.settings.colors.CRAFTING, Localize("CRAFTINGSkill", target.UserIDString));
}
}
}
}
}
try {
if (item.info.shortname != "lantern_a" && item.info.shortname != "lantern_b") setPointsAndLevel(pi, Skills.CRAFTING, Points, Level);
}
catch {
}
try {
var xpPercentAfter = getExperiencePercent(pi, Skills.CRAFTING);
if (xpPercentAfter != xpPercentBefore) GUIUpdateSkill(crafter, Skills.CRAFTING);
}
catch {
}
if (task.amount > 0) return null;
if (task.blueprint != null && task.blueprint.name.Contains("(Clone)")) {
var behaviours = task.blueprint.GetComponents<MonoBehaviour>();
foreach (var behaviour in behaviours) {
if (behaviour.name.Contains("(Clone)")) UnityEngine.Object.Destroy(behaviour);
}
}
return null;
}
#endregion Serverhooks
#region Commands
[HookMethod("SendHelpText"), ChatCommand("stathelp")]
private void SendHelpText(BasePlayer player) {
string L(string key) => Localize(key, player.UserIDString);
_sb.Clear();
_sb.AppendLine(L("<size=18><color=orange>ZLevels</color></size><size=14><color=#ce422b>REMASTERED</color></size>"));
_sb.AppendLine(L("/stats - Displays your stats."));
_sb.AppendLine(L("/statsui - Enable/Disable stats UI. Use /statsui reset to restore its position."));
_sb.AppendLine(L("/statsui move - Start/finish moving the UI, or click DONE to finish moving the UI. /statsui button - Show/hide the move button."));
_sb.AppendLine(L("/statsonoff - Enable/Disable whole leveling."));
_sb.AppendLine(L("/statinfo - Displays information about skills."));
_sb.AppendLine(L("/stathelp - Displays the help."));
//sb.AppendLine("/topskills - Display max levels reached so far.");
if (_sb.Length > 0) SendReply(player, _sb.ToString());
}
private void PointsPerHitCommand(IPlayer user, string command, string[] args) {
if (!user.IsAdmin) return;
string L(string key, params object[] args) => Localize(key, user.Id, args);
if (args.Length < 2) {
Reply(user, "Syntax: zl.pointsperhit skill number");
Reply(user, "Possible skills are: WC, M, S, A, C, *(All skills)");
_sb.Clear();
_sb.Append(L("Current points per hit:"));
foreach (var currSkill in AllSkills) {
if (currSkill == Skills.FISHING || !IsSkillEnabled(currSkill)) continue;
_sb.Append(L(" {0} > {1} |", L(currSkill + "Skill"), pointsPerHitCurrent.Get(currSkill)));
}
user.Reply(_sb.ToString().TrimEnd('|'));
return;
}
if (!double.TryParse(args[1], out var points) || points < 1) {
Reply(user, "Incorrect number. Must be greater than or equal to 1");
return;
}
var skillName = args[0].ToUpper();
Skills skill;
if (skillName == "*") skill = Skills.ALL;
else if (skillName.Equals("C")) skill = Skills.CRAFTING;
else if (skillName.Equals("WC")) skill = Skills.WOODCUTTING;
else if (skillName.Equals("M")) skill = Skills.MINING;
else if (skillName.Equals("A")) skill = Skills.ACQUIRE;
else if (skillName.Equals("S")) skill = Skills.SKINNING;
else {
Reply(user, "Incorrect skill. Possible skills are: WC, M, S, A, C, *(All skills).");
return;
}
if (skill == Skills.ALL) {
foreach (var currSkill in AllSkills) {
if (currSkill == Skills.FISHING || !IsSkillEnabled(currSkill)) continue;
pointsPerHitCurrent.Set(currSkill, points);
}
_sb.Clear();
_sb.Append(L("New points per hit:"));
foreach (var currSkill in AllSkills) {
if (currSkill == Skills.FISHING || !IsSkillEnabled(currSkill)) continue;
_sb.Append(L(" {0} > {1} |", L(currSkill + "Skill"), pointsPerHitCurrent.Get(currSkill)));
}
user.Reply(_sb.ToString().TrimEnd('|'));
}
else {
pointsPerHitCurrent.Set(skill, points);
_sb.Clear();
_sb.Append(L("New points per hit:"));
foreach (var currSkill in AllSkills) {
if (currSkill == Skills.FISHING || !IsSkillEnabled(currSkill)) continue;
_sb.Append(L(" {0} > {1} |", L(currSkill + "Skill"), pointsPerHitCurrent.Get(currSkill)));
}
user.Reply(_sb.ToString().TrimEnd('|'));
}
}
private void PlayerXpmCommand(IPlayer user, string command, string[] args) {
if (!user.IsAdmin) return;
if (args.Length < 1) {
Reply(user, "XPM USE 1");
Reply(user, "XPM USE 2");
return;
}
IPlayer target = covalence.Players.FindPlayer(args[0]);
if (target == null) {
Reply(user, "PLAYER NOT FOUND");
return;
}
if (!data.PlayerInfo.TryGetValue(Convert.ToUInt64(target.Id), out var playerData)) {
Reply(user, "PlayerData is NULL!");
return;
}
if (args.Length < 2) {
var bonus1 = getXpMulti(playerData, target.Id);
var baseMultiplier = playerData.XP_MULTIPLIER;
if (baseMultiplier != bonus1) {
Reply(user, "Current XP multiplier for player '{0}' is {1}% with vip raising total to {2}%", target.Name, baseMultiplier, bonus1);
}
else Reply(user, "Current XP multiplier for player '{0}' is {1}%", target.Name, baseMultiplier);
return;
}
double multiplier = -1;
if (double.TryParse(args[1], out multiplier)) {
if (multiplier <= 0) {
Reply(user, "Incorrect number. Must be greater than 0");
return;
}
}
playerData.XP_MULTIPLIER = multiplier;
var bonus2 = getXpMulti(playerData, target.Id);
if (multiplier != bonus2) {
Reply(user, "New XP multiplier for player '{0}' is {1}% with vip raising total to {2}%", target.Name, multiplier, bonus2);
}
else Reply(user, "New XP multiplier for player '{0}' is {1}%", target.Name, bonus2);
}
private void InfoCommand(IPlayer user, string command, string[] args) {
if (!user.IsAdmin) {
return;
}
if (args.Length < 1) {
if (!GetLookingAtPlayer(user)) {
Reply(user, "INFO USE");
}
return;
}
IPlayer target = covalence.Players.FindPlayer(args[0]);
if (target == null) {
Reply(user, "PLAYER NOT FOUND");
return;
}
if (!data.PlayerInfo.TryGetValue(Convert.ToUInt64(target.Id), out var playerData)) {
Reply(user, "PlayerData is NULL!");
return;
}
PrintInfo(user, target.Id, target.Name, playerData);
}
private bool GetLookingAtPlayer(IPlayer user) {
BasePlayer player = user.Object as BasePlayer;
if (player == null) return false;
BasePlayer target = RelationshipManager.GetLookingAtPlayer(player);
if (target == null) return false;
if (!data.PlayerInfo.TryGetValue(target.userID, out var playerData)) return false;
PrintInfo(user, target.UserIDString, target.displayName, playerData);
return true;
}
private void PrintInfo(IPlayer user, string targetId, string targetName, PlayerInfo playerData) {
string L(string key) => Localize(key, user.Id);
TextTable textTable = new();
textTable.AddColumn(L("FieldInfo"));
textTable.AddColumn(L("Level"));
textTable.AddColumn(L("Points"));
textTable.AddRow(L("ACQUIRESkill"), $"{playerData.ACQUIRE_LEVEL:#0.##}", $"{playerData.ACQUIRE_POINTS:#0.##}");
textTable.AddRow(L("CRAFTINGSkill"), $"{playerData.CRAFTING_LEVEL:#0.##}", $"{playerData.CRAFTING_POINTS:#0.##}");
textTable.AddRow(L("MININGSkill"), $"{playerData.MINING_LEVEL:#0.##}", $"{playerData.MINING_POINTS:#0.##}");
textTable.AddRow(L("SKINNINGSkill"), $"{playerData.SKINNING_LEVEL:#0.##}", $"{playerData.SKINNING_POINTS:#0.##}");
textTable.AddRow(L("WOODCUTTINGSkill"), $"{playerData.WOODCUTTING_LEVEL:#0.##}", $"{playerData.WOODCUTTING_POINTS:#0.##}");
textTable.AddRow(L("FISHINGSkill"), $"{playerData.FISHING_LEVEL:#0.##}", $"{playerData.FISHING_POINTS:#0.##}");
textTable.AddRow(L("XPM"), $"{getXpMulti(playerData, targetId):#0.##}%", string.Empty);
user.Reply($"\n{L("STATS")}{targetName}\n{textTable}");
}
private void ResetCommand(IPlayer user, string command, string[] args) {
if (!user.IsAdmin) return;
if (args.Length != 1 || args[0] != "true") {
Reply(user, "RESET USE");
return;
}
WipeData();
SaveData();
foreach (var player in BasePlayer.activePlayerList) {
CreateGUI(player, GetPlayerInfo(player));
}
Reply(user, "Userdata was successfully reset to zero");
Puts("{0} ({1}) reset userdata to zero", user.Name, user.Id);
}
private void ZlvlCommand(IPlayer user, string command, string[] args) {
if (!user.IsAdmin) return;
string L(string key) => Localize(key, user.Id);
if (args.Length < 3) {
_sb.Clear();
_sb.AppendLine(L("Syntax: zl.lvl name|steamid skill [OPERATOR]NUMBER"));
_sb.AppendLine(L("Example: zl.lvl Player WC /2 -- Player gets his WC level divided by 2."));
_sb.AppendLine(L("Example: zl.lvl * * +3 -- Everyone currently playing in the server gets +3 for all skills."));
_sb.AppendLine(L("Example: zl.lvl ** * /2 -- Everyone (including offline players) gets their level divided by 2."));
_sb.AppendLine(L("Instead of names you can use wildcard(*): * - affects online players, ** - affects all players"));
_sb.AppendLine(L("Possible operators: *(XP Modified %), +(Adds level), -(Removes level), /(Divides level)")).AppendLine();
user.Reply(_sb.ToString());
return;
}
var playerName = args[0];
IPlayer target = covalence.Players.FindPlayer(playerName);
if (playerName != "*" && playerName != "**" && target == null) {
Reply(user, "PLAYER NOT FOUND");
return;
}
if (playerName != "*" && playerName != "**" && !data.PlayerInfo.ContainsKey(Convert.ToUInt64(target.Id))) {
Reply(user, "PlayerData is NULL!");
return;
}
if (target != null || playerName == "*" || playerName == "**") {
var playerMode = 0;
// Exact player
if (playerName == "*") playerMode = 1;
// Online players
else if (playerName == "**") playerMode = 2;
// All players
var skillName = args[1].ToUpper();
Skills skill;
if (skillName == "*") skill = Skills.ALL;
else if (skillName.Equals("C")) skill = Skills.CRAFTING;
else if (skillName.Equals("WC")) skill = Skills.WOODCUTTING;
else if (skillName.Equals("M")) skill = Skills.MINING;
else if (skillName.Equals("A")) skill = Skills.ACQUIRE;
else if (skillName.Equals("S")) skill = Skills.SKINNING;
else if (skillName.Equals("F")) skill = Skills.FISHING;
else {
Reply(user, "Incorrect skill. Possible skills are: WC, M, S, A, C, F, *(All skills).");
return;
}
var mode = 0;
// 0 = SET, 1 = ADD, 2 = SUBTRACT, 3 = multiplier, 4 = divide
int value;
bool correct;
if (args[2][0] == '+') {
mode = 1;
correct = int.TryParse(args[2].Replace("+", string.Empty), out value);
}
else if (args[2][0] == '-') {
mode = 2;
correct = int.TryParse(args[2].Replace("-", string.Empty), out value);
}
else if (args[2][0] == '*') {
mode = 3;
correct = int.TryParse(args[2].Replace("*", string.Empty), out value);
}
else if (args[2][0] == '/') {
mode = 4;
correct = int.TryParse(args[2].Replace("/", string.Empty), out value);
}
else {
correct = int.TryParse(args[2], out value);
}
if (correct) {
if (mode == 3) // Change XP Multiplier.
{
if (skill != Skills.ALL) {
Reply(user, "XPMultiplier is changeable for all skills! Use * instead of {0}.", skill);
return;
}
if (playerMode == 1) {
foreach (var currPlayer in BasePlayer.activePlayerList) editMultiplierForPlayer(value, currPlayer.userID);
}
else if (playerMode == 2) editMultiplierForPlayer(value);
else if (target != null) editMultiplierForPlayer(value, Convert.ToUInt64(target.Id));
var whom = playerMode == 1 ? L("ALL ONLINE PLAYERS") : playerMode == 2 ? L("ALL PLAYERS") : target.Name;
Reply(user, "XP rates has changed to {0}% of normal XP for {1}", value, whom);
return;
}
if (playerMode == 1) {
foreach (var connPlayer in covalence.Players.Connected) {
adminModifyPlayerStats(user, skill, value, mode, connPlayer);
}
}
else if (playerMode == 2) {
foreach (var other in covalence.Players.All) {
if (data.PlayerInfo.ContainsKey(Convert.ToUInt64(other.Id))) {
adminModifyPlayerStats(user, skill, value, mode, other);
}
}
}
else {
adminModifyPlayerStats(user, skill, value, mode, target);
}
}
}
}
private void CheckCommand(IPlayer user, string command, string[] args) {
if (!user.IsAdmin) return;
int count = 0;
var pi = CreatePlayerInfo();
foreach (var info in data.PlayerInfo) {
if (info.Value.IsDefault(pi)) {
count++;
}
}
Reply(user, "{0} / {1} entries in datafile can be removed", count, data.PlayerInfo.Count);
}
private void CleanCommand(IPlayer user, string command, string[] args) {
if (!user.IsAdmin) return;
int count = data.PlayerInfo.Count;
int cleaned = Clean();
Reply(user, "{0} / {1} entries in datafile have been removed", cleaned, count);
SaveData();
}
private int Clean() {
int count = 0;
if (data == null) {
return count;
}
var pi = CreatePlayerInfo();
foreach (var info in data.PlayerInfo.ToList()) {
if (info.Value.IsDefault(pi)) {
count++;
data.PlayerInfo.Remove(info.Key);
}
}
return count;
}
private void PenaltyCommand(IPlayer user, string command, string[] args) {
if (!user.IsAdmin) return;
if (args.Length == 1 && args[0] == "status") {
Reply(user, config.generic.penaltyOnDeath ? "Penalty is currently enabled." : "Penalty is currently disabled.");
return;
}
if (args.Length == 0) config.generic.penaltyOnDeath = !config.generic.penaltyOnDeath;
else config.generic.penaltyOnSuicide = !config.generic.penaltyOnSuicide;
if (config.generic.penaltyOnDeath || config.generic.penaltyOnSuicide) {
Subscribe(nameof(OnEntityDeath));
Reply(user, "Penalty is now enabled.");
}
else {
Unsubscribe(nameof(OnEntityDeath));
Reply(user, "Penalty is now disabled.");
}
SaveConfig();
}
[ChatCommand("stats")]
private void StatsCommand(BasePlayer player, string command, string[] args) {
if (!hasRights(player)) {
Message(player, "NoPermission");
return;
}
string id = player.UserIDString;
string L(string key, params object[] args) => Localize(key, id, args);
_sb.Clear();
_sb.Appen
// ... [Code preview truncated due to length. Full source in attached file] ...