C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using Oxide.Core;
using System.Collections.Generic;
using UnityEngine;
using Newtonsoft.Json;
using CompanionServer.Handlers;
using System.Linq;
using Oxide.Core.Plugins;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using System.Globalization;
namespace Oxide.Plugins {
[Info("TreasureMaps", "Copek", "1.1.5")]
class TreasureMaps : RustPlugin {
# region Config
private Configuration config;
private static System.Random rng = new System.Random();
private int _halfWorldSize;
[PluginReference]
private readonly Plugin NpcSpawn, Economics, ServerRewards;
class Configuration {
[JsonProperty("AutomaticEventEnabled")]
public bool AutomaticEventEnabled {
get;
set;
}
= false;
[JsonProperty("MaxEventsAtTime")]
public int MaxConcurrentEvents {
get;
set;
}
= 5;
[JsonProperty("MinEventTimer")]
public float MinEventTimer {
get;
set;
}
= 1800f;
// 30 minutes
[JsonProperty("MaxEventTimer")]
public float MaxEventTimer {
get;
set;
}
= 3600f;
[JsonProperty("LocalTreasureMaxDistance")]
public float LocalTreasureMaxDistance {
get;
set;
}
= 150;
[JsonProperty("MaxOpenedMaps")]
public int MaxOpenedMaps {
get;
set;
}
= 3;
[JsonProperty("MapOpenCooldown")]
public int MapOpenCooldown {
get;
set;
}
= 60;
// Default value, in seconds
[JsonProperty("MaxOpenedCoins")]
public int MaxOpenedCoins {
get;
set;
}
= 3;
[JsonProperty("CoinOpenCooldown")]
public int CoinOpenCooldown {
get;
set;
}
= 60;
// Default value, in seconds
[JsonProperty("Chest/NPC/Marker DestroyTimer")]
public float ChestDestroyTimer {
get;
set;
}
= 1800f;
[JsonProperty("TreasureMapsDescription")]
public List<string> TreasureMapsDescription {
get;
set;
}
[JsonProperty("Maps")]
public List<MapConfiguration> Maps {
get;
set;
}
[JsonProperty("Coins")]
public List<CoinConfiguration> Coins {
get;
set;
}
public Oxide.Core.VersionNumber Version {
get;
set;
}
}
class MapConfiguration {
[JsonProperty("EventProbability")]
public float EventProbability {
get;
set;
}
= 0.1f;
[JsonProperty("MarkerRadius")]
public float MarkerRadius {
get;
set;
}
= 0.2f;
[JsonProperty("MarkerDisplayName")]
public string MarkerDisplayName {
get;
set;
}
= "Treasure";
[JsonProperty("MarkerColor")]
public string MarkerColor {
get;
set;
}
= "00FFFF";
[JsonProperty("MarkerOutlineColor")]
public string MarkerOutlineColor {
get;
set;
}
= "00FFFFFF";
[JsonProperty("CustomStackSize")]
public int CustomStackSize {
get;
set;
}
= 1;
[JsonProperty("ItemDisplayName")]
public string ItemDisplayName {
get;
set;
}
[JsonProperty("ItemShortname")]
public string ItemShortname {
get;
set;
}
[JsonProperty("SkinID")]
public ulong SkinID {
get;
set;
}
[JsonProperty("SpawnedPrefabChest")]
public string SpawnedPrefabChest {
get;
set;
}
[JsonProperty("SpawnedPrefabSkin")]
public ulong SpawnedPrefabSkin {
get;
set;
}
[JsonProperty("MinItemPerChest")]
public int MinItemPerChest {
get;
set;
}
[JsonProperty("MaxItemPerChest")]
public int MaxItemPerChest {
get;
set;
}
[JsonProperty("LootTable")]
public string LootTable {
get;
set;
}
[JsonProperty("LootTables")]
public Dictionary<string, List<LootTableItem>> LootTables {
get;
set;
}
[JsonProperty("NPCSpawns")]
public List<NPCSpawn> NPCSpawns {
get;
set;
}
[JsonProperty("Spawns")]
public List<MapSpawn> Spawns {
get;
set;
}
[JsonProperty("SpawnBradley")]
public bool SpawnBradley {
get;
set;
}
= false;
[JsonProperty("BradleyHealth")]
public int BradleyHealth {
get;
set;
}
= 1000;
// Default Bradley health
[JsonProperty("MinCratesToSpawn")]
public int MinCratesToSpawn {
get;
set;
}
= 1;
// Default min crates
[JsonProperty("MaxCratesToSpawn")]
public int MaxCratesToSpawn {
get;
set;
}
= 3;
// Default max crates
}
private enum EconomyType {
Plugin, Item
}
class CoinConfiguration {
[JsonProperty("CustomStackSize")]
public int CustomStackSize {
get;
set;
}
= 1;
[JsonProperty("ItemDisplayName")]
public string ItemDisplayName {
get;
set;
}
[JsonProperty("ItemShortname")]
public string ItemShortname {
get;
set;
}
[JsonProperty("SkinID")]
public ulong SkinID {
get;
set;
}
[JsonProperty("MinItemPerCoin")]
public int MinItemPerCoin {
get;
set;
}
[JsonProperty("MaxItemPerCoin")]
public int MaxItemPerCoin {
get;
set;
}
[JsonProperty("LootTable")]
public string LootTable {
get;
set;
}
[JsonProperty("LootTables")]
public Dictionary<string, List<CoinLootTableItem>> LootTables {
get;
set;
}
[JsonProperty("Spawns")]
public List<MapSpawn> Spawns {
get;
set;
}
}
class LootTableItem {
public string DisplayName {
get;
set;
}
public string Shortname {
get;
set;
}
public ulong SkinID {
get;
set;
}
public int MaxItemAmount {
get;
set;
}
public int MinItemAmount {
get;
set;
}
public float Probability {
get;
set;
}
public bool IsBlueprint {
get;
set;
}
}
class CoinLootTableItem {
[JsonConverter(typeof(StringEnumConverter))]
public EconomyType Economy {
get;
set;
}
= EconomyType.Item;
public string PluginName {
get;
set;
}
public string DisplayName {
get;
set;
}
public string Shortname {
get;
set;
}
public ulong SkinID {
get;
set;
}
public int MaxItemAmount {
get;
set;
}
public int MinItemAmount {
get;
set;
}
public float Probability {
get;
set;
}
public bool IsBlueprint {
get;
set;
}
}
class MapSpawn {
[JsonProperty("PrefabPath")]
public string PrefabPath {
get;
set;
}
[JsonProperty("SpawnChance")]
public float SpawnChance {
get;
set;
}
}
class NPCSpawn {
[JsonProperty("SpawnCount")]
public int SpawnCount {
get;
set;
}
= 0;
[JsonProperty("SpawnRadius")]
public float SpawnRadius {
get;
set;
}
= 5f;
[JsonProperty("EntityDisplayName")]
public string EntityDisplayName {
get;
set;
}
= "Treasure Guard";
[JsonProperty("Health")]
public int Health {
get;
set;
}
= 100;
[JsonProperty("RoamRange")]
public float RoamRange {
get;
set;
}
= 5f;
[JsonProperty("ChaseRange")]
public float ChaseRange {
get;
set;
}
= 30f;
[JsonProperty("SenseRange")]
public float SenseRange {
get;
set;
}
= 20f;
[JsonProperty("ListenRange")]
public float ListenRange {
get;
set;
}
= 10f;
[JsonProperty("AttackRangeMultiplier")]
public float AttackRangeMultiplier {
get;
set;
}
= 1f;
[JsonProperty("CheckVisionCone")]
public bool CheckVisionCone {
get;
set;
}
= true;
[JsonProperty("VisionCone")]
public float VisionCone {
get;
set;
}
= 140f;
[JsonProperty("HostileTargetsOnly")]
public bool HostileTargetsOnly {
get;
set;
}
= false;
[JsonProperty("DamageScale")]
public float DamageScale {
get;
set;
}
= 1f;
[JsonProperty("AimConeScale")]
public float AimConeScale {
get;
set;
}
= 1f;
[JsonProperty("Speed")]
public float Speed {
get;
set;
}
= 6f;
[JsonProperty("MemoryDuration")]
public float MemoryDuration {
get;
set;
}
= 10f;
[JsonProperty("Kit")]
public string Kit {
get;
set;
}
[JsonProperty("WearItems")]
public List<NPCWearItem> WearItems {
get;
set;
}
= new List<NPCWearItem>();
[JsonProperty("BeltItems")]
public List<NPCBeltItem> BeltItems {
get;
set;
}
= new List<NPCBeltItem>();
[JsonProperty("CustomLoot")]
public bool CustomLoot {
get;
set;
}
= false;
[JsonProperty("LootTable")]
public List<LootTableItem> LootTable {
get;
set;
}
= new List<LootTableItem>();
}
class NPCWearItem {
[JsonProperty("ShortName")]
public string ShortName {
get;
set;
}
[JsonProperty("SkinID")]
public ulong SkinId {
get;
set;
}
}
class NPCBeltItem {
[JsonProperty("ShortName")]
public string ShortName {
get;
set;
}
[JsonProperty("Amount")]
public int Amount {
get;
set;
}
[JsonProperty("SkinID")]
public ulong SkinId {
get;
set;
}
[JsonProperty("Mods")]
public List<string> Mods {
get;
set;
}
= new List<string>();
[JsonProperty("Ammo")]
public string Ammo {
get;
set;
}
}
class PlayerMapData {
public int OpenedMapsCount {
get;
set;
}
public DateTime LastMapOpenTime {
get;
set;
}
public int OpenedCoinsCount {
get;
set;
}
public DateTime LastCoinOpenTime {
get;
set;
}
public PlayerMapData() {
OpenedMapsCount = 0;
LastMapOpenTime = DateTime.MinValue;
OpenedCoinsCount = 0;
LastCoinOpenTime = DateTime.MinValue;
}
}
private JObject GetNpcConfig(NPCSpawn npcSpawn) {
return new JObject {
["Name"]
= npcSpawn.EntityDisplayName, ["Health"] = npcSpawn.Health, ["RoamRange"] = npcSpawn.RoamRange, ["ChaseRange"] = npcSpawn.ChaseRange, ["SenseRange"] = npcSpawn.SenseRange, ["ListenRange"] = npcSpawn.ListenRange, ["AttackRangeMultiplier"] = npcSpawn.AttackRangeMultiplier, ["CheckVisionCone"] = npcSpawn.CheckVisionCone, ["VisionCone"] = npcSpawn.VisionCone, ["HostileTargetsOnly"] = npcSpawn.HostileTargetsOnly, ["DamageScale"] = npcSpawn.DamageScale, ["TurretDamageScale"] = 0f, ["AimConeScale"] = npcSpawn.AimConeScale, ["DisableRadio"] = true, ["CanRunAwayWater"] = true, ["CanSleep"] = false, ["Speed"] = npcSpawn.Speed, ["AreaMask"] = 1, ["AgentTypeID"] = -1372625422, ["HomePosition"] = string.Empty, ["MemoryDuration"] = npcSpawn.MemoryDuration, ["Kit"] = npcSpawn.Kit, ["WearItems"] = new JArray(npcSpawn.WearItems.Select(x => new JObject {
["ShortName"]
= x.ShortName, ["SkinID"] = x.SkinId
}
)), ["BeltItems"] = new JArray(npcSpawn.BeltItems.Select(x => new JObject {
["ShortName"]
= x.ShortName, ["Amount"] = x.Amount, ["SkinID"] = x.SkinId, ["Mods"] = new JArray(x.Mods), ["Ammo"] = x.Ammo
}
)), ["States"] = new JArray {
"RoamState", "ChaseState", "CombatState"
}
}
;
}
private void OnCorpsePopulate(ScientistNPC entity, NPCPlayerCorpse corpse) {
if (entity == null) {
Puts("OnCorpsePopulate: entity is null");
return;
}
if (config?.Maps == null) {
Puts("OnCorpsePopulate: config.Maps is null");
return;
}
var matchingNpcSpawn = config.Maps.Where(m => m.NPCSpawns != null).SelectMany(m => m.NPCSpawns).FirstOrDefault(n => n.EntityDisplayName == entity.displayName);
if (matchingNpcSpawn == null) {
return;
}
NextTick(() => {
if (corpse == null) {
Puts("OnCorpsePopulate: corpse is null");
return;
}
if (matchingNpcSpawn.CustomLoot) {
PopulateCorpseWithCustomLoot(corpse, matchingNpcSpawn.LootTable);
}
}
);
}
private void PopulateCorpseWithCustomLoot(NPCPlayerCorpse corpse, List<LootTableItem> lootTable) {
var container = corpse.containers[0];
container.Clear();
var selectedItems = lootTable.Where(item => UnityEngine.Random.value <= item.Probability).ToList();
foreach (var lootItem in selectedItems) {
var amount = UnityEngine.Random.Range(lootItem.MinItemAmount, lootItem.MaxItemAmount + 1);
Item item;
if (lootItem.IsBlueprint) {
var itemDefinition = ItemManager.FindItemDefinition(lootItem.Shortname);
var blueprint = ItemManager.CreateByItemID(ItemManager.FindItemDefinition("blueprintbase").itemid, amount);
blueprint.blueprintTarget = itemDefinition.itemid;
item = blueprint;
}
else {
var itemDefinition = ItemManager.FindItemDefinition(lootItem.Shortname);
item = ItemManager.CreateByItemID(itemDefinition.itemid, amount, lootItem.SkinID);
}
if (item != null) {
if (!string.IsNullOrEmpty(lootItem.DisplayName)) {
item.name = lootItem.DisplayName;
}
container.Insert(item);
}
}
}
private object CanPopulateLoot(ScientistNPC entity, NPCPlayerCorpse corpse) {
if (entity == null || corpse == null) return null;
var npcSpawn = config.Maps.Where(m => m.NPCSpawns != null).SelectMany(m => m.NPCSpawns).FirstOrDefault(n => n.EntityDisplayName == entity.displayName);
if (npcSpawn == null) return null;
return npcSpawn.CustomLoot ? (object)true : null;
}
private void OnEntityDeath(ScientistNPC npc, HitInfo info) {
if (npc == null || info == null) return;
}
private Configuration GetDefaultConfig() {
return new Configuration {
AutomaticEventEnabled = false, MaxConcurrentEvents = 5, MinEventTimer = 1800, MaxEventTimer = 3600, LocalTreasureMaxDistance = 150, MaxOpenedMaps = 3, MapOpenCooldown = 60, MaxOpenedCoins = 3, CoinOpenCooldown = 60, TreasureMapsDescription = new List<string> {
"LocalTreasureMaxDistance - max distance of spawning prefab/chest", "CustomStackSize - you can change stack size of each map,and for coins", "You can change skin and display name of each map,for each coin", "MinItemPerChest/MaxItemPerChest - how much items player will get from spawned chest ", "MinItemPerCoin/MaxItemPerCoin - how much items player will get from coin", "SpawnedPrefabChest - what chest will spawn when you unwarp map item,if you put default rust container it will be default rust loot table", "SpawnedPrefabSkin - skin of prefab", "You can add more items to loottable,works also with loottable of coins", "Min/MaxAmount - quantity of that item (you can put min/max - to same number (1),so its min and max 1 quantity of that item)", "Probability - from 0.0 (0%) to 1.0 (100%) chance to get that item", "IsBlueprint - if true you will give bp of that item", "command : /givemap <display name> <quantity>; example /givemap green map 5", "command : /givecoin <display name> <quantity>; example /givecoin gold coin 5", "command : /givemapto <player ID/name> <display name> <quantity>; example /givemapto copek green map 5", "command : /givecointo <player ID/name> <display name> <quantity>; example /givecointo copek gold coin 5", "console command : givemapto <player ID/name> <display name> <quantity>; example givemapto copek green map 5", "console command : givecointo <player ID/name> <display name> <quantity>; example givecointo copek gold coin 5", "Spawns - PrefabPath (chose where will map spawn),SpawnChance (0-100 ,chance to spawn map in that prefab),works also with coins,works also with coins", "SpawnCount - how much scientist will be defending chest", "SpawnRadius - distance where scientist will spawn", "NpcOptions - you can change stats of npc Health,Damage and more", "Kits - you can use kits or you can manualy add wear,belt items", "EntityDisplayName - name of npc, for each group change name ,if you are using custom loot for npc", "CustomLoot - true/false ,if true you can add custom loot table for npc body", "You can now add new custom item <coins>, coins can we crackopen/unwarp and you will get items/plugin balance", "In coins you have option to give economics and serverrewards", "Economy: here you put item or plugin", "PluginName: economics or serverrewards"
}
, Maps = new List<MapConfiguration> {
new MapConfiguration {
EventProbability = 1.0f, MarkerRadius = 0.2f, MarkerDisplayName = "Green Treasure", MarkerColor = "00FF00", MarkerOutlineColor = "00FF00", CustomStackSize = 10, ItemDisplayName = "Green Map", ItemShortname = "xmas.present.large", SkinID = 3073615238, SpawnedPrefabChest = "assets/prefabs/deployable/woodenbox/woodbox_deployed.prefab", SpawnedPrefabSkin = 1818868472, MinItemPerChest = 1, MaxItemPerChest = 2, LootTable = "greenmap", LootTables = new Dictionary<string, List<LootTableItem>> {
{
"greenmap", new List<LootTableItem> {
new LootTableItem {
DisplayName = "", Shortname = "rock", MinItemAmount = 1, MaxItemAmount = 5, Probability = 1.0F, SkinID = 0,
}
}
}
}
, Spawns = new List<MapSpawn> {
new MapSpawn {
PrefabPath = "assets/bundled/prefabs/radtown/crate_normal_2.prefab", SpawnChance = 0f
}
}
, NPCSpawns = new List<NPCSpawn> {
new NPCSpawn {
SpawnCount = 1, SpawnRadius = 5f, Health = 100, RoamRange = 5f, ChaseRange = 30f, SenseRange = 20f, ListenRange = 10f, AttackRangeMultiplier = 1f, CheckVisionCone = true, VisionCone = 140f, HostileTargetsOnly = false, DamageScale = 1f, AimConeScale = 1f, Speed = 6f, MemoryDuration = 10f, Kit = "", WearItems = new List<NPCWearItem> {
new NPCWearItem {
ShortName = "hazmatsuit", SkinId = 0
}
}
, BeltItems = new List<NPCBeltItem> {
new NPCBeltItem {
ShortName = "rifle.ak", Amount = 1, SkinId = 0, Mods = new List<string> {
"weapon.mod.flashlight"
}
, Ammo = "ammo.rifle"
}
}
, CustomLoot = false, LootTable = new List<LootTableItem> {
new LootTableItem {
DisplayName = "Custom Rock", Shortname = "rock", SkinID = 0, MinItemAmount = 1, MaxItemAmount = 5, Probability = 1.0f, IsBlueprint = false
}
}
}
}
}
, new MapConfiguration {
EventProbability = 1.0f, MarkerRadius = 0.2f, MarkerDisplayName = "Blue Treasure", MarkerColor = "00BFFF", MarkerOutlineColor = "00BFFF", CustomStackSize = 10, ItemDisplayName = "Blue Map", ItemShortname = "xmas.present.large", SkinID = 3073615579, SpawnedPrefabChest = "assets/prefabs/deployable/large wood storage/box.wooden.large.prefab", SpawnedPrefabSkin = 837107924, MinItemPerChest = 1, MaxItemPerChest = 2, LootTable = "bluemap", LootTables = new Dictionary<string, List<LootTableItem>> {
{
"bluemap", new List<LootTableItem> {
new LootTableItem {
DisplayName = "", Shortname = "scrap", MinItemAmount = 1, MaxItemAmount = 5, Probability = 1.0F, SkinID = 0,
}
}
}
}
, Spawns = new List<MapSpawn> {
new MapSpawn {
PrefabPath = "assets/bundled/prefabs/radtown/crate_normal_2.prefab", SpawnChance = 0f
}
}
}
, new MapConfiguration {
EventProbability = 1.0f, MarkerRadius = 0.2f, MarkerDisplayName = "Red Treasure", MarkerColor = "FF0000", MarkerOutlineColor = "FF0000", CustomStackSize = 10, ItemDisplayName = "Red Map", ItemShortname = "xmas.present.large", SkinID = 3073615919, SpawnedPrefabChest = "assets/prefabs/misc/decor_dlc/storagebarrel/storage_barrel_a.prefab", SpawnedPrefabSkin = 0, MinItemPerChest = 1, MaxItemPerChest = 2, LootTable = "redmap", LootTables = new Dictionary<string, List<LootTableItem>> {
{
"redmap", new List<LootTableItem> {
new LootTableItem {
DisplayName = "Not Bad Weapon", Shortname = "rifle.ak", MinItemAmount = 1, MaxItemAmount = 1, Probability = 1.0F, SkinID = 0,
}
}
}
}
, Spawns = new List<MapSpawn> {
new MapSpawn {
PrefabPath = "assets/bundled/prefabs/radtown/crate_normal_2.prefab", SpawnChance = 0f
}
}
}
, new MapConfiguration {
EventProbability = 1.0f, MarkerRadius = 0.2f, MarkerDisplayName = "Gold Treasure", MarkerColor = "FFFF00", MarkerOutlineColor = "FFFF00", CustomStackSize = 10, ItemDisplayName = "Gold Map", ItemShortname = "xmas.present.large", SkinID = 3073616325, SpawnedPrefabChest = "assets/prefabs/misc/decor_dlc/storagebarrel/storage_barrel_c.prefab", SpawnedPrefabSkin = 0, MinItemPerChest = 1, MaxItemPerChest = 2, LootTable = "goldmap", LootTables = new Dictionary<string, List<LootTableItem>> {
{
"goldmap", new List<LootTableItem> {
new LootTableItem {
DisplayName = "Fire!!", Shortname = "lmg.m249", MinItemAmount = 1, MaxItemAmount = 1, Probability = 1.0F, SkinID = 0,
}
}
}
}
, Spawns = new List<MapSpawn> {
new MapSpawn {
PrefabPath = "assets/bundled/prefabs/radtown/crate_normal_2.prefab", SpawnChance = 0f
}
}
}
}
, Coins = new List<CoinConfiguration> {
new CoinConfiguration {
CustomStackSize = 10, ItemDisplayName = "Gold Coin", ItemShortname = "easter.goldegg", SkinID = 3151241149, MinItemPerCoin = 1, MaxItemPerCoin = 10, LootTable = "goldcoin", LootTables = new Dictionary<string, List<CoinLootTableItem>> {
{
"goldcoin", new List<CoinLootTableItem> {
new CoinLootTableItem {
Economy = EconomyType.Item, PluginName = "", DisplayName = "Gold Coin", Shortname = "easter.goldegg", MinItemAmount = 1, MaxItemAmount = 10, Probability = 1.0F, SkinID = 3151241149,
}
, new CoinLootTableItem {
Economy = EconomyType.Plugin, PluginName = "Economics", DisplayName = "Gold Coin", Shortname = "easter.goldegg", MinItemAmount = 50, MaxItemAmount = 100, Probability = 1.0F, SkinID = 3151241149,
}
, new CoinLootTableItem {
Economy = EconomyType.Plugin, PluginName = "ServerRewards", DisplayName = "Gold Coin", Shortname = "easter.goldegg", MinItemAmount = 150, MaxItemAmount = 200, Probability = 1.0F, SkinID = 3151241149,
}
,
}
}
}
, Spawns = new List<MapSpawn> {
new MapSpawn {
PrefabPath = "assets/bundled/prefabs/radtown/crate_normal_2.prefab", SpawnChance = 100f
}
}
}
}
}
;
}
private void Init() {
Puts("Treasure Maps plugin loaded.");
LoadConfig();
permission.RegisterPermission("treasuremaps.give", this);
if (!plugins.Exists("NpcSpawn")) {
PrintError("NpcSpawn plugin doesn't exist! Please install it from the appropriate source.");
Interface.Oxide.UnloadPlugin(Name);
return;
}
}
protected override void LoadConfig() {
base.LoadConfig();
config = Config.ReadObject<Configuration>();
if (config.Version < Version) UpdateConfigValues();
Config.WriteObject(config, true);
}
protected override void LoadDefaultConfig() => config = GetDefaultConfig();
protected override void SaveConfig() => Config.WriteObject(config, true);
private void UpdateConfigValues() {
PrintWarning("Config update detected! Updating config values...");
Configuration defaultConfig = GetDefaultConfig();
if (config.Version < new VersionNumber(1, 1, 5)) {
foreach (var mapConfig in config.Maps) {
if (mapConfig.NPCSpawns == null) {
PrintWarning($"NPCSpawns was null in configuration for map '{mapConfig.MarkerDisplayName}', initializing to an empty list.");
mapConfig.NPCSpawns = new List<NPCSpawn>();
}
foreach (var npcSpawn in mapConfig.NPCSpawns) {
foreach (var beltItem in npcSpawn.BeltItems) {
if (int.TryParse(beltItem.Ammo, out int ammoCount)) {
beltItem.Ammo = "ammo.rifle";
PrintWarning($"Converted ammo count ({ammoCount}) to default ammo type 'ammo.rifle' for NPC '{npcSpawn.EntityDisplayName}'.");
}
}
}
}
config.Version = Version;
PrintWarning("Config update for Ammo type and NPCSpawns initialization completed!");
}
Config.WriteObject(config, true);
}
void OnServerInitialized() {
_halfWorldSize = ConVar.Server.worldsize / 2;
FindMonuments();
spawnedEntities.RemoveAll(entity => entity == null || entity.IsDestroyed);
foreach (var entity in spawnedEntities) {
entity.Kill();
}
spawnedEntities.Clear();
StartEventTimer();
}
private List<string> spawnedMarkers = new List<string>();
void Unload() {
foreach (var entity in spawnedEntities.ToList()) {
if (entity != null && !entity.IsDestroyed) {
entity.Kill();
}
}
spawnedEntities.Clear();
foreach (var markerName in spawnedMarkers.ToList()) {
RemoveMarker(markerName);
}
spawnedMarkers.Clear();
}
void OnEntityKill(BaseNetworkable networkable) {
BaseEntity entity = networkable as BaseEntity;
if (entity != null) {
spawnedEntities.Remove(entity);
}
}
public void AddToSpawnedEntities(BaseEntity entity) {
if (entity != null && !entity.IsDestroyed) {
spawnedEntities.Add(entity);
}
}
#endregion
private static Item GetMap(MapConfiguration config) {
Item item = ItemManager.CreateByName("xmas.present.large", 1, config.SkinID);
if (!string.IsNullOrEmpty(config.ItemDisplayName)) item.name = config.ItemDisplayName;
return item;
}
private void GivePlayerItem(BasePlayer player, string shortname, int amount, ulong skinID, string displayName) {
var itemDefinition = ItemManager.FindItemDefinition(shortname);
if (itemDefinition == null) {
PrintError($"Item shortname '{shortname}' not found!");
return;
}
var mapConfig = config.Maps.FirstOrDefault(c => c.ItemShortname == shortname && c.SkinID == skinID);
var coinConfig = config.Coins.FirstOrDefault(c => c.ItemShortname == shortname && c.SkinID == skinID);
int customStackSize = (mapConfig != null) ? mapConfig.CustomStackSize : (coinConfig != null) ? coinConfig.CustomStackSize : itemDefinition.stackable;
if (amount <= customStackSize) {
var item = ItemManager.Create(itemDefinition, amount, skinID);
if (!string.IsNullOrEmpty(displayName)) {
item.name = displayName;
}
if (!player.inventory.GiveItem(item, player.inventory.containerMain)) {
item.Drop(player.transform.position, player.transform.forward);
}
}
else {
while (amount > 0) {
var stackSize = Mathf.Min(amount, customStackSize);
var item = ItemManager.Create(itemDefinition, stackSize, skinID);
if (!string.IsNullOrEmpty(displayName)) {
item.name = displayName;
}
if (!player.inventory.GiveItem(item, player.inventory.containerMain)) {
item.Drop(player.transform.position, player.transform.forward);
}
amount -= stackSize;
}
}
}
private void OnLootSpawn(LootContainer container) {
if (container == null || container.inventory == null) return;
List<MapConfiguration> possibleChests = config.Maps.Where(x => x.Spawns.Any(y => y.PrefabPath == container.PrefabName)).ToList();
foreach (var chest in possibleChests) {
MapSpawn crate = chest.Spawns.FirstOrDefault(x => x.PrefabPath == container.PrefabName);
if (crate != null && UnityEngine.Random.Range(0f, 100f) <= crate.SpawnChance) {
if (container.inventory.itemList.Count == container.inventory.capacity) {
container.inventory.capacity++;
}
Item item = GetMap(chest);
if (!item.MoveToContainer(container.inventory)) {
item.Remove();
}
}
}
List<CoinConfiguration> possibleCoins = config.Coins.Where(x => x.Spawns.Any(y => y.PrefabPath == container.PrefabName)).ToList();
foreach (var coin in possibleCoins) {
MapSpawn spawn = coin.Spawns.FirstOrDefault(x => x.PrefabPath == container.PrefabName);
if (spawn != null && UnityEngine.Random.Range(0f, 100f) <= spawn.SpawnChance) {
if (container.inventory.itemList.Count == container.inventory.capacity) {
container.inventory.capacity++;
}
Item item = GetCoin(coin);
if (!item.MoveToContainer(container.inventory)) {
item.Remove();
}
}
}
}
private static Item GetCoin(CoinConfiguration config) {
Item item = ItemManager.CreateByName("easter.goldegg", 1, config.SkinID);
if (!string.IsNullOrEmpty(config.ItemDisplayName)) item.name = config.ItemDisplayName;
return item;
}
private Dictionary<string, PlayerMapData> playerMapData = new Dictionary<string, PlayerMapData>();
object OnItemAction(Item item, string action, BasePlayer player) {
if (item == null || string.IsNullOrEmpty(action) || player == null) return null;
var mapConfig = config.Maps.Find(c => c.ItemShortname == item.info.shortname && c.SkinID == item.skin);
if (mapConfig != null) {
if (action == "unwrap") {
if (!playerMapData.ContainsKey(player.UserIDString)) playerMapData[player.UserIDString] = new PlayerMapData();
var data = playerMapData[player.UserIDString];
double timeSinceLastOpen = (DateTime.UtcNow - data.LastMapOpenTime).TotalSeconds;
if (timeSinceLastOpen < config.MapOpenCooldown && data.OpenedMapsCount >= config.MaxOpenedMaps) {
int remainingCooldown = (int)(config.MapOpenCooldown - timeSinceLastOpen);
SendReply(player, $"You must wait {remainingCooldown} seconds before opening another map.");
return false;
}
if (timeSinceLastOpen >= config.MapOpenCooldown) {
data.OpenedMapsCount = 0;
data.LastMapOpenTime = DateTime.UtcNow;
}
data.OpenedMapsCount++;
if (data.OpenedMapsCount >= config.MaxOpenedMaps) {
data.LastMapOpenTime = DateTime.UtcNow;
}
ItemByPlayer[player] = item;
item.UseItem(1);
SpawnChest(player, config);
return true;
}
}
var coinConfig = config.Coins.Find(c => c.ItemShortname == item.info.shortname && c.SkinID == item.skin);
if (coinConfig != null) {
if (action == "unwrap") {
if (!playerMapData.ContainsKey(player.UserIDString)) playerMapData[player.UserIDString] = new PlayerMapData();
var data = playerMapData[player.UserIDString];
double timeSinceLastOpen = (DateTime.UtcNow - data.LastCoinOpenTime).TotalSeconds;
if (timeSinceLastOpen < config.CoinOpenCooldown && data.OpenedCoinsCount >= config.MaxOpenedCoins) {
int remainingCooldown = (int)(config.CoinOpenCooldown - timeSinceLastOpen);
SendReply(player, $"You must wait {remainingCooldown} seconds before opening another coin.");
return false;
}
if (timeSinceLastOpen >= config.CoinOpenCooldown) {
data.OpenedCoinsCount = 0;
data.LastCoinOpenTime = DateTime.UtcNow;
}
data.OpenedCoinsCount++;
if (data.OpenedCoinsCount >= config.MaxOpenedCoins) {
data.LastCoinOpenTime = DateTime.UtcNow;
}
ItemByPlayer[player] = item;
item.UseItem(1);
GiveCoinLoot(player, coinConfig);
return true;
}
}
return null;
}
[ChatCommand("givemap")]
private void GiveMapCommand(BasePlayer player, string command, string[] args) {
if (args.Length < 2) {
SendReply(player, "Syntax: /givemap \"<display name>\" <quantity>");
return;
}
string displayName = string.Join(" ", args.Take(args.Length - 1));
int numLootBoxesToGive;
if (!int.TryParse(args[args.Length - 1], out numLootBoxesToGive) || numLootBoxesToGive <= 0) {
SendReply(player, "Invalid quantity.Please specify a positive number.");
return;
}
if (!permission.UserHasPermission(player.UserIDString, "treasuremaps.give")) {
SendReply(player, "You don't have permission to use this command.");
return;
}
var mapConfig = config.Maps.FirstOrDefault(c => c.ItemDisplayName.Equals(displayName, StringComparison.OrdinalIgnoreCase));
if (mapConfig == null) {
SendReply(player, $"Invalid display name or configuration not found for '{displayName}'.");
return;
}
for (int i = 0; i < numLootBoxesToGive;) {
int stackSize = Mathf.Min(numLootBoxesToGive - i, mapConfig.CustomStackSize);
GivePlayerItem(player, mapConfig.ItemShortname, stackSize, mapConfig.SkinID, mapConfig.ItemDisplayName);
i += stackSize;
}
SendReply(player, $"You've received {numLootBoxesToGive} <color=green>{mapConfig.ItemDisplayName}</color> map!");
}
[ChatCommand("givemapto")]
private void GiveMapToCommand(BasePlayer player, string command, string[] args) {
if (args.Length < 3) {
SendReply(player, "Syntax: /givemapto <player ID/name> \"<display name>\" <quantity>");
return;
}
string targetPlayerIdentifier = args[0];
string displayName = string.Join(" ", args.Skip(1).Take(args.Length - 2));
int numLootBoxesToGive;
if (!int.TryParse(args[args.Length - 1], out numLootBoxesToGive) || numLootBoxesToGive <= 0) {
SendReply(player, "Invalid quantity.Please specify a positive number.");
return;
}
if (!permission.UserHasPermission(player.UserIDString, "treasuremaps.give")) {
SendReply(player, "You don't have permission to use this command.");
return;
}
BasePlayer targetPlayer = FindPlayerByIDOrName(targetPlayerIdentifier);
if (targetPlayer == null) {
SendReply(player, $"Player '{targetPlayerIdentifier}' not found.");
return;
}
var mapConfig = config.Maps.FirstOrDefault(c => c.ItemDisplayName.Equals(displayName, StringComparison.OrdinalIgnoreCase));
if (mapConfig == null) {
SendReply(player, $"Invalid display name or configuration not found for '{displayName}'.");
return;
}
for (int i = 0; i < numLootBoxesToGive;) {
int stackSize = Mathf.Min(numLootBoxesToGive - i, mapConfig.CustomStackSize);
GivePlayerItem(targetPlayer, mapConfig.ItemShortname, stackSize, mapConfig.SkinID, mapConfig.ItemDisplayName);
i += stackSize;
}
SendReply(targetPlayer, $"You've received {numLootBoxesToGive} <color=green>{mapConfig.ItemDisplayName}</color> map!");
SendReply(player, $"You've given {numLootBoxesToGive} <color=green>{mapConfig.ItemDisplayName}</color> map to {targetPlayer.displayName}!");
}
[ConsoleCommand("givemapto")]
private void GiveMapConsoleCommand(ConsoleSystem.Arg arg) {
if (arg.Args == null || arg.Args.Length < 3) {
arg.ReplyWith("Syntax: givemapto <player ID/name> \"<display name>\" <quantity>");
return;
}
string targetPlayerIdentifier = arg.Args[0];
string displayName = string.Join(" ", arg.Args.Skip(1).Take(arg.Args.Length - 2));
int numLootBoxesToGive;
if (!int.TryParse(arg.Args[arg.Args.Length - 1], out numLootBoxesToGive) || numLootBoxesToGive <= 0) {
arg.ReplyWith("Invalid quantity.Please specify a positive number.");
return;
}
BasePlayer targetPlayer = FindPlayerByIDOrName(targetPlayerIdentifier);
if (targetPlayer == null) {
arg.ReplyWith($"Player '{targetPlayerIdentifier}' not found.");
return;
}
var mapConfig = config.Maps.FirstOrDefault(c => c.ItemDisplayName.Equals(displayName, StringComparison.OrdinalIgnoreCase));
if (mapConfig == null) {
arg.ReplyWith($"Invalid display name or configuration not found for '{displayName}'.");
return;
}
for (int i = 0; i < numLootBoxesToGive;) {
int stackSize = Mathf.Min(numLootBoxesToGive - i, mapConfig.CustomStackSize);
// ... [Code preview truncated due to length. Full source in attached file] ...