Decompiled source of BuildBeacon v0.2.0

plugins/BuildBeacon.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BuildBeacon.Patches;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("BuildBeacon")]
[assembly: AssemblyDescription("Boss-trophy-powered beacon that discounts build costs in a radius.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BuildBeacon")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("0.2.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.0.0")]
namespace BuildBeacon
{
	public enum StackMode
	{
		Max,
		Sum
	}
	public enum RefundMode
	{
		PaidCost,
		CurrentCost
	}
	public enum TrophyPlacement
	{
		Inset = 1,
		Mounted
	}
	public class BeaconConfig
	{
		public ConfigEntry<bool> Enabled;

		public ConfigEntry<string> LevelPercents;

		public ConfigEntry<float> BossPercent;

		public ConfigEntry<StackMode> Stacking;

		public ConfigEntry<RefundMode> Refund;

		public ConfigEntry<bool> ShowRadius;

		public ConfigEntry<bool> ShowTrophySources;

		public ConfigEntry<bool> ShrinkGreatBeaconTrophies;

		public ConfigEntry<TrophyPlacement> Placement;

		public ConfigEntry<bool> DevMode;

		public ConfigEntry<bool> VerboseLogging;

		public ConfigEntry<float> RadiusAtLevel1;

		public ConfigEntry<float> RadiusPerLevel;

		public ConfigEntry<float> GreatRadiusAtLevel1;

		public ConfigEntry<float> GreatRadiusPerLevel;

		public ConfigEntry<int> MaxLevel;

		public ConfigEntry<float> HolderRange;

		public ConfigEntry<int> BossSlots;

		public ConfigEntry<int> BeaconMobSlots;

		public ConfigEntry<string> BossRules;

		public ConfigEntry<string> MobRules;

		public const string DefaultBossRules = "TrophyEikthyr|Wood\nTrophyEikthyr|Flint\nTrophyEikthyr|Feathers\nTrophyEikthyr|Acorns\nTrophyEikthyr|Dandelion\nTrophyEikthyr|Mushroom\nTrophyEikthyr|Raspberries\nTrophyTheElder|Finewood\nTrophyTheElder|Corewood\nTrophyTheElder|Copper\nTrophyTheElder|Tin\nTrophyTheElder|Bronze\nTrophyTheElder|Bronze Nails\nTrophyTheElder|Blueberries\nTrophyTheElder|Thistle\nTrophyTheElder|Fir Cone\nTrophyTheElder|Pine Cone\nTrophyBonemass|Stone\nTrophyBonemass|Rock\nTrophyBonemass|Iron\nTrophyBonemass|Iron Nails\nTrophyBonemass|Guck\nTrophyBonemass|Chain\nTrophyBonemass|Surtling Core\nTrophyBonemass|Coal\nTrophyDragonQueen|Silver\nTrophyDragonQueen|Silver Necklace\nTrophyDragonQueen|Dragon Tear\nTrophyDragonQueen|Crystal\nTrophyGoblinKing|Black Metal\nTrophyGoblinKing|Tar\nTrophyGoblinKing|Cloudberries\nTrophyGoblinKing|Red Jute\nTrophySeekerQueen|Black Marble\nTrophySeekerQueen|Yggdrasil Wood\nTrophySeekerQueen|Black Core\nTrophySeekerQueen|Refined Eitr\nTrophySeekerQueen|Sap\nTrophySeekerQueen|Wisp\nTrophySeekerQueen|Blue Jute\nTrophySeekerQueen|Majestic Carapace\nTrophySeekerQueen|Sharpening Stone\nTrophyFader|Grausten\nTrophyFader|Warrior Trophy\nTrophyFader|Ashwood\nTrophyFader|Flametal\nTrophyFader|Bloodstone\nTrophyFader|Sulfur\nTrophyFader|Charcoal Resin\nTrophyFader|Proustite Powder\nTrophyFader|Ceramic Plate\nTrophyFader|Pot Shard\nTrophyFader|Kindled Ribs\nTrophyFader|Shield Core\nTrophyFader|Torn Spirit";

		public const string DefaultMobRules = "TrophyGreydwarf|Wood|1\nTrophyGreydwarf|Stone|1\nTrophyGreydwarf|Resin|1\nTrophyGreydwarf|Greydwarf Eye|1\nTrophyGreydwarfBrute|Wood|1\nTrophyGreydwarfBrute|Corewood|1\nTrophyGreydwarfBrute|Resin|1\nTrophyGreydwarfShaman|Finewood|1\nTrophyGreydwarfShaman|Resin|1\nTrophyGreydwarfShaman|Greydwarf Eye|1\nTrophyGreydwarfShaman|Queen Bee|1\nTrophyForestTroll|Finewood|1\nTrophyForestTroll|Corewood|1\nTrophyForestTroll|Stone|1\nTrophyForestTroll|Troll Hide|1\nTrophyBjorn|Wood|1\nTrophyBjorn|Finewood|1\nTrophyBjorn|Corewood|1\nTrophyBjorn|Bear Hide|1\nTrophyBjorn|Bear Paw|1\nTrophySkeleton|Bone Fragments|1\nTrophyGhost|Ectoplasm|1\nTrophySkeletonHildir|Bone Fragments|1\nTrophySkeletonHildir|Coal|1\nTrophySkeletonHildir|Surtling Core|1\nTrophyBoar|Leather Scraps|1\nTrophyDeer|Deer Hide|1\nTrophyDeer|Leather Scraps|1\nTrophyBlob|Guck|1\nTrophyDraugr|Ancient Bark|1\nTrophyDraugr|Iron|1\nTrophyDraugrElite|Ancient Bark|1\nTrophyDraugrElite|Iron|1\nTrophyDraugrElite|Chain|1\nTrophyLeech|Bloodbag|1\nTrophyWraith|Chain|1\nTrophyWraith|Ectoplasm|1\nTrophyAbomination|Ancient Bark|1\nTrophySurtling|Surtling Core|1\nTrophySurtling|Coal|1\nTrophySkeletonPoison|Bone Fragments|1\nTrophyKvastur|Guck|1\nTrophyWolf|Wolf Pelt|1\nTrophyWolf|Silver|1\nTrophyFenring|Fenris Claw|1\nTrophyFenring|Wolf Pelt|1\nTrophyHatchling|Obsidian|1\nTrophyHatchling|Crystal|1\nTrophySGolem|Stone|1\nTrophySGolem|Crystal|1\nTrophySGolem|Obsidian|1\nTrophySGolem|Silver|1\nTrophyCultist|Fenris Claw|1\nTrophyUlv|Scale Hide|1\nTrophyUlv|Wolf Pelt|1\nTrophyCultist_Hildir|Obsidian|1\nTrophyGoblin|Black Metal|1\nTrophyGoblinShaman|Tar|1\nTrophyGoblinBrute|Black Metal|1\nTrophyLox|Lox Pelt|1\nTrophyGrowth|Tar|1\nTrophyGoblinBruteBrosBrute|Lox Pelt|1\nTrophyGoblinBruteBrosShaman|Tar|1\nTrophyBjornUndead|Bear Hide|1\nTrophyBjornUndead|Bear Paw|1\nTrophySeeker|Yggdrasil Wood|1\nTrophyTick|Yggdrasil Wood|1\nTrophySeekerBrute|Black Marble|1\nTrophyDvergr|Black Marble|1\nTrophyDvergr|Mechanical Spring|1\nTrophyDvergr|Dvergr Extractor|1\nTrophyDvergr|Dvergr Lantern|1\nTrophyGjall|Yggdrasil Wood|1\nTrophyGjall|Black Marble|1\nTrophyCharredMelee|Ashwood|1\nTrophyCharredMelee|Charred Bone|1\nTrophyCharredMelee|Charred Skull|1\nTrophyCharredMelee|Warrior Trophy|1\nTrophyCharredArcher|Ashwood|1\nTrophyCharredArcher|Charred Bone|1\nTrophyCharredArcher|Charred Skull|1\nTrophyCharredMage|Bloodgold|1\nTrophyCharredMage|Charred Cogwheel|1\nTrophyMorgen|Grausten|1\nTrophyMorgen|Ashwood|1\nTrophyMorgen|Morgen Sinew|1\nTrophyAsksvin|Grausten|1\nTrophyAsksvin|Asksvin Hide|1\nTrophyAsksvin|Asksvin Neck|1\nTrophyAsksvin|Asksvin Pelvis|1\nTrophyAsksvin|Asksvin Ribcage|1\nTrophyAsksvin|Asksvin Skull|1\nTrophyBlob_Lava|Grausten|1\nTrophyBlob_Lava|Flametal|1\nTrophyBlob_Lava|Molten Core|1\nTrophyVolture|Charred Bone|1\nTrophyVolture|Charred Skull|1\nTrophyFallenValkyrie|Celestial Feather|1\nTrophyFallenValkyrie|Bloodgold|1\nTrophyMoose|Timberwood|1\nTrophyMoose|Moose Hide|1\nTrophyMoose|Moose Sinew|1\nTrophyBarka|Timberwood|1\nTrophyBarka|Ice|1\nTrophyBlob_Frost|Frostcore|1\nTrophyBlob_Frost|Ice|1\nTrophySeal|Seal Pelt|1\nTrophyWrithan|Writhan Roots|1\nTrophyWrithan|Timberwood|1\nTrophyJotunWitch|Ice|1\nTrophyJotunWarrior|Frostcore|1\nTrophyElaking|Elaking Hair Bundle|1";

		public event Action SettingChanged;

