using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using ProjectI.Builds; using ProjectI.Combat; using ProjectI.Runs; using ProjectI.Sessions; using ProjectI.Settings; using UnityEngine; using UnityEngine.UIElements; using Cursor=UnityEngine.Cursor; namespace ProjectI.UI { /// Local input/cursor owner only. Never pauses the host simulation or mutates installed upgrades. public sealed class CoreInspectionPanel : IDisposable { private readonly VisualElement screen,panel,grid; private readonly ScrollView statScroll,inventoryScroll,detailScroll; private readonly Label empty,detailTitle,detailBody; private readonly PrototypeSessionController session; private readonly Func canRelock; private readonly List<(Button Control,Func Value)> statRows=new List<(Button,Func)>(); private readonly Dictionary<(int,int),Button> tiles=new Dictionary<(int,int),Button>(); private readonly IVisualElementScheduledItem refreshTask,detailLayoutTask; private VisualElement detailAnchor; private Func detailText; private bool detailHovered,anchorHovered; private string inventoryIdentity; private (int,int)? focusedUpgrade; private BuildStats equippedBaseline; public bool IsOpen=>!panel.ClassListContains("hidden"); public static bool CanOpen(bool connected,bool busy,bool menuVisible,bool settingsOpen,RunPhase phase) =>connected&&!busy&&!menuVisible&&!settingsOpen&&(phase==RunPhase.Menu||phase==RunPhase.Active||phase==RunPhase.Collection); public static Rect PlaceDetail(Rect viewport,Rect anchor,Vector2 requested) { float width=Mathf.Min(requested.x,Mathf.Max(0,viewport.width-16)); float height=Mathf.Min(requested.y,Mathf.Max(0,viewport.height-16)); float left=anchor.xMax+10; if(left+width>viewport.xMax-8)left=anchor.xMin-width-10; return new Rect(Mathf.Clamp(left,viewport.xMin+8,Mathf.Max(viewport.xMin+8,viewport.xMax-width-8)), Mathf.Clamp(anchor.yMin,viewport.yMin+8,Mathf.Max(viewport.yMin+8,viewport.yMax-height-8)),width,height); } public static string Detail(UpgradeInventoryEntry entry,float maxHealth,float maxNormalShield) { var definition=UpgradeCatalog.Get(entry.UpgradeId); if(definition==null)return "Unknown installed upgrade."; var card=UpgradeCardFormatter.Format((UpgradeId)entry.UpgradeId,entry.Rank,new CoreDisplayContext(maxHealth,maxNormalShield),CorePresentationMode.Installed); return card.Primary+(string.IsNullOrEmpty(card.Secondary)?"":"\n"+card.Secondary)+"\n\n"+card.Details; } public CoreInspectionPanel(VisualElement screen,PrototypeSessionController session,Func canRelock) { this.screen=screen;this.session=session;this.canRelock=canRelock; panel=new VisualElement {name="coreInspectionPanel"};panel.AddToClassList("inspection-panel");panel.AddToClassList("hidden"); var header=new VisualElement();header.AddToClassList("inspection-header");panel.Add(header); var title=new Label("RUN INSPECTION");title.AddToClassList("inspection-title");header.Add(title); var close=new Button(()=>Close(true)){text="Close [Tab / Esc]",name="inspectionClose"};close.AddToClassList("inspection-close");header.Add(close); var note=new Label("Run continues. Hover or focus a stat or core for its installed effect and sources.");note.AddToClassList("inspection-note");panel.Add(note); var columns=new VisualElement();columns.AddToClassList("inspection-columns");panel.Add(columns); statScroll=new ScrollView(ScrollViewMode.Vertical){name="inspectionStats"};statScroll.AddToClassList("inspection-stats");columns.Add(statScroll); inventoryScroll=new ScrollView(ScrollViewMode.Vertical){name="inspectionInventory"};inventoryScroll.AddToClassList("inspection-inventory");columns.Add(inventoryScroll); var installed=new Label("INSTALLED CORES");installed.AddToClassList("inspection-section");inventoryScroll.Add(installed); var inventoryNote=new Label("Each tile is one installed core and ability recipient. Unspent cores remain on the combat HUD.");inventoryNote.AddToClassList("inspection-note");inventoryScroll.Add(inventoryNote); empty=new Label("No cores installed yet. Collect cores, then choose after everyone enters the portal.");empty.AddToClassList("inspection-empty");inventoryScroll.Add(empty); grid=new VisualElement {name="installedUpgradeGrid"};grid.AddToClassList("inspection-grid");inventoryScroll.Add(grid); detailScroll=new ScrollView(ScrollViewMode.Vertical){name="inspectionDetail"};detailScroll.AddToClassList("inspection-detail");detailScroll.AddToClassList("hidden"); detailTitle=new Label();detailTitle.AddToClassList("inspection-detail-title");detailScroll.Add(detailTitle); detailBody=new Label();detailBody.AddToClassList("inspection-detail-body");detailScroll.Add(detailBody); detailScroll.RegisterCallback(_=>detailHovered=true); detailScroll.RegisterCallback(_=>{detailHovered=false;ScheduleHideDetail();}); detailScroll.RegisterCallback(_=>ScheduleHideDetail()); screen.Add(panel);screen.Add(detailScroll); refreshTask=panel.schedule.Execute(Refresh).Every(250); detailLayoutTask=detailScroll.schedule.Execute(ResolveDetailLayout); } public bool Open() { var player=PrototypePlayerCombat.LocalPlayer;var run=PrototypeRunDirector.Instance; if(player==null||!player.IsSpawned||session==null||!session.Connected||session.Busy||!canRelock()||LocalPlayerSettings.IsInputBlocked ||!CanOpen(true,false,false,false,run!=null?run.Phase:RunPhase.Menu))return false; LocalPlayerSettings.SetInputBlocked(this,true);player.ClearHeldInputForInspection(); Cursor.lockState=CursorLockMode.None;Cursor.visible=true; panel.RemoveFromClassList("hidden");panel.BringToFront();inventoryIdentity=null; BuildStats(player);Refresh();panel.Q