		public BeaconConfig(ConfigFile cfg)
		{
			Enabled = ConfigUtil.Synced(cfg, "1. General", "Enabled", def: true, "Master switch.");
			LevelPercents = ConfigUtil.Synced(cfg, "1. General", "LevelPercents", "50, 80, 90", "Percentage off at each creature discount level, level 1 first; the number of entries is the top level. The levels of a beacon's creature trophies add up and stop at the top level. The default 50, 80, 90 makes each item go 2x, 5x and 10x as far.");
			if (LevelPercents.Value.Replace(" ", "") == "25,60,80,90,95")
			{
				LevelPercents.Value = (string)((ConfigEntryBase)LevelPercents).DefaultValue;
			}
			BossPercent = ConfigUtil.Synced(cfg, "1. General", "BossPercent", 100f, "Percentage off a slotted boss trophy takes from its materials. 100 = free, no gathering needed. 95 = each item goes 20x as far, so building still takes some gathering. Keep it at or above the top of LevelPercents: a boss's materials take the boss's discount, never a creature's.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(50f, 100f));
			Stacking = ConfigUtil.Synced(cfg, "1. General", "Stacking", StackMode.Max, "How overlapping beacons' creature discounts combine. Max = the beacon with the highest level counts. Sum = their levels add up, up to the top level.");
			Refund = ConfigUtil.Synced(cfg, "1. General", "Refund", RefundMode.PaidCost, "PaidCost = refund what was actually paid. CurrentCost = refund current discounted cost.");
			ShowRadius = ConfigUtil.Local(cfg, "1. General", "ShowRadius", def: true, "Show the radius ring around beacons while holding a build tool (hammer, hoe, cultivator).");
			ShowTrophySources = ConfigUtil.Local(cfg, "1. General", "ShowTrophySources", def: true, "In the beacon panel, hovering a material on the Discounts tab lists every trophy that can discount it, including ones you have not slotted yet. Turn off to find them out for yourself.");
			ShrinkGreatBeaconTrophies = ConfigUtil.Local(cfg, "1. General", "ShrinkGreatBeaconTrophies", def: true, "Size the boss trophies in the Great Beacon's alcoves to fit around the pillar: Eikthyr, the Elder and Moder at vanilla size, Bonemass at 75%, the Queen, Yagluth and Fader at 65%. Off: all at vanilla size.");
			Placement = ConfigUtil.Local(cfg, "1. General", "TrophyPlacement", TrophyPlacement.Mounted, "How slotted trophies sit in the beacon's alcoves. Inset (1) = scaled down to fit inside the alcove. Mounted (2) = vanilla item stand size, back set into the alcove and the rest standing out of it.");
			RadiusAtLevel1 = ConfigUtil.Synced(cfg, "2. Radius & Slots", "RadiusAtLevel1", 30f, "Radius at level 1, with no boss holders linked (m).");
			RadiusPerLevel = ConfigUtil.Synced(cfg, "2. Radius & Slots", "RadiusPerLevel", 10f, "Extra radius per level (m). Each linked boss holder adds a level.");
			GreatRadiusAtLevel1 = ConfigUtil.Synced(cfg, "2. Radius & Slots", "GreatRadiusAtLevel1", 65f, "The Great Beacon's radius at level 1 (m).");
			GreatRadiusPerLevel = ConfigUtil.Synced(cfg, "2. Radius & Slots", "GreatRadiusPerLevel", 5f, "The Great Beacon's extra radius per level (m). Each boss trophy in its alcoves, linked holder and linked rack adds a level.");
			MaxLevel = ConfigUtil.Synced(cfg, "2. Radius & Slots", "MaxLevel", 8, "Highest beacon level; holders beyond MaxLevel - 1 add none.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 32));
			HolderRange = ConfigUtil.Synced(cfg, "2. Radius & Slots", "HolderRange", 30f, "How far a boss holder may stand from its beacon (m). A holder links to the closest beacon within this distance.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 100f));
			BossSlots = ConfigUtil.Synced(cfg, "2. Radius & Slots", "BossSlots", 8, "Boss trophy slots per beacon. Each boss trophy can be slotted once.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 16));
			BeaconMobSlots = ConfigUtil.Synced(cfg, "2. Radius & Slots", "BeaconMobSlots", 4, "Creature trophy slots in the beacon itself. Fixed: bosses do not add any; each trophy rack adds four of its own to the beacon's network.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 32));
			BossRules = ConfigUtil.Synced(cfg, "3. Boss Rules", "BossRules", "TrophyEikthyr|Wood\nTrophyEikthyr|Flint\nTrophyEikthyr|Feathers\nTrophyEikthyr|Acorns\nTrophyEikthyr|Dandelion\nTrophyEikthyr|Mushroom\nTrophyEikthyr|Raspberries\nTrophyTheElder|Finewood\nTrophyTheElder|Corewood\nTrophyTheElder|Copper\nTrophyTheElder|Tin\nTrophyTheElder|Bronze\nTrophyTheElder|Bronze Nails\nTrophyTheElder|Blueberries\nTrophyTheElder|Thistle\nTrophyTheElder|Fir Cone\nTrophyTheElder|Pine Cone\nTrophyBonemass|Stone\nTrophyBonemass|Rock\nTrophyBonemass|Iron\nTrophyBonemass|Iron Nails\nTrophyBonemass|Guck\nTrophyBonemass|Chain\nTrophyBonemass|Surtling Core\nTrophyBonemass|Coal\nTrophyDragonQueen|Silver\nTrophyDragonQueen|Silver Necklace\nTrophyDragonQueen|Dragon Tear\nTrophyDragonQueen|Crystal\nTrophyGoblinKing|Black Metal\nTrophyGoblinKing|Tar\nTrophyGoblinKing|Cloudberries\nTrophyGoblinKing|Red Jute\nTrophySeekerQueen|Black Marble\nTrophySeekerQueen|Yggdrasil Wood\nTrophySeekerQueen|Black Core\nTrophySeekerQueen|Refined Eitr\nTrophySeekerQueen|Sap\nTrophySeekerQueen|Wisp\nTrophySeekerQueen|Blue Jute\nTrophySeekerQueen|Majestic Carapace\nTrophySeekerQueen|Sharpening Stone\nTrophyFader|Grausten\nTrophyFader|Warrior Trophy\nTrophyFader|Ashwood\nTrophyFader|Flametal\nTrophyFader|Bloodstone\nTrophyFader|Sulfur\nTrophyFader|Charcoal Resin\nTrophyFader|Proustite Powder\nTrophyFader|Ceramic Plate\nTrophyFader|Pot Shard\nTrophyFader|Kindled Ribs\nTrophyFader|Shield Core\nTrophyFader|Torn Spirit", "Managed by BepInEx/config/BuildBeacon.BossRules.txt on the server; edit that file instead. trophyPrefab|material, one per line. A slotted boss trophy takes BossPercent off the material within the radius (100: free). Material matches the item's prefab name or display name, spaces and case ignored.");
			MobRules = ConfigUtil.Synced(cfg, "4. Mob Rules", "MobRules", "TrophyGreydwarf|Wood|1\nTrophyGreydwarf|Stone|1\nTrophyGreydwarf|Resin|1\nTrophyGreydwarf|Greydwarf Eye|1\nTrophyGreydwarfBrute|Wood|1\nTrophyGreydwarfBrute|Corewood|1\nTrophyGreydwarfBrute|Resin|1\nTrophyGreydwarfShaman|Finewood|1\nTrophyGreydwarfShaman|Resin|1\nTrophyGreydwarfShaman|Greydwarf Eye|1\nTrophyGreydwarfShaman|Queen Bee|1\nTrophyForestTroll|Finewood|1\nTrophyForestTroll|Corewood|1\nTrophyForestTroll|Stone|1\nTrophyForestTroll|Troll Hide|1\nTrophyBjorn|Wood|1\nTrophyBjorn|Finewood|1\nTrophyBjorn|Corewood|1\nTrophyBjorn|Bear Hide|1\nTrophyBjorn|Bear Paw|1\nTrophySkeleton|Bone Fragments|1\nTrophyGhost|Ectoplasm|1\nTrophySkeletonHildir|Bone Fragments|1\nTrophySkeletonHildir|Coal|1\nTrophySkeletonHildir|Surtling Core|1\nTrophyBoar|Leather Scraps|1\nTrophyDeer|Deer Hide|1\nTrophyDeer|Leather Scraps|1\nTrophyBlob|Guck|1\nTrophyDraugr|Ancient Bark|1\nTrophyDraugr|Iron|1\nTrophyDraugrElite|Ancient Bark|1\nTrophyDraugrElite|Iron|1\nTrophyDraugrElite|Chain|1\nTrophyLeech|Bloodbag|1\nTrophyWraith|Chain|1\nTrophyWraith|Ectoplasm|1\nTrophyAbomination|Ancient Bark|1\nTrophySurtling|Surtling Core|1\nTrophySurtling|Coal|1\nTrophySkeletonPoison|Bone Fragments|1\nTrophyKvastur|Guck|1\nTrophyWolf|Wolf Pelt|1\nTrophyWolf|Silver|1\nTrophyFenring|Fenris Claw|1\nTrophyFenring|Wolf Pelt|1\nTrophyHatchling|Obsidian|1\nTrophyHatchling|Crystal|1\nTrophySGolem|Stone|1\nTrophySGolem|Crystal|1\nTrophySGolem|Obsidian|1\nTrophySGolem|Silver|1\nTrophyCultist|Fenris Claw|1\nTrophyUlv|Scale Hide|1\nTrophyUlv|Wolf Pelt|1\nTrophyCultist_Hildir|Obsidian|1\nTrophyGoblin|Black Metal|1\nTrophyGoblinShaman|Tar|1\nTrophyGoblinBrute|Black Metal|1\nTrophyLox|Lox Pelt|1\nTrophyGrowth|Tar|1\nTrophyGoblinBruteBrosBrute|Lox Pelt|1\nTrophyGoblinBruteBrosShaman|Tar|1\nTrophyBjornUndead|Bear Hide|1\nTrophyBjornUndead|Bear Paw|1\nTrophySeeker|Yggdrasil Wood|1\nTrophyTick|Yggdrasil Wood|1\nTrophySeekerBrute|Black Marble|1\nTrophyDvergr|Black Marble|1\nTrophyDvergr|Mechanical Spring|1\nTrophyDvergr|Dvergr Extractor|1\nTrophyDvergr|Dvergr Lantern|1\nTrophyGjall|Yggdrasil Wood|1\nTrophyGjall|Black Marble|1\nTrophyCharredMelee|Ashwood|1\nTrophyCharredMelee|Charred Bone|1\nTrophyCharredMelee|Charred Skull|1\nTrophyCharredMelee|Warrior Trophy|1\nTrophyCharredArcher|Ashwood|1\nTrophyCharredArcher|Charred Bone|1\nTrophyCharredArcher|Charred Skull|1\nTrophyCharredMage|Bloodgold|1\nTrophyCharredMage|Charred Cogwheel|1\nTrophyMorgen|Grausten|1\nTrophyMorgen|Ashwood|1\nTrophyMorgen|Morgen Sinew|1\nTrophyAsksvin|Grausten|1\nTrophyAsksvin|Asksvin Hide|1\nTrophyAsksvin|Asksvin Neck|1\nTrophyAsksvin|Asksvin Pelvis|1\nTrophyAsksvin|Asksvin Ribcage|1\nTrophyAsksvin|Asksvin Skull|1\nTrophyBlob_Lava|Grausten|1\nTrophyBlob_Lava|Flametal|1\nTrophyBlob_Lava|Molten Core|1\nTrophyVolture|Charred Bone|1\nTrophyVolture|Charred Skull|1\nTrophyFallenValkyrie|Celestial Feather|1\nTrophyFallenValkyrie|Bloodgold|1\nTrophyMoose|Timberwood|1\nTrophyMoose|Moose Hide|1\nTrophyMoose|Moose Sinew|1\nTrophyBarka|Timberwood|1\nTrophyBarka|Ice|1\nTrophyBlob_Frost|Frostcore|1\nTrophyBlob_Frost|Ice|1\nTrophySeal|Seal Pelt|1\nTrophyWrithan|Writhan Roots|1\nTrophyWrithan|Timberwood|1\nTrophyJotunWitch|Ice|1\nTrophyJotunWarrior|Frostcore|1\nTrophyElaking|Elaking Hair Bundle|1", "Managed by BepInEx/config/BuildBeacon.MobRules.txt on the server; edit that file instead. trophyPrefab|material|level, one per line, and trophyPrefab|Stackable for trophies whose every copy counts. Levels add up across a beacon's trophies; see LevelPercents.");
			DevMode = ConfigUtil.Local(cfg, "5. Dev", "DevMode", def: false, "For mod development. When on, spawning into a world this machine hosts turns on devcommands, god mode and debug fly. Does nothing on dedicated servers or when joining someone else's world.");
			VerboseLogging = ConfigUtil.Local(cfg, "5. Dev", "VerboseLogging", def: false, "For mod development. Logs the details of registering the pieces (materials, effects, snap points), how each trophy is fitted in its alcove, and the [diag] lines (placements, snapping, slow inventory changes). Off: only what players need.");
			cfg.SettingChanged += delegate
			{
				this.SettingChanged?.Invoke();
			};
		}
	}
	public class BeaconController : MonoBehaviour, Interactable, Hoverable
	{
		private sealed class Shard
		{
			public Transform T;

			public Vector3 RestPos;

			public Vector3 AxisLocal;

			public Quaternion RestRot;

			public float Speed;

			public float Period;

			public float Phase;

			public float Angle;
		}

		private const string KeyTrophies = "xai_beacon_trophies";

		private const float InteractRange = 6f;

		private const string RpcRequestOpen = "xai_beacon_request_open";

		private const string RpcOpenResponse = "xai_beacon_open_response";

		private const string RpcRequestOwn = "xai_beacon_request_own";

		private const float QueueTimeout = 2f;

		private const float DropHeight = 1.2f;

		public CircleProjector m_areaMarker;

		public GameObject m_activeEffect;

		public TrophyKind m_ownSlotKind = TrophyKind.Mob;

		public int m_ownSlotCount;

		private ZNetView _nview;

		private BeaconTrophyDisplay _display;

		private bool _inUse;

		private ItemData _queuedItem;

		private float _queuedAt;

		private readonly Dictionary<string, int> _trophies = new Dictionary<string, int>();

		private readonly Dictionary<string, int> _effective = new Dictionary<string, int>();

		private readonly List<(string prefab, BossHolder holder)> _bosses = new List<(string, BossHolder)>();

		private readonly List<MobRack> _racks = new List<MobRack>();

		private readonly List<(MobRack rack, int slot, string prefab, bool counted)> _rackTrophies = new List<(MobRack, int, string, bool)>();

		private static int s_toolFrame = -1;

		private static bool s_holdingTool;

		private CraftingStation _station;

		private const float SpinDegreesPerSecond = 18f;

		private const float BobAmplitude = 0.02f;

		private const float BobPeriod = 4f;

		private const float MotionEaseSeconds = 1.5f;

		private Transform _crystal;

		private Vector3 _crystalRestPos;

		private Quaternion _crystalRestRot;

		private float _bobPhase;

		private float _motion;

		private float _spin;

		private bool _lit;

		private const string ShardMarker = "_shard_";

		private const float ShardBobAmplitude = 0.05f;

		private readonly List<Shard> _shards = new List<Shard>();

		private const string CoreMaterial = "BeaconCore";

		private static readonly int ColorId = Shader.PropertyToID("_Color");

		private static readonly int EmissionId = Shader.PropertyToID("_EmissionColor");

		private Renderer _coreRenderer;

		private int _coreIndex = -1;

		private bool _coreSearched;

		private bool? _coreLit;

		private MaterialPropertyBlock _coreBlock;

		public bool IsGreat => m_ownSlotKind == TrophyKind.Boss;

		public bool IsValid
		{
			get
			{
				if ((Object)(object)_nview != (Object)null)
				{
					return _nview.IsValid();
				}
				return false;
			}
		}

		public IReadOnlyDictionary<string, int> Trophies => _effective;

		public IReadOnlyList<(string prefab, BossHolder holder)> BossEntries => _bosses;

		public int BossCount => _bosses.Count;

		public int MobCount => _trophies.Where((KeyValuePair<string, int> kv) => !DiscountRules.IsBossTrophy(kv.Key)).Sum((KeyValuePair<string, int> kv) => kv.Value);

		public int TrophyCount => _effective.Values.Sum();

		public int HolderCount { get; private set; }

		public int RackCount => _racks.Count;

		public IReadOnlyList<(MobRack rack, int slot, string prefab, bool counted)> RackTrophies => _rackTrophies;

		public int RackSlots => RackCount * 4;

		public int RackTrophyCount => _rackTrophies.Count;

		public string MobSlotsText => $"{MobCount + RackTrophyCount}/{MobSlots + RackSlots}";

		public int Level => DiscountRules.LevelFor(OwnBossLevels + HolderCount + RackCount);

		public int MaxLevel => BuildBeaconPlugin.Cfg.MaxLevel.Value;

		public float Radius
		{
			get
			{
				if (!IsGreat)
				{
					return DiscountRules.RadiusFor(Level);
				}
				return DiscountRules.GreatRadiusFor(Level);
			}
		}

		public int BossSlots => BuildBeaconPlugin.Cfg.BossSlots.Value;

		public int MobSlots
		{
			get
			{
				if (!IsGreat)
				{
					return BuildBeaconPlugin.Cfg.BeaconMobSlots.Value;
				}
				return 0;
			}
		}

		public int OwnBossSlots
		{
			get
			{
				if (!IsGreat)
				{
					return 0;
				}
				return m_ownSlotCount;
			}
		}

		public int OwnBossCount => _trophies.Where((KeyValuePair<string, int> kv) => DiscountRules.IsBossTrophy(kv.Key)).Sum((KeyValuePair<string, int> kv) => kv.Value);

		private int OwnBossLevels
		{
			get
			{
				if (!IsGreat)
				{
					return 0;
				}
				return _bosses.Count(((string prefab, BossHolder holder) b) => (Object)(object)b.holder == (Object)null);
			}
		}

		public static bool HoldingBuildTool
		{
			get
			{
				if (s_toolFrame == Time.frameCount)
				{
					return s_holdingTool;
				}
				s_toolFrame = Time.frameCount;
				Player localPlayer = Player.m_localPlayer;
				ItemData val = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetRightItem() : null);
				s_holdingTool = val != null && (Object)(object)val.m_shared.m_buildPieces != (Object)null;
				return s_holdingTool;
			}
		}

		public string PieceNameKey
		{
			get
			{
				if (!IsGreat)
				{
					return "$piece_xai_beacon";
				}
				return "$piece_xai_great_beacon";
			}
		}

		public IEnumerable<string> SlotsOf(TrophyKind kind)
		{
			return (from kv in _trophies
				where DiscountRules.KindOf(kv.Key) == kind || (kind == TrophyKind.Mob && DiscountRules.KindOf(kv.Key) == TrophyKind.None)
				orderby kv.Key
				select kv).SelectMany((KeyValuePair<string, int> kv) => Enumerable.Repeat(kv.Key, kv.Value));
		}

		public bool InRangeOf(Player p)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)p != (Object)null)
			{
				return Vector3.Distance(((Component)p).transform.position, ((Component)this).transform.position) <= 6f;
			}
			return false;
		}

		private void Awake()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			_nview = ((Component)this).GetComponent<ZNetView>();
			if (IsValid)
			{
				_display = new BeaconTrophyDisplay(((Component)this).transform, IsGreat ? new Vector2?(BeaconTrophyDisplay.GreatTrophyFit) : ((Vector2?)null));
				WearNTear component = ((Component)this).GetComponent<WearNTear>();
				if ((Object)(object)component != (Object)null)
				{
					component.m_onDestroyed = (Action)Delegate.Combine(component.m_onDestroyed, new Action(OnDestroyed));
				}
				_nview.Register<long>("xai_beacon_request_open", (Action<long, long>)RPC_RequestOpen);
				_nview.Register<bool>("xai_beacon_open_response", (Action<long, bool>)RPC_OpenResponse);
				_nview.Register("xai_beacon_request_own", (Action<long>)RPC_RequestOwn);
				_crystal = ((Component)this).transform.Find("crystal");
				if ((Object)(object)_crystal != (Object)null)
				{
					_crystalRestPos = _crystal.localPosition;
					_crystalRestRot = _crystal.localRotation;
					_bobPhase = Random.value * 4f;
					FindShards();
				}
				LoadFromZdo();
				BeaconRegistry.Register(this);
				((MonoBehaviour)this).InvokeRepeating("LoadFromZdo", 1f, 1f);
			}
		}

		private void OnDestroy()
		{
			BeaconRegistry.Unregister(this);
		}

		private void Update()
		{
			UpdateRing();
			AnimateCrystal();
		}

		private void UpdateRing()
		{
			if (!((Object)(object)m_areaMarker == (Object)null))
			{
				bool flag = BuildBeaconPlugin.Cfg.ShowRadius.Value && HoldingBuildTool;
				if (flag)
				{
					m_areaMarker.m_radius = Radius;
					m_areaMarker.m_nrOfSegments = Mathf.Max(20, Mathf.RoundToInt(Radius * 2f));
				}
				if (((Component)m_areaMarker).gameObject.activeSelf != flag)
				{
					((Component)m_areaMarker).gameObject.SetActive(flag);
				}
			}
		}

		private void OnDestroyed()
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			if (!IsValid || !_nview.IsOwner())
			{
				return;
			}
			LoadFromZdo();
			int num = 0;
			foreach (KeyValuePair<string, int> item in _trophies.ToList())
			{
				num += DropTrophy(item.Key, item.Value);
				_trophies.Remove(item.Key);
			}
			if (num > 0)
			{
				SaveToZdo();
				BuildBeaconPlugin.Log.LogInfo((object)$"Beacon destroyed at {((Component)this).transform.position:F1}: dropped {num} trophies");
			}
		}

		private int DropTrophy(string prefab, int count)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			return SpawnItems(prefab, count, ((Component)this).transform.position + Vector3.up * 1.2f);
		}

		public static int SpawnItems(string prefab, int count, Vector3 at)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			ItemDrop val = DiscountRules.ItemDropFor(prefab);
			if ((Object)(object)val == (Object)null)
			{
				BuildBeaconPlugin.Log.LogWarning((object)$"No item prefab \"{prefab}\"; {count} lost");
				return 0;
			}
			int num = Mathf.Max(1, val.m_itemData.m_shared.m_maxStackSize);
			int num2 = count;
			while (num2 > 0)
			{
				int num3 = Mathf.Min(num2, num);
				Vector3 val2 = Random.insideUnitSphere * 0.3f;
				val2.y = Mathf.Abs(val2.y);
				ItemDrop component = Object.Instantiate<GameObject>(((Component)val).gameObject, at + val2, Quaternion.identity).GetComponent<ItemDrop>();
				component.SetStack(num3);
				ItemDrop.OnCreateNew(component, false);
				num2 -= num3;
			}
			return count;
		}

		public void Resync()
		{
			LoadFromZdo();
		}

		private void RebuildEffective()
		{
			_effective.Clear();
			_bosses.Clear();
			foreach (KeyValuePair<string, int> trophy in _trophies)
			{
				if (trophy.Value > 0 && !DiscountRules.IsBossTrophy(trophy.Key))
				{
					_effective[trophy.Key] = trophy.Value;
				}
			}
			List<(string, BossHolder)> list = new List<(string, BossHolder)>();
			foreach (KeyValuePair<string, int> trophy2 in _trophies)
			{
				if (trophy2.Value > 0 && DiscountRules.IsBossTrophy(trophy2.Key))
				{
					list.Add((trophy2.Key, null));
				}
			}
			int num = 0;
			foreach (BossHolder item in BossHolder.All)
			{
				if (!((Object)(object)item == (Object)null) && item.IsValid && !((Object)(object)item.LinkedBeacon != (Object)(object)this))
				{
					num++;
					if (item.HasTrophy)
					{
						list.Add((item.Trophy, item));
					}
				}
			}
			HolderCount = num;
			RebuildRacks();
			foreach (var c in from tuple in list
				orderby DiscountRules.BossOrder(tuple.prefab), (!((Object)(object)tuple.holder == (Object)null)) ? 1 : 0
				select tuple)
			{
				if (_bosses.Count >= BossSlots)
				{
					break;
				}
				if (DiscountRules.IsBossTrophy(c.Item1) && !_bosses.Any(((string prefab, BossHolder holder) b) => b.prefab == c.Item1))
				{
					_bosses.Add(c);
					_effective[c.Item1] = 1;
				}
			}
		}

		private void RebuildRacks()
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			_racks.Clear();
			_rackTrophies.Clear();
			foreach (MobRack item in MobRack.All)
			{
				if ((Object)(object)item != (Object)null && item.IsValid && (Object)(object)item.LinkedBeacon == (Object)(object)this)
				{
					_racks.Add(item);
				}
			}
			Vector3 here = ((Component)this).transform.position;
			_racks.Sort(delegate(MobRack a, MobRack b)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = ((Component)a).transform.position - here;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				val = ((Component)b).transform.position - here;
				return sqrMagnitude.CompareTo(((Vector3)(ref val)).sqrMagnitude);
			});
			foreach (MobRack rack in _racks)
			{
				for (int num = 0; num < 4; num++)
				{
					string text = rack.Trophy(num);
					if (text.Length != 0)
					{
						int value;
						bool flag = DiscountRules.KindOf(text) == TrophyKind.Mob && (DiscountRules.IsStackable(text) || !_effective.TryGetValue(text, out value) || value <= 0);
						if (flag)
						{
							_effective.TryGetValue(text, out var value2);
							_effective[text] = value2 + 1;
						}
						_rackTrophies.Add((rack, num, text, flag));
					}
				}
			}
		}

		public bool CountsRackSlot(MobRack rack, int slot)
		{
			foreach (var rackTrophy in _rackTrophies)
			{
				if ((Object)(object)rackTrophy.rack == (Object)(object)rack && rackTrophy.slot == slot)
				{
					return rackTrophy.counted;
				}
			}
			return false;
		}

		public bool RackAddsLevel(MobRack rack)
		{
			int num = MaxLevel - 1 - OwnBossLevels - HolderCount;
			int num2 = _racks.IndexOf(rack);
			if (num2 >= 0)
			{
				return num2 < num;
			}
			return false;
		}

		private bool NetworkHas(string prefab, MobRack exceptRack = null, int exceptSlot = -1)
		{
			if (_trophies.TryGetValue(prefab, out var value) && value > 0)
			{
				return true;
			}
			foreach (var rackTrophy in _rackTrophies)
			{
				if (rackTrophy.prefab == prefab && (!((Object)(object)rackTrophy.rack == (Object)(object)exceptRack) || rackTrophy.slot != exceptSlot))
				{
					return true;
				}
			}
			return false;
		}

		public string RackInsertBlockedReason(string prefab, MobRack rack, int slot)
		{
			RebuildEffective();
			switch (DiscountRules.KindOf(prefab))
			{
			case TrophyKind.Boss:
				return "$xai_rack_mob_only";
			case TrophyKind.Mob:
				if (DiscountRules.IsStackable(prefab) || !NetworkHas(prefab, rack, slot))
				{
					return null;
				}
				return "$xai_beacon_mob_duplicate";
			default:
				return "$xai_beacon_not_a_trophy";
			}
		}

		public bool CountsHolder(BossHolder holder)
		{
			return _bosses.Any(((string prefab, BossHolder holder) b) => (Object)(object)b.holder == (Object)(object)holder);
		}

		public string HolderInsertBlockedReason(string prefab, BossHolder holder)
		{
			RebuildEffective();
			List<(string, BossHolder)> list = _bosses.Where(((string prefab, BossHolder holder) b) => (Object)(object)b.holder != (Object)(object)holder || (Object)(object)holder == (Object)null).ToList();
			if (list.Any<(string, BossHolder)>(((string prefab, BossHolder holder) b) => b.prefab == prefab))
			{
				return "$xai_beacon_boss_duplicate";
			}
			if (list.Count >= BossSlots)
			{
				return "$xai_beacon_boss_full";
			}
			return null;
		}

		public void PokeStation()
		{
			if ((Object)(object)_station == (Object)null)
			{
				_station = ((Component)this).GetComponent<CraftingStation>();
			}
			if ((Object)(object)_station != (Object)null)
			{
				_station.PokeInUse();
			}
		}

		private void LoadFromZdo()
		{
			_trophies.Clear();
			if (ZdoUtil.TryGetPackage(_nview, "xai_beacon_trophies", out var pkg))
			{
				int num = pkg.ReadInt();
				for (int i = 0; i < num; i++)
				{
					_trophies[pkg.ReadString()] = pkg.ReadInt();
				}
			}
			RebuildEffective();
			UpdateVisuals();
		}

		private void SaveToZdo()
		{
			ZdoUtil.SetPackage(_nview, "xai_beacon_trophies", delegate(ZPackage pkg)
			{
				pkg.Write(_trophies.Count);
				foreach (KeyValuePair<string, int> trophy in _trophies)
				{
					pkg.Write(trophy.Key);
					pkg.Write(trophy.Value);
				}
			});
			RebuildEffective();
			UpdateVisuals();
		}

		private void UpdateVisuals()
		{
			UpdateRing();
			bool flag = (_lit = TrophyCount > 0);
			if ((Object)(object)m_activeEffect != (Object)null && m_activeEffect.activeSelf != flag)
			{
				m_activeEffect.SetActive(flag);
			}
			SetCoreLit(flag);
			_display?.Refresh(_trophies);
		}

		private void AnimateCrystal()
		{
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_crystal == (Object)null))
			{
				float num = (_lit ? 1f : 0f);
				if (_motion != 0f || num != 0f)
				{
					_motion = Mathf.MoveTowards(_motion, num, Time.deltaTime / 1.5f);
					float num2 = Mathf.SmoothStep(0f, 1f, _motion);
					_spin = (_spin + 18f * num2 * Time.deltaTime) % 360f;
					float num3 = Mathf.Sin((Time.time + _bobPhase) * ((float)Math.PI / 2f)) * 0.02f * num2;
					_crystal.localPosition = _crystalRestPos + Vector3.up * num3;
					_crystal.localRotation = _crystalRestRot * Quaternion.Euler(0f, _spin, 0f);
					AnimateShards(num2);
				}
			}
		}

		private void FindShards()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			Transform[] componentsInChildren = ((Component)_crystal).GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (((Object)val).name.IndexOf("_shard_", StringComparison.Ordinal) >= 0)
				{
					MeshFilter component = ((Component)val).GetComponent<MeshFilter>();
					Vector3 val2;
					if (!((Object)(object)component != (Object)null) || !((Object)(object)component.sharedMesh != (Object)null))
					{
						val2 = Vector3.up;
					}
					else
					{
						Bounds bounds = component.sharedMesh.bounds;
						val2 = ((Bounds)(ref bounds)).size;
					}
					Vector3 val3 = val2;
					Vector3 axisLocal = ((val3.x >= val3.y && val3.x >= val3.z) ? Vector3.right : ((val3.y >= val3.z) ? Vector3.up : Vector3.forward));
					int count = _shards.Count;
					_shards.Add(new Shard
					{
						T = val,
						RestPos = val.localPosition,
						RestRot = val.localRotation,
						AxisLocal = axisLocal,
						Speed = (40f + 11f * (float)count) * ((count % 2 == 0) ? 1f : (-1f)),
						Period = 2.5f + 0.37f * (float)count,
						Phase = 1.7f * (float)count + Random.value
					});
				}
			}
		}

		private void AnimateShards(float ease)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			foreach (Shard shard in _shards)
			{
				if (!((Object)(object)shard.T == (Object)null))
				{
					shard.Angle = (shard.Angle + shard.Speed * ease * Time.deltaTime) % 360f;
					float num = Mathf.Sin((Time.time + shard.Phase) * ((float)Math.PI * 2f / shard.Period)) * 0.05f * ease;
					shard.T.localRotation = shard.RestRot * Quaternion.AngleAxis(shard.Angle, shard.AxisLocal);
					shard.T.localPosition = shard.RestPos + shard.RestRot * shard.AxisLocal * num;
				}
			}
		}

		private void SetCoreLit(bool lit)
		{
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Expected O, but got Unknown
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			if (_coreLit == lit)
			{
				return;
			}
			if (!_coreSearched)
			{
				_coreSearched = true;
				MeshRenderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<MeshRenderer>(true);
				foreach (MeshRenderer val in componentsInChildren)
				{
					Material[] sharedMaterials = ((Renderer)val).sharedMaterials;
					for (int j = 0; j < sharedMaterials.Length; j++)
					{
						if ((Object)(object)sharedMaterials[j] != (Object)null && ((Object)sharedMaterials[j]).name.StartsWith("BeaconCore"))
						{
							_coreRenderer = (Renderer)(object)val;
							_coreIndex = j;
							break;
						}
					}
					if ((Object)(object)_coreRenderer != (Object)null)
					{
						break;
					}
				}
				if ((Object)(object)_coreRenderer == (Object)null)
				{
					BuildBeaconPlugin.Log.LogWarning((object)"Beacon: no BeaconCore material on the model; the crystal's core cannot follow the lit state");
				}
			}
			_coreLit = lit;
			if (!((Object)(object)_coreRenderer == (Object)null))
			{
				if (_coreBlock == null)
				{
					_coreBlock = new MaterialPropertyBlock();
				}
				_coreBlock.Clear();
				if (!lit)
				{
					Material val2 = _coreRenderer.sharedMaterials[_coreIndex];
					Color val3 = (((Object)(object)val2 != (Object)null && val2.HasProperty(ColorId)) ? val2.GetColor(ColorId) : Color.white);
					val3.a = 0f;
					_coreBlock.SetColor(ColorId, val3);
					_coreBlock.SetColor(EmissionId, Color.black);
				}
				_coreRenderer.SetPropertyBlock(_coreBlock, _coreIndex);
			}
		}

		private void RPC_RequestOpen(long uid, long playerID)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if (_nview.IsOwner())
			{
				if (_inUse && uid != ZNet.GetUID())
				{
					_nview.InvokeRPC(uid, "xai_beacon_open_response", new object[1] { false });
					return;
				}
				ZDOMan.instance.ForceSendZDO(uid, _nview.GetZDO().m_uid);
				_nview.GetZDO().SetOwner(uid);
				_nview.InvokeRPC(uid, "xai_beacon_open_response", new object[1] { true });
			}
		}

		private void RPC_OpenResponse(long uid, bool granted)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				if (granted)
				{
					BeaconUI.Open(this);
				}
				else
				{
					((Character)localPlayer).Message((MessageType)2, "$msg_inuse", 0, (Sprite)null, false);
				}
			}
		}

		private void RPC_RequestOwn(long sender)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (_nview.IsOwner() && (!_inUse || sender == ZNet.GetUID()))
			{
				ZDOMan.instance.ForceSendZDO(sender, _nview.GetZDO().m_uid);
				_nview.GetZDO().SetOwner(sender);
			}
		}

		public void SetInUse(bool inUse)
		{
			_inUse = inUse;
		}

		public string InsertBlockedReason(string prefab)
		{
			switch (DiscountRules.KindOf(prefab))
			{
			case TrophyKind.Boss:
			{
				if (!IsGreat)
				{
					return "$xai_beacon_boss_use_holder";
				}
				RebuildEffective();
				string key = DiscountRules.TrophyKey(prefab);
				if (_bosses.Any(((string prefab, BossHolder holder) b) => DiscountRules.TrophyKey(b.prefab) == key) || _trophies.Any((KeyValuePair<string, int> kv) => kv.Value > 0 && DiscountRules.TrophyKey(kv.Key) == key))
				{
					return "$xai_beacon_boss_duplicate";
				}
				if (OwnBossCount >= OwnBossSlots)
				{
					return "$xai_beacon_boss_full";
				}
				return null;
			}
			case TrophyKind.Mob:
				if (IsGreat)
				{
					return "$xai_great_beacon_mob_refused";
				}
				if (!DiscountRules.IsStackable(prefab) && NetworkHas(prefab))
				{
					return "$xai_beacon_mob_duplicate";
				}
				if (MobCount >= MobSlots)
				{
					return "$xai_beacon_mob_full";
				}
				return null;
			default:
				return "$xai_beacon_not_a_trophy";
			}
		}

		public bool TryInsertTrophy(Player p, ItemData item)
		{
			if ((Object)(object)p == (Object)null || item == null || (Object)(object)item.m_dropPrefab == (Object)null || !IsValid)
			{
				return false;
			}
			if (!_nview.IsOwner())
			{
				((Character)p).Message((MessageType)2, "$xai_beacon_busy", 0, (Sprite)null, false);
				return false;
			}
			return InsertNow(p, item);
		}

		private bool InsertNow(Player p, ItemData item)
		{
			string name = ((Object)item.m_dropPrefab).name;
			if (DiscountRules.KindOf(name) == TrophyKind.None)
			{
				return false;
			}
			LoadFromZdo();
			string text = InsertBlockedReason(name);
			if (text != null)
			{
				((Character)p).Message((MessageType)2, text, 0, (Sprite)null, false);
				return false;
			}
			_trophies.TryGetValue(name, out var value);
			_trophies[name] = value + 1;
			SaveToZdo();
			((Humanoid)p).GetInventory().RemoveOneItem(item);
			return true;
		}

		private void UpdateQueued()
		{
			if (_queuedItem == null)
			{
				((MonoBehaviour)this).CancelInvoke("UpdateQueued");
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!_nview.IsOwner())
			{
				if (!(Time.time - _queuedAt < 2f))
				{
					((MonoBehaviour)this).CancelInvoke("UpdateQueued");
					_queuedItem = null;
					if ((Object)(object)localPlayer != (Object)null)
					{
						((Character)localPlayer).Message((MessageType)2, "$msg_inuse", 0, (Sprite)null, false);
					}
				}
			}
			else
			{
				((MonoBehaviour)this).CancelInvoke("UpdateQueued");
				ItemData queuedItem = _queuedItem;
				_queuedItem = null;
				if ((Object)(object)localPlayer != (Object)null && ((Humanoid)localPlayer).GetInventory().ContainsItem(queuedItem))
				{
					InsertNow(localPlayer, queuedItem);
				}
			}
		}

		public bool TryInsertTrophy(Player p, string prefab)
		{
			ItemData val = ((IEnumerable<ItemData>)((Humanoid)p).GetInventory().GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => (Object)(object)i.m_dropPrefab != (Object)null && ((Object)i.m_dropPrefab).name == prefab));
			if (val != null)
			{
				return TryInsertTrophy(p, val);
			}
			return false;
		}

		public bool TryRemoveTrophy(Player p, string prefab)
		{
			if (!_trophies.TryGetValue(prefab, out var value) || value <= 0)
			{
				return false;
			}
			ItemDrop val = DiscountRules.ItemDropFor(prefab);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if (!((Humanoid)p).GetInventory().CanAddItem(((Component)val).gameObject, 1))
			{
				((Character)p).Message((MessageType)2, "$msg_noroom", 0, (Sprite)null, false);
				return false;
			}
			if (!_nview.IsOwner())
			{
				((Character)p).Message((MessageType)2, "$xai_beacon_busy", 0, (Sprite)null, false);
				return false;
			}
			LoadFromZdo();
			if (!_trophies.TryGetValue(prefab, out value) || value <= 0)
			{
				return false;
			}
			if (value == 1)
			{
				_trophies.Remove(prefab);
			}
			else
			{
				_trophies[prefab] = value - 1;
			}
			SaveToZdo();
			((Humanoid)p).GetInventory().AddItem(((Component)val).gameObject, 1);
			return true;
		}

		private bool WardAllowsEdit()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return PrivateArea.CheckAccess(((Component)this).transform.position, 0f, true, false);
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			if (hold || !IsValid || (Object)(object)Game.instance == (Object)null)
			{
				return false;
			}
			if (!WardAllowsEdit())
			{
				return true;
			}
			_nview.InvokeRPC("xai_beacon_request_open", new object[1] { Game.instance.GetPlayerProfile().GetPlayerID() });
			return true;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			Player val = (Player)(object)((user is Player) ? user : null);
			if ((Object)(object)val == (Object)null || item == null || (Object)(object)item.m_dropPrefab == (Object)null || !IsValid)
			{
				return false;
			}
			string name = ((Object)item.m_dropPrefab).name;
			if (DiscountRules.KindOf(name) == TrophyKind.None)
			{
				return false;
			}
			if (!WardAllowsEdit())
			{
				return true;
			}
			string text = InsertBlockedReason(name);
			if (text != null)
			{
				((Character)val).Message((MessageType)2, text, 0, (Sprite)null, false);
				return true;
			}
			if (_nview.IsOwner())
			{
				InsertNow(val, item);
				return true;
			}
			_nview.InvokeRPC("xai_beacon_request_own", Array.Empty<object>());
			_queuedItem = item;
			_queuedAt = Time.time;
			((MonoBehaviour)this).CancelInvoke("UpdateQueued");
			((MonoBehaviour)this).InvokeRepeating("UpdateQueued", 0f, 0.1f);
			return true;
		}

		public string GetHoverText()
		{
			return Localization.instance.Localize($"{PieceNameKey}\n$xai_beacon_level {Level}/{MaxLevel}  $xai_beacon_radius {Radius:0}m  $xai_beacon_boss {(IsGreat ? $"{OwnBossCount}/{OwnBossSlots}" : BossCount.ToString())}  $xai_beacon_mob {MobSlotsText}\n" + "[<color=yellow><b>$KEY_Use</b></color>] $xai_beacon_open");
		}

		public string GetHoverName()
		{
			return Localization.instance.Localize(PieceNameKey);
		}

		public float GetHoverOffset()
		{
			return 0f;
		}
	}
	public static class BeaconRegistry
	{
		private static readonly HashSet<BeaconController> Beacons = new HashSet<BeaconController>();

		public static IEnumerable<BeaconController> All => Beacons;

		public static void Register(BeaconController b)
		{
			Beacons.Add(b);
		}

		public static void Unregister(BeaconController b)
		{
			Beacons.Remove(b);
		}

		public static IEnumerable<BeaconController> InRange(Vector3 pos)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			return Beacons.Where((BeaconController b) => (Object)(object)b != (Object)null && b.IsValid && Vector3.Distance(((Component)b).transform.position, pos) <= b.Radius);
		}

		public static Requirement[] GetEffectiveRequirements(Piece piece, Vector3 pos)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return GetEffectiveRequirements(piece.m_resources, pos);
		}

		public static Requirement[] GetEffectiveRequirements(Requirement[] source, Vector3 pos, List<DiscountSavings.Step> plan = null, List<CostLine> lines = null)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Expected O, but got Unknown
			if (source == null || !BuildBeaconPlugin.Cfg.Enabled.Value)
			{
				return source;
			}
			List<BeaconController> list = InRange(pos).ToList();
			if (list.Count == 0)
			{
				return source;
			}
			Requirement[] array = (Requirement[])(object)new Requirement[source.Length];
			bool flag = false;
			bool flag2 = false;
			for (int i = 0; i < array.Length; i++)
			{
				Requirement src = source[i];
				int num = src.m_amount;
				bool bossFree = false;
				float num2 = 0f;
				int streak = 0;
				bool flag3 = list.Any((BeaconController b) => DiscountRules.BossCovers(b.Trophies, src.m_resItem));
				if (flag3 && DiscountRules.BossMakesFree)
				{
					num = 0;
					bossFree = true;
				}
				else
				{
					if (flag3)
					{
						num2 = DiscountRules.BossPercent;
					}
					else
					{
						bool flag4 = BuildBeaconPlugin.Cfg.Stacking.Value == StackMode.Sum;
						int num3 = 0;
						float num4 = 0f;
						foreach (BeaconController item in list)
						{
							int num5 = DiscountRules.MobLevel(item.Trophies, src.m_resItem);
							if (num5 > 0)
							{
								num3 += num5;
								num4 = Mathf.Max(num4, DiscountRules.PercentForLevel(num5));
							}
						}
						if (num3 > 0)
						{
							num2 = (flag4 ? DiscountRules.PercentForLevel(num3) : num4);
						}
					}
					if (num2 > 0f)
					{
						float exact = DiscountRules.ExactMobCost(src.m_amount, num2);
						num = DiscountRules.RoundCost(exact);
						if (plan != null)
						{
							int count = plan.Count;
							num = DiscountSavings.Apply(src.m_resItem, exact, num, num2, plan);
							flag2 |= plan.Count > count;
							if (plan.Count > count)
							{
								streak = plan[plan.Count - 1].Streak;
							}
						}
					}
				}
				flag |= num != src.m_amount;
				lines?.Add(new CostLine
				{
					Item = src.m_resItem,
					Base = src.m_amount,
					Amount = num,
					Percent = num2,
					BossFree = bossFree,
					Streak = streak
				});
				array[i] = new Requirement
				{
					m_resItem = src.m_resItem,
					m_amount = num,
					m_amountPerLevel = src.m_amountPerLevel,
					m_recover = src.m_recover
				};
			}
			if (!(flag || flag2))
			{
				return source;
			}
			return array;
		}
	}
	public sealed class BeaconTrophyDisplay
	{
		public const int AlcoveCount = 4;

		public const string AttachPrefix = "attach_";

		private static readonly Quaternion StandAttachRotation = new Quaternion(0f, 0.7071068f, 0.7071068f, 0f);

		private static readonly Vector3 FitBox = new Vector3(0.24f, 0.34f, 0.28f);

		private const float MaxUpscale = 1.5f;

		private readonly Transform[] _mounts;

		private readonly string[] _shown;

		private readonly GameObject[] _visuals;

		private readonly Vector2? _shrinkTo;

		private TrophyPlacement _placement;

		private bool _shrinking;

		private static readonly Dictionary<string, float> GreatTrophyScales = new Dictionary<string, float>
		{
			{ "TrophyEikthyr", 1f },
			{ "TrophyTheElder", 1f },
			{ "TrophyDragonQueen", 1f },
			{ "TrophyBonemass", 0.75f },
			{ "TrophySeekerQueen", 0.65f },
			{ "TrophyGoblinKing", 0.65f },
			{ "TrophyFader", 0.65f }
		};

		public const float GreatTrophyMaxScale = 0.9f;

		public static readonly Vector2 GreatTrophyFit = new Vector2(4.5f, 4.5f);

		private static readonly Dictionary<string, int> GreatTrophyAlcoves = new Dictionary<string, int>
		{
			{ "TrophyEikthyr", 0 },
			{ "TrophyTheElder", 1 },
			{ "TrophyBonemass", 5 },
			{ "TrophyDragonQueen", 4 },
			{ "TrophyGoblinKing", 3 },
			{ "TrophySeekerQueen", 6 },
			{ "TrophyFader", 2 }
		};

		private static float? GreatScaleFor(string prefab)
		{
			string text = DiscountRules.TrophyKey(prefab);
			foreach (KeyValuePair<string, float> greatTrophyScale in GreatTrophyScales)
			{
				if (DiscountRules.TrophyKey(greatTrophyScale.Key) == text)
				{
					return greatTrophyScale.Value;
				}
			}
			return null;
		}

		private static int GreatAlcoveFor(string prefab)
		{
			string text = DiscountRules.TrophyKey(prefab);
			foreach (KeyValuePair<string, int> greatTrophyAlcofe in GreatTrophyAlcoves)
			{
				if (DiscountRules.TrophyKey(greatTrophyAlcofe.Key) == text)
				{
					return greatTrophyAlcofe.Value;
				}
			}
			return -1;
		}

		public static List<string> ChooseGreat(IReadOnlyDictionary<string, int> trophies, int count)
		{
			string[] array = new string[count];
			List<string> list = new List<string>();
			foreach (string item in (from kv in trophies
				where kv.Value > 0
				select kv.Key).OrderBy(DiscountRules.BossOrder))
			{
				int num = GreatAlcoveFor(item);
				if (num >= 0 && num < count && array[num] == null)
				{
					array[num] = item;
				}
				else
				{
					list.Add(item);
				}
			}
			foreach (string item2 in list)
			{
				int num2 = Array.IndexOf(array, null);
				if (num2 < 0)
				{
					break;
				}
				array[num2] = item2;
			}
			return array.ToList();
		}

		public BeaconTrophyDisplay(Transform beacon, Vector2? shrinkTo = null)
		{
			List<Transform> list = new List<Transform>();
			Transform item;
			while ((Object)(object)(item = beacon.Find("attach_" + list.Count)) != (Object)null)
			{
				list.Add(item);
			}
			if (list.Count == 0)
			{
				list.AddRange((IEnumerable<Transform>)(object)new Transform[4]);
			}
			_mounts = list.ToArray();
			_shown = new string[_mounts.Length];
			_visuals = (GameObject[])(object)new GameObject[_mounts.Length];
			_shrinkTo = shrinkTo;
		}

		public static List<string> Choose(IReadOnlyDictionary<string, int> trophies, int count = 4)
		{
			List<string> source = (from kv in trophies
				where kv.Value > 0
				select kv.Key).ToList();
			List<string> list = source.Where(DiscountRules.IsBossTrophy).OrderBy(DiscountRules.BossOrder).ToList();
			if (list.Count > count)
			{
				list = list.Skip(list.Count - count).ToList();
			}
			IOrderedEnumerable<string> second = source.Where((string t) => !DiscountRules.IsBossTrophy(t)).OrderBy<string, string>((string t) => t, StringComparer.Ordinal);
			return list.Concat(second).Take(count).ToList();
		}

		public void Refresh(IReadOnlyDictionary<string, int> trophies)
		{
			List<string> list = (_shrinkTo.HasValue ? ChooseGreat(trophies, _mounts.Length) : Choose(trophies, _mounts.Length));
			TrophyPlacement value = BuildBeaconPlugin.Cfg.Placement.Value;
			bool flag = _shrinkTo.HasValue && BuildBeaconPlugin.Cfg.ShrinkGreatBeaconTrophies.Value;
			bool flag2 = (_shrinkTo.HasValue ? (flag != _shrinking) : (value != _placement));
			_placement = value;
			_shrinking = flag;
			for (int i = 0; i < _mounts.Length; i++)
			{
				string text = ((i < list.Count) ? list[i] : null);
				if (!flag2 && text == _shown[i] && (text == null || (Object)(object)_visuals[i] != (Object)null))
				{
					continue;
				}
				if ((Object)(object)_visuals[i] != (Object)null)
				{
					Object.Destroy((Object)(object)_visuals[i]);
				}
				_visuals[i] = null;
				_shown[i] = text;
				if (text == null || !((Object)(object)_mounts[i] != (Object)null))
				{
					continue;
				}
				float? num = (flag ? GreatScaleFor(text) : ((float?)null));
				GameObject[] visuals = _visuals;
				int num2 = i;
				object obj;
				if (_shrinkTo.HasValue)
				{
					if (!num.HasValue)
					{
						obj = (flag ? Build(_mounts[i], text, TrophyPlacement.Mounted, _shrinkTo, 0.9f) : Build(_mounts[i], text, TrophyPlacement.Mounted));
					}
					else
					{
						Transform mount = _mounts[i];
						float? fixedScale = num;
						obj = Build(mount, text, TrophyPlacement.Mounted, null, 1.5f, fixedScale);
					}
				}
				else
				{
					obj = Build(_mounts[i], text, value);
				}
				visuals[num2] = (GameObject)obj;
			}
		}

		internal static GameObject Build(Transform mount, string prefabName, TrophyPlacement placement, Vector2? faceFit = null, float maxScale = 1.5f, float? fixedScale = null)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_029d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02db: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(prefabName) : null);
			if ((Object)(object)val == (Object)null)
			{
				BuildBeaconPlugin.Log.LogWarning((object)("Trophy display: no item prefab \"" + prefabName + "\""));
				return null;
			}
			Transform val2 = val.transform.Find("attach");
			if ((Object)(object)val2 == (Object)null)
			{
				BuildBeaconPlugin.Log.LogWarning((object)("Trophy display: \"" + prefabName + "\" has no attach child"));
				return null;
			}
			Transform val3 = val2.Find("attachobj");
			Transform obj = (((Object)(object)val3 != (Object)null) ? val3 : val2);
			GameObject val4 = new GameObject("trophy_" + prefabName);
			val4.transform.SetParent(mount, false);
			Transform transform = new GameObject("pivot").transform;
			transform.SetParent(val4.transform, false);
			transform.localRotation = StandAttachRotation;
			GameObject val5 = Object.Instantiate<GameObject>(((Component)obj).gameObject, transform, false);
			val5.transform.localPosition = val2.localPosition;
			val5.transform.localRotation = val2.localRotation;
			val5.transform.localScale = val2.localScale;
			Collider[] componentsInChildren = val5.GetComponentsInChildren<Collider>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
			}
			Rigidbody[] componentsInChildren2 = val5.GetComponentsInChildren<Rigidbody>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren2[i]);
			}
			if (!TryMeshBounds(val5, val4.transform, out var bounds))
			{
				BuildBeaconPlugin.Log.LogWarning((object)("Trophy display: \"" + prefabName + "\" has no meshes to show"));
				return val4;
			}
			Vector3 size = ((Bounds)(ref bounds)).size;
			float num = (fixedScale.HasValue ? fixedScale.Value : (faceFit.HasValue ? Mathf.Min(new float[3]
			{
				maxScale,
				faceFit.Value.x / Mathf.Max(size.x, 0.001f),
				faceFit.Value.y / Mathf.Max(size.y, 0.001f)
			}) : ((placement == TrophyPlacement.Inset) ? Mathf.Min(new float[4]
			{
				1.5f,
				FitBox.x / Mathf.Max(size.x, 0.001f),
				FitBox.y / Mathf.Max(size.y, 0.001f),
				FitBox.z / Mathf.Max(size.z, 0.001f)
			}) : 1f)));
			val4.transform.localScale = Vector3.one * num;
			val4.transform.localPosition = (0f - num) * new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).center.y, ((Bounds)(ref bounds)).min.z);
			BuildBeaconPlugin.Verbose(string.Format("Trophy display: {0} in {1} ({2}), size {3} scaled x{4:F2}", prefabName, ((Object)mount).name, fixedScale.HasValue ? "Fixed" : (faceFit.HasValue ? "Face" : placement.ToString()), ((Vector3)(ref size)).ToString("F2"), num));
			return val4;
		}

		private static bool TryMeshBounds(GameObject root, Transform space, out Bounds bounds)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			bounds = default(Bounds);
			bool flag = false;
			Matrix4x4 worldToLocalMatrix = space.worldToLocalMatrix;
			Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true);
			Vector3 val5 = default(Vector3);
			foreach (Renderer val in componentsInChildren)
			{
				Mesh val2 = null;
				SkinnedMeshRenderer val3 = (SkinnedMeshRenderer)(object)((val is SkinnedMeshRenderer) ? val : null);
				if (val3 != null)
				{
					val2 = val3.sharedMesh;
				}
				else if (val is MeshRenderer)
				{
					MeshFilter component = ((Component)val).GetComponent<MeshFilter>();
					if ((Object)(object)component != (Object)null)
					{
						val2 = component.sharedMesh;
					}
				}
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				Matrix4x4 val4 = worldToLocalMatrix * ((Component)val).transform.localToWorldMatrix;
				Bounds bounds2 = val2.bounds;
				for (int j = 0; j < 8; j++)
				{
					((Vector3)(ref val5))..ctor(((j & 1) == 0) ? (-1f) : 1f, ((j & 2) == 0) ? (-1f) : 1f, ((j & 4) == 0) ? (-1f) : 1f);
					Vector3 val6 = ((Matrix4x4)(ref val4)).MultiplyPoint3x4(((Bounds)(ref bounds2)).center + Vector3.Scale(((Bounds)(ref bounds2)).extents, val5));
					if (!flag)
					{
						bounds = new Bounds(val6, Vector3.zero);
						flag = true;
					}
					else
					{
						((Bounds)(ref bounds)).Encapsulate(val6);
					}
				}
			}
			return flag;
		}
	}
	internal class BeaconUI : MonoBehaviour
	{
		private enum Tab
		{
			Trophies,
			Discounts
		}

		private sealed class RowHover : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler
		{
			private static readonly Color Grey = new Color(1f, 1f, 1f, 0.2f);

			private static readonly Color Red = new Color(1f, 0.6f, 0.6f, 0.45f);

			public Image Square;

			public bool CtrlRemoves = true;

			private bool _over;

			public void OnPointerEnter(PointerEventData e)
			{
				_over = true;
				((Behaviour)Square).enabled = true;
				Tint();
			}

			public void OnPointerExit(PointerEventData e)
			{
				_over = false;
				((Behaviour)Square).enabled = false;
			}

			private void OnDisable()
			{
				_over = false;
				if ((Object)(object)Square != (Object)null)
				{
					((Behaviour)Square).enabled = false;
				}
			}

			private void Update()
			{
				if (_over)
				{
					Tint();
				}
			}

			private void Tint()
			{
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				((Graphic)Square).color = ((CtrlRemoves && CtrlHeld()) ? Red : Grey);
			}
		}

		private sealed class HoverTarget : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler
		{
			public string Topic;

			public string Text;

			public List<HoverTrophy> Trophies = new List<HoverTrophy>();

			public RectTransform Anchor;

			public void OnPointerEnter(PointerEventData eventData)
			{
				if ((Object)(object)_instance != (Object)null)
				{
					_instance.ShowHover(this);
				}
			}

			public void OnPointerExit(PointerEventData eventData)
			{
				if ((Object)(object)_instance != (Object)null)
				{
					_instance.HideHover(this);
				}
			}

			private void OnDisable()
			{
				if ((Object)(object)_instance != (Object)null)
				{
					_instance.HideHover(this);
				}
			}
		}

		private struct HoverTrophy
		{
			public Sprite Icon;

			public string Name;

			public bool InNetwork;

			public bool Boss;
		}

		private const float PanelW = 640f;

		private const float PanelH = 664f;

		private const float TabW = 150f;

		private const float TabH = 36f;

		private const float SourceIconSize = 34f;

		private const float Margin = 20f;

		private const float RowH = 60f;

		private const float HeaderRowH = 30f;

		private const float IconSize = 44f;

		private const float RowGap = 4f;

		private const float ListH = 410f;

		private const int TitleSize = 26;

		private const int BodySize = 17;

		private const int SmallSize = 15;

		private static BeaconUI _instance;

		private GameObject _panel;

		private Text _title;

		private Text _subtitle;

		private Text _hint;

		private RectTransform _scrollRt;

		private float _listTopWithFilter;

		private float _listTopNoFilter;

		private const float BottomButtonW = 120f;

		private const float BottomGap = 16f;

		private const float FooterH = 56f;

		private Button _closeButton;

		private Button _backButton;

		private Button _tabTrophies;

		private Button _tabDiscounts;

		private Button _clearFilter;

		private InputField _filter;

		private string _filterText = "";

		private bool _openedInventory;

		private bool _inventorySeen;

		private bool _hidCrafting;

		private RectTransform _content;

		private ScrollRect _scroll;

		private Sprite _iconFrame;

		private RectTransform _hoverBox;

		private Text _hoverText;

		private RectTransform _hoverTrophies;

		private const float HoverW = 316f;

		private const float HoverPad = 8f;

		private const float HoverTrophySize = 30f;

		private const float HoverTrophyGap = 4f;

		private const float HoverTrophyCell = 56f;

		private const int HoverNameMaxSize = 12;

		private const int HoverNameMinSize = 9;

		private HoverTarget _hovered;

		private BeaconController _beacon;

		private TrophyKind _picking;

		private Tab _tab;

		private static readonly Color Unreachable = new Color(0.5f, 0.5f, 0.5f, 0.14f);

		private static readonly Color[] LevelColors = (Color[])(object)new Color[3]
		{
			new Color(0.95f, 0.52f, 0.12f),
			new Color(0.96f, 0.8f, 0.18f),
			new Color(0.32f, 0.78f, 0.26f)
		};

		private static readonly Color BossColor = new Color(0.28f, 0.58f, 1f);

		private const float SegmentH = 16f;

		private const float SegmentGap = 3f;

		private static readonly Dictionary<Sprite, Sprite> s_grey = new Dictionary<Sprite, Sprite>();

		private static Sprite s_star;

		public static bool IsOpen
		{
			get
			{
				if ((Object)(object)_instance != (Object)null && (Object)(object)_instance._panel != (Object)null)
				{
					return _instance._panel.activeSelf;
				}
				return false;
			}
		}

		public static BeaconController OpenBeacon
		{
			get
			{
				if (!IsOpen)
				{
					return null;
				}
				return _instance._beacon;
			}
		}

		internal static void Init(GameObject host)
		{
			if ((Object)(object)_instance == (Object)null)
			{
				_instance = host.AddComponent<BeaconUI>();
			}
		}

		public static void Open(BeaconController beacon)
		{
			if (!((Object)(object)_instance == (Object)null) && !((Object)(object)beacon == (Object)null) && beacon.IsValid && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				_instance.Show(beacon);
			}
		}

		public static void CloseIfOpen()
		{
			if (IsOpen)
			{
				_instance.Close();
			}
		}

		private void Show(BeaconController beacon)
		{
			EnsureBuilt();
			if ((Object)(object)_beacon != (Object)null && (Object)(object)_beacon != (Object)(object)beacon)
			{
				_beacon.SetInUse(inUse: false);
			}
			_beacon = beacon;
			_beacon.SetInUse(inUse: true);
			_picking = TrophyKind.None;
			_tab = Tab.Trophies;
			_filterText = "";
			_filter.SetTextWithoutNotify("");
			_panel.SetActive(true);
			GUIManager.BlockInput(true);
			OpenInventoryAlongside();
			Refresh();
		}

		private void OpenInventoryAlongside()
		{
			InventoryGui instance = InventoryGui.instance;
			_openedInventory = (Object)(object)instance != (Object)null && !InventoryGui.IsVisible();
			_inventorySeen = false;
			if (_openedInventory)
			{
				instance.Show((Container)null, 1);
				GameObject val = (((Object)(object)instance.m_crafting != (Object)null) ? ((Component)instance.m_crafting).gameObject : null);
				_hidCrafting = (Object)(object)val != (Object)null && val.activeSelf;
				if (_hidCrafting)
				{
					val.SetActive(false);
				}
			}
		}

		private void Close()
		{
			HideHover(null);
			if ((Object)(object)_panel != (Object)null)
			{
				_panel.SetActive(false);
			}
			if (_openedInventory && InventoryGui.IsVisible())
			{
				InventoryGui.instance.Hide();
			}
			if (_hidCrafting && (Object)(object)InventoryGui.instance != (Object)null && (Object)(object)InventoryGui.instance.m_crafting != (Object)null)
			{
				((Component)InventoryGui.instance.m_crafting).gameObject.SetActive(true);
			}
			_hidCrafting = false;
			_openedInventory = false;
			GUIManager.BlockInput(false);
			if ((Object)(object)_beacon != (Object)null)
			{
				_beacon.SetInUse(inUse: false);
			}
			_beacon = null;
			_picking = TrophyKind.None;
		}

		private void Update()
		{
			if (!IsOpen)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)_beacon == (Object)null || !_beacon.IsValid || (Object)(object)localPlayer == (Object)null || !_beacon.InRangeOf(localPlayer))
			{
				Close();
				return;
			}
			_beacon.PokeStation();
			if (_openedInventory)
			{
				if (InventoryGui.IsVisible())
				{
					_inventorySeen = true;
				}
				else if (_inventorySeen)
				{
					Close();
					return;
				}
			}
			if (ZInput.GetKeyDown((KeyCode)27, true))
			{
				if (_picking != TrophyKind.None)
				{
					_picking = TrophyKind.None;
					Refresh();
				}
				else
				{
					Close();
				}
			}
		}

		internal static void OnInventoryCtrlClick(ItemData item)
		{
			if (!((Object)(object)_instance == (Object)null) && !((Object)(object)_instance._beacon == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)item?.m_dropPrefab == (Object)null || DiscountRules.KindOf(((Object)item.m_dropPrefab).name) == TrophyKind.None)
				{
					((Character)localPlayer).Message((MessageType)2, "$xai_beacon_not_a_trophy", 0, (Sprite)null, false);
				}
				else if (_instance._beacon.TryInsertTrophy(localPlayer, item))
				{
					_instance._picking = TrophyKind.None;
					_instance.Refresh();
				}
			}
		}

		private static Button Quiet(Button button)
		{
			ButtonSfx val = (((Object)(object)button != (Object)null) ? ((Component)button).GetComponent<ButtonSfx>() : null);
			if ((Object)(object)val != (Object)null)
			{
				val.m_selectSfxPrefab = null;
			}
			return button;
		}

		private void AddClickSound(GameObject go)
		{
			ButtonSfx val = (((Object)(object)_closeButton != (Object)null) ? ((Component)_closeButton).GetComponent<ButtonSfx>() : null);
			if (!((Object)(object)val == (Object)null) && !((Object)(object)go.GetComponent<ButtonSfx>() != (Object)null))
			{
				go.AddComponent<ButtonSfx>().m_sfxPrefab = val.m_sfxPrefab;
			}
		}

		private static bool CtrlHeld()
		{
			if (!ZInput.GetKey((KeyCode)306, true))
			{
				return ZInput.GetKey((KeyCode)305, true);
			}
			return true;
		}

		private void EnsureBuilt()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Expected O, but got Unknown
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Expected O, but got Unknown
			//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02af: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0341: Unknown result type (might be due to invalid IL or missing references)
			//IL_0343: Unknown result type (might be due to invalid IL or missing references)
			//IL_034c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0375: Unknown result type (might be due to invalid IL or missing references)
			//IL_0384: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03aa: Expected O, but got Unknown
			//IL_03b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0412: Unknown result type (might be due to invalid IL or missing references)
			//IL_0432: Unknown result type (might be due to invalid IL or missing references)
			//IL_0452: Unknown result type (might be due to invalid IL or missing references)
			//IL_0474: Unknown result type (might be due to invalid IL or missing references)
			//IL_0476: Unknown result type (might be due to invalid IL or missing references)
			//IL_0490: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0532: Unknown result type (might be due to invalid IL or missing references)
			//IL_053c: Expected O, but got Unknown
			//IL_0597: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a1: Expected O, but got Unknown
			//IL_05e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0624: Unknown result type (might be due to invalid IL or missing references)
			//IL_062e: Expected O, but got Unknown
			//IL_0645: Unknown result type (might be due to invalid IL or missing references)
			//IL_0647: Unknown result type (might be due to invalid IL or missing references)
			//IL_0653: Unknown result type (might be due to invalid IL or missing references)
			//IL_0688: Unknown result type (might be due to invalid IL or missing references)
			//IL_0692: Expected O, but got Unknown
			//IL_06b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_06b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_06cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_06cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0702: Unknown result type (might be due to invalid IL or missing references)
			//IL_0704: Unknown result type (might be due to invalid IL or missing references)
			//IL_0705: Unknown result type (might be due to invalid IL or missing references)
			//IL_070c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0713: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_panel != (Object)null)
			{
				return;
			}
			GUIManager instance = GUIManager.Instance;
			Font averiaSerifBold = instance.AveriaSerifBold;
			Color valheimOrange = GUIManager.Instance.ValheimOrange;
			Color valheimBeige = GUIManager.Instance.ValheimBeige;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.5f, 0.5f);
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0f, 1f);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0f, 0f);
			Vector2 val4 = default(Vector2);
			((Vector2)(ref val4))..ctor(1f, 0f);
			_panel = instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, val, val, Vector2.zero, 640f, 664f, true);
			((Object)_panel).name = "BuildBeaconPanel";
			_panel.SetActive(false);
			float num = 600f;
			float num2 = -20f;
			Vector2 val5 = default(Vector2);
			((Vector2)(ref val5))..ctor(0.5f, 1f);
			_title = instance.CreateText("", _panel.transform, val5, val5, new Vector2(0f, num2), averiaSerifBold, 26, valheimOrange, true, Color.black, num, 34f, false).GetComponent<Text>();
			Place((Component)(object)_title, val5, new Vector2(0f, num2));
			_title.alignment = (TextAnchor)1;
			num2 -= 38f;
			_subtitle = instance.CreateText("", _panel.transform, val5, val5, new Vector2(0f, num2), averiaSerifBold, 15, valheimBeige, true, Color.black, num, 22f, false).GetComponent<Text>();
			Place((Component)(object)_subtitle, val5, new Vector2(0f, num2));
			_subtitle.alignment = (TextAnchor)1;
			num2 -= 32f;
			_tabTrophies = Quiet(instance.CreateButton(L("$xai_beacon_tab_trophies"), _panel.transform, val5, val5, new Vector2(-79f, num2 - 18f), 150f, 36f).GetComponent<Button>());
			((UnityEvent)_tabTrophies.onClick).AddListener((UnityAction)delegate
			{
				SetTab(Tab.Trophies);
			});
			_tabDiscounts = Quiet(instance.CreateButton(L("$xai_beacon_tab_discounts"), _panel.transform, val5, val5, new Vector2(79f, num2 - 18f), 150f, 36f).GetComponent<Button>());
			((UnityEvent)_tabDiscounts.onClick).AddListener((UnityAction)delegate
			{
				SetTab(Tab.Discounts);
			});
			num2 -= 44f;
			_filter = instance.CreateInputField(_panel.transform, val2, val2, new Vector2(20f, num2), (ContentType)0, L("$xai_beacon_filter_placeholder"), 17, num - 40f - 8f, 34f).GetComponent<InputField>();
			Place((Component)(object)_filter, val2, new Vector2(20f, num2 - 2f));
			((UnityEvent<string>)(object)_filter.onValueChanged).AddListener((UnityAction<string>)delegate(string v)
			{
				_filterText = v ?? "";
				if (_tab == Tab.Discounts)
				{
					Refresh();
				}
			});
			Vector2 val6 = default(Vector2);
			((Vector2)(ref val6))..ctor(1f, 1f);
			_clearFilter = Quiet(instance.CreateButton("X", _panel.transform, val6, val6, new Vector2(-20f, num2), 40f, 34f).GetComponent<Button>());
			Place((Component)(object)_clearFilter, val6, new Vector2(-20f, num2 - 2f));
			((UnityEvent)_clearFilter.onClick).AddListener((UnityAction)delegate
			{
				_filter.text = "";
			});
			num2 -= 44f;
			ColorBlock val7 = default(ColorBlock);
			((ColorBlock)(ref val7)).normalColor = new Color(0.8f, 0.7f, 0.5f, 0.8f);
			((ColorBlock)(ref val7)).highlightedColor = new Color(0.9f, 0.8f, 0.6f, 1f);
			((ColorBlock)(ref val7)).pressedColor = new Color(0.7f, 0.6f, 0.4f, 1f);
			((ColorBlock)(ref val7)).selectedColor = new Color(0.9f, 0.8f, 0.6f, 1f);
			((ColorBlock)(ref val7)).disabledColor = new Color(0.5f, 0.5f, 0.5f, 0.5f);
			((ColorBlock)(ref val7)).colorMultiplier = 1f;
			((ColorBlock)(ref val7)).fadeDuration = 0.1f;
			ColorBlock val8 = val7;
			GameObject val9 = instance.CreateScrollView(_panel.transform, false, true, 8f, 4f, val8, new Color(0f, 0f, 0f, 0.35f), num, 410f);
			RectTransform component = val9.GetComponent<RectTransform>();
			component.anchorMin = val2;
			component.anchorMax = val2;
			component.pivot = val2;
			component.anchoredPosition = new Vector2(20f, num2);
			_scrollRt = component;
			_listTopWithFilter = num2;
			_listTopNoFilter = num2 + 44f;
			num2 -= 420f;
			_scroll = val9.GetComponentInChildren<ScrollRect>();
			FollowWrapperHeight(component, (RectTransform)((Component)_scroll).transform);
			_content = _scroll.content;
			VerticalLayoutGroup obj = ((Component)_content).GetComponent<VerticalLayoutGroup>() ?? ((Component)_content).gameObject.AddComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)obj).spacing = 4f;
			((LayoutGroup)obj).padding = new RectOffset(4, 4, 4, 4);
			if ((Object)(object)((Component)_content).GetComponent<ContentSizeFitter>() == (Object)null)
			{
				((Component)_content).gameObject.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
			}
			_closeButton = Quiet(instance.CreateButton(L("$xai_beacon_close"), _panel.transform, val4, val4, new Vector2(-80f, 40f), 120f, 40f).GetComponent<Button>());
			((UnityEvent)_closeButton.onClick).AddListener(new UnityAction(Close));
			_backButton = Quiet(instance.CreateButton(L("$xai_beacon_back"), _panel.transform, val3, val3, new Vector2(80f, 40f), 120f, 40f).GetComponent<Button>());
			((UnityEvent)_backButton.onClick).AddListener((UnityAction)delegate
			{
				_picking = TrophyKind.None;
				Refresh();
			});
			Vector2 pivot = default(Vector2);
			((Vector2)(ref pivot))..ctor(0f, 0.5f);
			_hint = instance.CreateText("", _panel.transform, val3, val3, new Vector2(20f, 40f), averiaSerifBold, 15, valheimBeige, true, Color.black, num - 120f - 16f, 56f, false).GetComponent<Text>();
			RectTransform component2 = ((Component)_hint).GetComponent<RectTransform>();
			Vector2 anchorMin = (component2.anchorMax = val3);
			component2.anchorMin = anchorMin;
			component2.pivot = pivot;
			_hint.alignment = (TextAnchor)3;
			_hint.fontStyle = (FontStyle)2;
			_hint.resizeTextForBestFit = true;
			_hint.resizeTextMinSize = 11;
			_hint.resizeTextMaxSize = 15;
			_iconFrame = instance.GetSprite("item_background");
			BuildHoverBox();
		}

		private void Refresh()
		{
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_beacon == (Object)null))
			{
				HideHover(null);
				_title.text = L(_beacon.PieceNameKey);
				_subtitle.text = string.Format("{0} {1}/{2}   ·   ", L("$xai_beacon_level"), _beacon.Level, _beacon.MaxLevel) + string.Format("{0} {1:0}m   ·   ", L("$xai_beacon_radius"), _beacon.Radius) + string.Format("{0} {1}   ·   ", L("$xai_beacon_boss_section"), _beacon.BossCount) + L("$xai_beacon_mob_section") + " " + L(_beacon.MobSlotsText);
				((Selectable)_tabTrophies).interactable = _tab != Tab.Trophies;
				((Selectable)_tabDiscounts).interactable = _tab != Tab.Discounts;
				bool flag = _tab == Tab.Discounts;
				((Component)_filter).gameObject.SetActive(flag);
				((Component)_clearFilter).gameObject.SetActive(flag);
				((Selectable)_clearFilter).interactable = _filterText.Length > 0;
				bool flag2 = _tab == Tab.Trophies && _picking != TrophyKind.None;
				((Component)_backButton).gameObject.SetActive(flag2);
				float num = (flag ? _listTopWithFilter : _listTopNoFilter);
				_scrollRt.anchoredPosition = new Vector2(20f, num);
				float num2 = 74f;
				_scrollRt.sizeDelta = new Vector2(_scrollRt.sizeDelta.x, 664f + num - num2);
				float num3 = 600f;
				float num4 = (flag2 ? 136f : 0f);
				RectTransform component = ((Component)_hint).GetComponent<RectTransform>();
				component.anchoredPosition = new Vector2(20f + num4, 40f);
				component.sizeDelta = new Vector2(num3 - 120f - 16f - num4, 56f);
				ClearRows();
				if (_tab == Tab.Discounts)
				{
					BuildDiscounts();
				}
				else if (_picking != TrophyKind.None)
				{
					BuildPicker(_picking);
				}
				else
				{
					BuildSlots();
				}
				_scroll.verticalNormalizedPosition = 1f;
			}
		}

		private void BuildSlots()
		{
			_hint.text = L("$xai_beacon_hint_slots");
			if (_beacon.IsGreat)
			{
				AddSectionHeader(string.Format("{0}  {1}/{2}", L("$xai_beacon_boss_section"), _beacon.OwnBossCount, _beacon.OwnBossSlots));
				foreach (string item in _beacon.SlotsOf(TrophyKind.Boss))
				{
					AddTrophyRow(item);
				}
				if (_beacon.OwnBossCount < _beacon.OwnBossSlots)
				{
					AddRow(null, L("$xai_beacon_empty_boss_slot"), L("$xai_beacon_click_to_add_boss"), null, delegate
					{
						_picking = TrophyKind.Boss;
						Refresh();
					}, wholeRowClick: true);
				}
				foreach (var bossEntry in _beacon.BossEntries)
				{
					var (prefab, _) = bossEntry;
					if ((Object)(object)bossEntry.holder != (Object)null)
					{
						AddTrophyRow(prefab, L("$xai_beacon_on_holder"));
					}
				}
			}
			else
			{
				AddSectionHeader(string.Format("{0}  {1}", L("$xai_beacon_boss_section"), _beacon.BossCount));
				foreach (var (prefab2, bossHolder) in _beacon.BossEntries)
				{
					AddTrophyRow(prefab2, ((Object)(object)bossHolder != (Object)null) ? L("$xai_beacon_on_holder") : null);
				}
				if (_beacon.BossCount == 0)
				{
					AddRow(null, L("$xai_beacon_no_bosses"), L("$xai_beacon_no_bosses_hint"), null, null, wholeRowClick: true);
				}
			}
			if (_beacon.MobSlots > 0 || _beacon.MobCount > 0)
			{
				AddSectionHeader(string.Format("{0}  {1}/{2}", L("$xai_beacon_mob_section"), _beacon.MobCount, _beacon.MobSlots));
				foreach (string item2 in _beacon.SlotsOf(TrophyKind.Mob))
				{
					AddTrophyRow(item2);
				}
				if (_beacon.MobCount < _beacon.MobSlots)
				{
					AddRow(null, L("$xai_beacon_empty_mob_slot"), L("$xai_beacon_click_to_add_mob"), null, delegate
					{
						_picking = TrophyKind.Mob;
						Refresh();
					}, wholeRowClick: true);
				}
			}
			if (_beacon.RackCount <= 0)
			{
				return;
			}
			AddSectionHeader(string.Format("{0}  {1}/{2}", L("$xai_beacon_racks_section"), _beacon.RackTrophyCount, _beacon.RackSlots));
			foreach (var rackTrophy in _beacon.RackTrophies)
			{
				AddTrophyRow(rackTrophy.prefab, rackTrophy.counted ? L("$xai_beacon_on_rack") : (L("$xai_beacon_on_rack") + ", " + L("$xai_beacon_not_counted")));
			}
		}

		private void AddTrophyRow(string prefab, string placedOn = null)
		{
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Expected O, but got Unknown
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Expected O, but got Unknown
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			bool flag = placedOn != null;
			string captured = prefab;
			Action remove = (flag ? null : ((Action)delegate
			{
				if (_beacon.TryRemoveTrophy(Player.m_localPlayer, captured))
				{
					Refresh();
				}
			}));
			string text = DiscountRules.ItemDisplayName(prefab) ?? prefab;
			GameObject val = AddRow(IconFor(prefab), flag ? $"{text}  <size={15}><color=#c8b89a>({placedOn})</color></size>" : text, DiscountRules.DescribeTrophy(prefab), flag ? null : L("$xai_beacon_remove"), remove);
			string trophyName = DiscountRules.ItemDisplayName(prefab) ?? prefab;
			Image component = val.GetComponent<Image>();
			Button obj = val.AddComponent<Button>();
			AddClickSound(val);
			((Selectable)obj).transition = (Transition)0;
			((Selectable)obj).targetGraphic = (Graphic)(object)component;
			((UnityEvent)obj.onClick).AddListener((UnityAction)delegate
			{
				if (CtrlHeld() && remove != null)
				{
					remove();
				}
				else
				{
					ShowDiscountsFor(trophyName);
				}
			});
			Transform val2 = val.transform.Find("icon");
			if ((Object)(object)val2 != (Object)null)
			{
				GameObject val3 = new GameObject("iconHover", new Type[2]
				{
					typeof(RectTransform),
					typeof(Image)
				});
				val3.transform.SetParent(val.transform, false);
				val3.transform.SetSiblingIndex(val2.GetSiblingIndex());
				RectTransform val4 = (RectTransform)val2;
				RectTransform val5 = (RectTransform)val3.transform;
				val5.anchorMin = val4.anchorMin;
				val5.anchorMax = val4.anchorMax;
				val5.pivot = val4.pivot;
				val5.sizeDelta = val4.sizeDelta;
				val5.anchoredPosition = val4.anchoredPosition;
				Image component2 = val3.GetComponent<Image>();
				component2.sprite = _iconFrame;
				((Graphic)component2).raycastTarget = false;
				((Behaviour)component2).enabled = false;
				RowHover rowHover = val.AddComponent<RowHover>();
				rowHover.Square = component2;
				rowHover.CtrlRemoves = remove != null;
			}
		}

		private void BuildPicker(TrophyKind kind)
		{
			_hint.text = L((kind == TrophyKind.Boss) ? "$xai_beacon_hint_picker_boss" : "$xai_beacon_hint_picker_mob");
			List<(string, int)> list = (from i in ((Humanoid)Player.m_localPlayer).GetInventory().GetAllItems()
				where (Object)(object)i.m_dropPrefab != (Object)null && DiscountRules.KindOf(((Object)i.m_dropPrefab).name) == kind
				group i by ((Object)i.m_dropPrefab).name into g
				select (prefab: g.Key, count: g.Sum((ItemData i) => i.m_stack))).OrderBy(delegate((string prefab, int count) c)
			{
				string text3 = DiscountRules.ItemDisplayName(c.prefab);
				if (text3 == null)
				{
					(text3, _) = c;
				}
				return text3;
			}).ToList();
			if (list.Count == 0)
			{
				AddRow(null, L((kind == TrophyKind.Boss) ? "$xai_beacon_no_boss_trophies" : "$xai_beacon_no_mob_trophies"), "", null, null, wholeRowClick: true);
				return;
			}
			foreach (var item in list)
			{
				string captured = item.Item1;
				string text = _beacon.InsertBlockedReason(item.Item1);
				Sprite icon = IconFor(item.Item1);
				string text2 = DiscountRules.ItemDisplayName(item.Item1);
				if (text2 == null)
				{
					(text2, _) = item;
				}
				AddRow(icon, $"{text2}  x{item.Item2}", (text != null) ? L(text) : DiscountRules.DescribeTrophy(item.Item1), (text != null) ? null : L("$xai_beacon_add"), delegate
				{
					if (_beacon.TryInsertTrophy(Player.m_localPlayer, captured))
					{
						_picking = TrophyKind.None;
						Refresh();
					}
				});
			}
		}

		private void ShowDiscountsFor(string filter)
		{
			_filterText = filter;
			_filter.SetTextWithoutNotify(filter);
			SetTab(Tab.Discounts);
		}

		private void SetTab(Tab tab)
		{
			if (_tab == Tab.Discounts && tab != Tab.Discounts)
			{
				_filterText = "";
				_filter.SetTextWithoutNotify("");
			}
			_tab = tab;
			_picking = TrophyKind.None;
			Refresh();
		}

		private void BuildDiscounts()
		{
			_hint.text = L("$xai_beacon_levels_note");
			List<MaterialDiscount> list = DiscountRules.Breakdown(_beacon.Trophies);
			if (list.Count == 0)
			{
				AddRow(null, L("$xai_beacon_no_discounts"), L("$xai_beacon_no_discounts_hint"), null, null, wholeRowClick: true);
				return;
			}
			string filter = _filterText.Trim();
			List<MaterialDiscount> list2 = ((filter.Length == 0) ? list : list.Where((MaterialDiscount d) => MatchesFilter(d, filter)).ToList());
			if (list2.Count == 0)
			{
				AddRow(null, L("$xai_beacon_filter_none") + " \"" + filter + "\"", "", null, null, wholeRowClick: true);
				return;
			}
			int trophyColumns = list2.Max((MaterialDiscount r) => r.Sources.Count);
			foreach (MaterialDiscount item in list2)
			{
				AddDiscountRow(item, trophyColumns);
			}
		}

		private static Color LevelColor(int level, int top)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			int num = LevelColors.Length - 1;
			if (top <= 1)
			{
				return LevelColors[num];
			}
			float num2 = Mathf.Clamp01((float)(level - 1) / (float)(top - 1));
			return LevelColors[Mathf.RoundToInt(num2 * (float)num)];
		}

		private void AddLevelBar(Transform row, MaterialDiscount d, Vector2 pos, float width)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			int reachable = DiscountRules.MaxMobLevel(d.Item);
			int topLevel = DiscountRules.TopLevel;
			int shown = Mathf.Clamp(d.Level, 0, topLevel);
			Color color = LevelColor(Mathf.Max(shown, 1), topLevel);
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(1f, 0.5f);
			GameObject val2 = new GameObject("levelBar", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val2.transform.SetParent(row, false);
			RectTransform component = val2.GetComponent<RectTransform>();
			component.anchorMin = val;
			component.anchorMax = va