From 95fc53ee123389dcd3226757acdad15ce8bfda77 Mon Sep 17 00:00:00 2001 From: Alex Yong Date: Fri, 7 Nov 2025 05:27:58 +0000 Subject: [PATCH 01/26] Testing out github actions workflow --- .github/workflows/build.yml | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..73d1437 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,59 @@ +name: Build Simitone + +on: + workflow_dispatch: + inputs: + configuration: + description: 'Build configuration' + required: false + default: 'Release' + type: choice + options: + - Release + - Debug + +jobs: + build: + runs-on: windows-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup .NET 9 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '9.0.x' + + - name: Run Protobuild + shell: pwsh + run: | + cd FreeSO/Other/libs/FSOMonoGame/ + ./protobuild.exe --generate + continue-on-error: true + + - name: Restore Simitone dependencies + run: dotnet restore Client/Simitone/Simitone.sln + + - name: Restore FreeSO dependencies + run: dotnet restore FreeSO/TSOClient/FreeSO.sln + continue-on-error: true + + - name: Restore Roslyn dependencies + shell: pwsh + run: | + cd FreeSO/TSOClient/FSO.SimAntics.JIT.Roslyn/ + dotnet restore + continue-on-error: true + + - name: Build + run: dotnet build Client/Simitone/Simitone.sln -c ${{ inputs.configuration || 'Release' }} --no-restore + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: SimitoneWindows-${{ inputs.configuration || 'Release' }} + path: Client/Simitone/Simitone.Windows/bin/${{ inputs.configuration || 'Release' }}/net9.0-windows/ + if-no-files-found: error From a65dcc478f6e82ec0a8d9923a8a96304da0153eb Mon Sep 17 00:00:00 2001 From: Alex Yong Date: Tue, 20 Jan 2026 20:30:35 +0000 Subject: [PATCH 02/26] First pass with eyedropper tool --- .../uigraphics/live/cat/cat_eyedropper.png | Bin 0 -> 485 bytes .../Panels/LiveSubpanels/UIBuyBrowsePanel.cs | 69 ++++++++++++++++++ .../UI/Panels/UIObjectHolder.cs | 23 +++++- 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_eyedropper.png diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_eyedropper.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_eyedropper.png new file mode 100644 index 0000000000000000000000000000000000000000..3c9ad47796b8ac6caf3089904fa078c9241b7a6c GIT binary patch literal 485 zcmeAS@N?(olHy`uVBq!ia0vp^jv&mz1|<8wpL7LMEX7WqAsj$Z!;#Vf4nJ za0`Jja>QWe}Xi&D$;i?WLqoP*6??Ag@Az`$th>EaktG3V{gi@t{qL|88fA7QxL z(075CL%M`nqH$sZvzfx(2Ei9>IRbW*{sb%))#%eMTz^I0{_9bzO) z>G}i(4yL9C1qBW))B?Rje6qhjUhYj^y?kZG-fx||<&)iWuFvyWJN1GUt3lt^)#qHk zv9K^YItT~=sXJnC_8xxb^Y+g7XP;X8?aybZsq%SC?|E4F@>nw84QU54PtF!U%LYw$ zZn;%n3TsOF@~34@=?#=@Np76yIzN!<;ZBG1i_U9&)!KXXa_g_hK5wsnm}NVCI``N3 z<=1MRy1&o7)%;LAZEH%h2*OTBhkrGWcc*SJzr*b_Q_nlh|LoKP)06YCdIv7}KswLQ WT+!jDBp)!q89ZJ6T-G@yGywn;!>&{S literal 0 HcmV?d00001 diff --git a/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs b/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs index d331844..de3a430 100644 --- a/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs +++ b/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs @@ -329,6 +329,10 @@ static UIBuyBrowsePanel() public bool HoldingEvents; + // Eyedropper tool support + public UICatButton EyedropperButton; + private bool EyedropperMode; + public UIBuyBrowsePanel(TS1GameScreen screen, sbyte category, UICatalogMode mode) : base(screen) { CatContainer = new UITouchScroll(() => FilterCategory?.Count() ?? 0, CatalogElemProvider); CatContainer.ItemWidth = 90; @@ -347,7 +351,70 @@ public UIBuyBrowsePanel(TS1GameScreen screen, sbyte category, UICatalogMode mode screen.LotControl.ObjectHolder.OnPickup += ObjectHolder_OnPickup; screen.LotControl.ObjectHolder.OnPutDown += ObjectHolder_OnPutDown; screen.LotControl.ObjectHolder.OnDelete += ObjectHolder_OnDelete; + screen.LotControl.ObjectHolder.OnEyedropperPick += ObjectHolder_OnEyedropperPick; HoldingEvents = true; + + // Create eyedropper button - TODO: Replace cat_eyedropper.png with a proper eyedropper icon + var ui = Content.Get().CustomUI; + EyedropperButton = new UICatButton(ui.Get("cat_eyedropper.png").Get(GameFacade.GraphicsDevice)); + EyedropperButton.Y = 8; + EyedropperButton.X = 8; // Position at left side + EyedropperButton.OnButtonClick += ToggleEyedropper; + Add(EyedropperButton); + } + + private void ToggleEyedropper(UIElement btn) + { + EyedropperMode = !EyedropperMode; + EyedropperButton.Selected = EyedropperMode; + Game.LotControl.ObjectHolder.EyedropperMode = EyedropperMode; + } + + private void ObjectHolder_OnEyedropperPick(uint guid) + { + // Turn off eyedropper mode + EyedropperMode = false; + EyedropperButton.Selected = false; + Game.LotControl.ObjectHolder.EyedropperMode = false; + + // Select the item in catalog + SelectItemByGUID(guid); + } + + /// + /// Finds an item by GUID in the current catalog and selects it. + /// + public void SelectItemByGUID(uint guid) + { + if (FilterCategory == null) return; + + // Find the item index in the current filtered category + int index = 0; + foreach (var item in FilterCategory) + { + if (item.Item.GUID == guid) + { + // Found it - select it + Selected(index); + return; + } + index++; + } + + // If not found in current category, search in FullCategory + index = 0; + foreach (var item in FullCategory) + { + if (item.Item.GUID == guid) + { + // Found it - need to show all items first, then select + FilterCategory = FullCategory; + CatContainer.Reset(); + Selected(index); + return; + } + index++; + } } private void ObjectHolder_OnDelete(UIObjectSelection holding, UpdateState state) @@ -392,6 +459,8 @@ private void RemoveEvents() Game.LotControl.ObjectHolder.OnPickup -= ObjectHolder_OnPickup; Game.LotControl.ObjectHolder.OnPutDown -= ObjectHolder_OnPutDown; Game.LotControl.ObjectHolder.OnDelete -= ObjectHolder_OnDelete; + Game.LotControl.ObjectHolder.OnEyedropperPick -= ObjectHolder_OnEyedropperPick; + Game.LotControl.ObjectHolder.EyedropperMode = false; Game.LotControl.QueryPanel.Active = false; Game.Frontend.MainPanel.SetSubpanelPickup(1f); diff --git a/Client/Simitone/Simitone.Client/UI/Panels/UIObjectHolder.cs b/Client/Simitone/Simitone.Client/UI/Panels/UIObjectHolder.cs index 457dbc7..a01eefc 100644 --- a/Client/Simitone/Simitone.Client/UI/Panels/UIObjectHolder.cs +++ b/Client/Simitone/Simitone.Client/UI/Panels/UIObjectHolder.cs @@ -50,6 +50,11 @@ public class UIObjectHolder //controls the object holder interface public bool Roommate = true; public bool UseNet = false; + // Eyedropper tool support + public bool EyedropperMode; + public event EyedropperEventHandler OnEyedropperPick; + public delegate void EyedropperEventHandler(uint guid); + public event HolderEventHandler OnPickup; public event HolderEventHandler OnDelete; public event HolderEventHandler OnPutDown; @@ -412,7 +417,23 @@ public void Update(UpdateState state, bool scrolled) { //not holding an object, but one can be selected var newHover = World.GetObjectIDAtScreenPos(state.MouseState.X, state.MouseState.Y, GameFacade.GraphicsDevice); - if (MouseClicked && (newHover != 0) && (vm.GetObjectById(newHover) is VMGameObject)) + + // Eyedropper mode - pick object GUID instead of selecting to move + if (EyedropperMode && newHover != 0 && vm.GetObjectById(newHover) is VMGameObject) + { + var obj = vm.GetObjectById(newHover); + uint guid = obj.Object.OBJ.GUID; + if (obj.MasterDefinition != null) guid = obj.MasterDefinition.GUID; + + var catalogItem = Content.Get().WorldCatalog.GetItemByGUID(guid); + if (catalogItem != null) + { + OnEyedropperPick?.Invoke(guid); + HITVM.Get().PlaySoundEvent(UISounds.ObjectPlace); + } + // Don't process normal click when in eyedropper mode + } + else if (MouseClicked && (newHover != 0) && (vm.GetObjectById(newHover) is VMGameObject)) { var objGroup = vm.GetObjectById(newHover).MultitileGroup; var objBasePos = objGroup.BaseObject.Position; From da634ef74d0588db674f03d531bee97a926308e6 Mon Sep 17 00:00:00 2001 From: Alex Yong Date: Tue, 20 Jan 2026 22:16:01 +0000 Subject: [PATCH 03/26] adding image reference --- Client/Simitone/Simitone.Client/Simitone.Client.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Client/Simitone/Simitone.Client/Simitone.Client.csproj b/Client/Simitone/Simitone.Client/Simitone.Client.csproj index 1e2baf0..fade365 100644 --- a/Client/Simitone/Simitone.Client/Simitone.Client.csproj +++ b/Client/Simitone/Simitone.Client/Simitone.Client.csproj @@ -308,6 +308,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest From 7a46b27b6901d5da90aaf3beb1dcbb6da490e6e7 Mon Sep 17 00:00:00 2001 From: Alex Yong Date: Wed, 21 Jan 2026 14:28:05 +0000 Subject: [PATCH 04/26] Giving this a shot --- .../Panels/LiveSubpanels/UIBuyBrowsePanel.cs | 104 +++++++++++++++--- .../Simitone.Client/UI/Panels/UIMainPanel.cs | 3 + 2 files changed, 92 insertions(+), 15 deletions(-) diff --git a/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs b/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs index de3a430..52cdf37 100644 --- a/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs +++ b/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs @@ -332,6 +332,7 @@ static UIBuyBrowsePanel() // Eyedropper tool support public UICatButton EyedropperButton; private bool EyedropperMode; + private bool CheckedPendingEyedropper; public UIBuyBrowsePanel(TS1GameScreen screen, sbyte category, UICatalogMode mode) : base(screen) { CatContainer = new UITouchScroll(() => FilterCategory?.Count() ?? 0, CatalogElemProvider); @@ -361,6 +362,25 @@ public UIBuyBrowsePanel(TS1GameScreen screen, sbyte category, UICatalogMode mode EyedropperButton.X = 8; // Position at left side EyedropperButton.OnButtonClick += ToggleEyedropper; Add(EyedropperButton); + + } + + /// + /// Checks if there's a pending eyedropper GUID to select after a category switch. + /// Called from Update since Parent may not be set during constructor. + /// + private void CheckPendingEyedropperSelection() + { + if (CheckedPendingEyedropper) return; + CheckedPendingEyedropper = true; + + var mainPanel = Parent as UIMainPanel; + if (mainPanel?.PendingEyedropperGUID != null) + { + var guid = mainPanel.PendingEyedropperGUID.Value; + mainPanel.PendingEyedropperGUID = null; + SelectItemInCurrentCategory(guid); + } } private void ToggleEyedropper(UIElement btn) @@ -382,38 +402,89 @@ private void ObjectHolder_OnEyedropperPick(uint guid) } /// - /// Finds an item by GUID in the current catalog and selects it. + /// Finds an item by GUID in the catalog and selects it. + /// If the item is in a different category, switches to that category first. /// public void SelectItemByGUID(uint guid) { - if (FilterCategory == null) return; + // First, look up the item in the world catalog to find its category + var catalogItem = Content.Get().WorldCatalog.GetItemByGUID(guid); + if (catalogItem == null) return; - // Find the item index in the current filtered category - int index = 0; - foreach (var item in FilterCategory) + var targetCategory = catalogItem.Value.Category; + + // Check if item is in a different category + if (targetCategory != Category) { - if (item.Item.GUID == guid) + // Store the GUID to select after category switch + var mainPanel = Parent as UIMainPanel; + if (mainPanel != null) { - // Found it - select it - Selected(index); - return; + mainPanel.PendingEyedropperGUID = guid; + // Switch to the target category - this will create a new panel + mainPanel.Switcher.Select(targetCategory); } - index++; + return; } - // If not found in current category, search in FullCategory - index = 0; + // Item is in this category - select it + SelectItemInCurrentCategory(guid); + } + + /// + /// Selects an item by GUID within the current category. + /// + private void SelectItemInCurrentCategory(uint guid) + { + // If we're still choosing subcategory, skip to show all items + if (ChoosingSub) + { + ChoosingSub = false; + foreach (var btn in SelButtons) + { + Remove(btn); + } + foreach (var label in SelLabels) + { + Remove(label); + } + SelButtons.Clear(); + SelLabels.Clear(); + + // Show all items in this category + FilterCategory = FullCategory.Where(x => GetSubsort(x.Item) > 0); + CatContainer.Opacity = 1f; + CatContainer.Reset(); + } + + // Search in FilterCategory first + if (FilterCategory != null) + { + int index = 0; + foreach (var item in FilterCategory) + { + if (item.Item.GUID == guid) + { + Selected(index); + return; + } + index++; + } + } + + // If not found in filtered, search in FullCategory + int fullIndex = 0; foreach (var item in FullCategory) { if (item.Item.GUID == guid) { - // Found it - need to show all items first, then select + // Show all items and select FilterCategory = FullCategory; CatContainer.Reset(); - Selected(index); + Selected(fullIndex); return; } - index++; + fullIndex++; } } @@ -853,6 +924,9 @@ public override void GameResized() public override void Update(UpdateState state) { + // Check for pending eyedropper selection (after category switch) + CheckPendingEyedropperSelection(); + Invalidate(); var first = SelButtons.FirstOrDefault(); if (first != null && first.Opacity == 0) diff --git a/Client/Simitone/Simitone.Client/UI/Panels/UIMainPanel.cs b/Client/Simitone/Simitone.Client/UI/Panels/UIMainPanel.cs index 1d98575..e20b3fb 100644 --- a/Client/Simitone/Simitone.Client/UI/Panels/UIMainPanel.cs +++ b/Client/Simitone/Simitone.Client/UI/Panels/UIMainPanel.cs @@ -57,6 +57,9 @@ public float CurWidth public event Action OnEndSelect; public event Action ModeChanged; + // Eyedropper tool support - stores GUID to select after category switch + public uint? PendingEyedropperGUID; + public string[] FloorNames = new string[] { "1st", From 859c1847b9510af17f57e67f2dc607094274855a Mon Sep 17 00:00:00 2001 From: Alex Yong Date: Wed, 21 Jan 2026 14:59:40 +0000 Subject: [PATCH 05/26] checkpoint empty commit From a070a7efd2ead2ca1734574e1f629ee766b87dbd Mon Sep 17 00:00:00 2001 From: Alex Yong Date: Wed, 21 Jan 2026 16:01:25 +0000 Subject: [PATCH 06/26] UI adjustments --- .../uigraphics/desktop/d_live_eyedropper.png | Bin 0 -> 351 bytes .../Simitone.Client/Simitone.Client.csproj | 6 ++-- .../UI/Controls/UITouchScroll.cs | 10 ++++++ .../UI/Panels/Desktop/UIDesktopUCP.cs | 34 ++++++++++++++++++ .../Panels/LiveSubpanels/UIBuyBrowsePanel.cs | 25 ++----------- .../UI/Panels/UIObjectHolder.cs | 1 - 6 files changed, 50 insertions(+), 26 deletions(-) create mode 100644 Client/Simitone/Simitone.Client/Content/uigraphics/desktop/d_live_eyedropper.png diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/desktop/d_live_eyedropper.png b/Client/Simitone/Simitone.Client/Content/uigraphics/desktop/d_live_eyedropper.png new file mode 100644 index 0000000000000000000000000000000000000000..30b7d74955852d5879eafcb94808ac93dbf21bae GIT binary patch literal 351 zcmV-l0igbgP)RlHMaIeAEF{KIcBfS;4j3O0) znFf4xv7~|sHR!dAD814n{s2xrb8B;{pxs8u8jt*qo`P%$t#&)9(0J4(qYU_LbRsFZ xJK)$^!*<(~EFZMsF3g1<`Lky4y-p*8;ujN~kmM>eO)~%h002ovPDHLkV1g&Mlnwv@ literal 0 HcmV?d00001 diff --git a/Client/Simitone/Simitone.Client/Simitone.Client.csproj b/Client/Simitone/Simitone.Client/Simitone.Client.csproj index fade365..59c536b 100644 --- a/Client/Simitone/Simitone.Client/Simitone.Client.csproj +++ b/Client/Simitone/Simitone.Client/Simitone.Client.csproj @@ -89,6 +89,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -308,9 +311,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest diff --git a/Client/Simitone/Simitone.Client/UI/Controls/UITouchScroll.cs b/Client/Simitone/Simitone.Client/UI/Controls/UITouchScroll.cs index af2da19..b7b8fc8 100644 --- a/Client/Simitone/Simitone.Client/UI/Controls/UITouchScroll.cs +++ b/Client/Simitone/Simitone.Client/UI/Controls/UITouchScroll.cs @@ -197,6 +197,16 @@ public void SetScroll(float value) Scroll = value; } + public void ScrollToItem(int itemIndex) + { + // Center the item in the visible area + var targetScroll = itemIndex * ItemWidth - (GetPAxis(Size) / 2) + (ItemWidth / 2); + // Clamp to valid scroll range + var length = LengthProvider(); + Scroll = Math.Max(-Margin, Math.Min(length * ItemWidth - GetPAxis(Size) + Margin, targetScroll)); + ScrollVelocity = 0; // Stop any ongoing momentum + } + public override void Draw(UISpriteBatch batch) { if (!Visible) return; diff --git a/Client/Simitone/Simitone.Client/UI/Panels/Desktop/UIDesktopUCP.cs b/Client/Simitone/Simitone.Client/UI/Panels/Desktop/UIDesktopUCP.cs index 74602ab..276395b 100644 --- a/Client/Simitone/Simitone.Client/UI/Panels/Desktop/UIDesktopUCP.cs +++ b/Client/Simitone/Simitone.Client/UI/Panels/Desktop/UIDesktopUCP.cs @@ -25,6 +25,7 @@ public class UIDesktopUCP : UICachedContainer { public UIImage Background; public UIImage FriendIcon; + public UIButton EyedropperButton; public UIButton LiveButton; public UIButton BuyButton; @@ -90,6 +91,16 @@ public UIDesktopUCP(TS1GameScreen screen) FriendIcon = new UIImage(ui.Get("d_live_friend.png").Get(gd)) { Position = new Vector2(156, 186) }; Add(FriendIcon); + // Eyedropper button - visible only in Buy/Build mode + Add(EyedropperButton = new UIStencilButton(ui.Get("d_live_eyedropper.png").Get(gd)) + { + Position = new Vector2(196, 186), + Shadow = true, + ShadowParam = sDir + }); + EyedropperButton.OnButtonClick += ToggleEyedropper; + EyedropperButton.Visible = false; // Hidden by default (LIVE mode) + Add(LiveButton = new UIButton(ui.Get("d_live_live.png").Get(gd)) { Position = new Vector2(15, 2) }); Add(BuyButton = new UIButton(ui.Get("d_live_buy.png").Get(gd)) { Position = new Vector2(107, 27) }); Add(BuildButton = new UIButton(ui.Get("d_live_build.png").Get(gd)) { Position = new Vector2(179, 80) }); @@ -322,6 +333,12 @@ public override void Update(UpdateState state) if (LastZoom != Game.ZoomLevel) UpdateZoomButton(); + // Sync eyedropper button state with ObjectHolder (for when it's auto-disabled after pick) + if (EyedropperButton.Visible && Game.LotControl?.ObjectHolder != null) + { + EyedropperButton.Selected = Game.LotControl.ObjectHolder.EyedropperMode; + } + base.Update(state); //KEY SHORTCUTS @@ -390,12 +407,29 @@ public void UpdateZoomButton() LastZoom = Game.ZoomLevel; } + private void ToggleEyedropper(UIElement btn) + { + var holder = Game.LotControl.ObjectHolder; + holder.EyedropperMode = !holder.EyedropperMode; + EyedropperButton.Selected = holder.EyedropperMode; + } + public void SetMode(UIMainPanelMode mode) { LiveButton.Selected = mode == UIMainPanelMode.LIVE; BuyButton.Selected = mode == UIMainPanelMode.BUY; BuildButton.Selected = mode == UIMainPanelMode.BUILD; OptionsButton.Selected = mode == UIMainPanelMode.OPTIONS; + + // Show eyedropper only in BUY or BUILD mode + EyedropperButton.Visible = (mode == UIMainPanelMode.BUY || mode == UIMainPanelMode.BUILD); + // Reset selection when changing modes + if (!EyedropperButton.Visible) + { + EyedropperButton.Selected = false; + if (Game.LotControl?.ObjectHolder != null) + Game.LotControl.ObjectHolder.EyedropperMode = false; + } } public void DisplayChange(int change) diff --git a/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs b/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs index 52cdf37..f0644be 100644 --- a/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs +++ b/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs @@ -329,9 +329,6 @@ static UIBuyBrowsePanel() public bool HoldingEvents; - // Eyedropper tool support - public UICatButton EyedropperButton; - private bool EyedropperMode; private bool CheckedPendingEyedropper; public UIBuyBrowsePanel(TS1GameScreen screen, sbyte category, UICatalogMode mode) : base(screen) { @@ -354,15 +351,6 @@ public UIBuyBrowsePanel(TS1GameScreen screen, sbyte category, UICatalogMode mode screen.LotControl.ObjectHolder.OnDelete += ObjectHolder_OnDelete; screen.LotControl.ObjectHolder.OnEyedropperPick += ObjectHolder_OnEyedropperPick; HoldingEvents = true; - - // Create eyedropper button - TODO: Replace cat_eyedropper.png with a proper eyedropper icon - var ui = Content.Get().CustomUI; - EyedropperButton = new UICatButton(ui.Get("cat_eyedropper.png").Get(GameFacade.GraphicsDevice)); - EyedropperButton.Y = 8; - EyedropperButton.X = 8; // Position at left side - EyedropperButton.OnButtonClick += ToggleEyedropper; - Add(EyedropperButton); - } /// @@ -383,18 +371,9 @@ private void CheckPendingEyedropperSelection() } } - private void ToggleEyedropper(UIElement btn) - { - EyedropperMode = !EyedropperMode; - EyedropperButton.Selected = EyedropperMode; - Game.LotControl.ObjectHolder.EyedropperMode = EyedropperMode; - } - private void ObjectHolder_OnEyedropperPick(uint guid) { - // Turn off eyedropper mode - EyedropperMode = false; - EyedropperButton.Selected = false; + // Turn off eyedropper mode (button state is synced in UIDesktopUCP) Game.LotControl.ObjectHolder.EyedropperMode = false; // Select the item in catalog @@ -466,6 +445,7 @@ private void SelectItemInCurrentCategory(uint guid) if (item.Item.GUID == guid) { Selected(index); + CatContainer.ScrollToItem(index); return; } index++; @@ -482,6 +462,7 @@ private void SelectItemInCurrentCategory(uint guid) FilterCategory = FullCategory; CatContainer.Reset(); Selected(fullIndex); + CatContainer.ScrollToItem(fullIndex); return; } fullIndex++; diff --git a/Client/Simitone/Simitone.Client/UI/Panels/UIObjectHolder.cs b/Client/Simitone/Simitone.Client/UI/Panels/UIObjectHolder.cs index a01eefc..d716b51 100644 --- a/Client/Simitone/Simitone.Client/UI/Panels/UIObjectHolder.cs +++ b/Client/Simitone/Simitone.Client/UI/Panels/UIObjectHolder.cs @@ -429,7 +429,6 @@ public void Update(UpdateState state, bool scrolled) if (catalogItem != null) { OnEyedropperPick?.Invoke(guid); - HITVM.Get().PlaySoundEvent(UISounds.ObjectPlace); } // Don't process normal click when in eyedropper mode } From 8f6fb4e319595647bf576b986b1654061512daf7 Mon Sep 17 00:00:00 2001 From: Alex Yong Date: Wed, 21 Jan 2026 16:59:41 +0000 Subject: [PATCH 07/26] some more tweaks --- .../UI/Panels/Desktop/UIDesktopUCP.cs | 6 +++--- .../Panels/LiveSubpanels/UIBuyBrowsePanel.cs | 18 ++++++++++++++++++ README.md | 4 ++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Client/Simitone/Simitone.Client/UI/Panels/Desktop/UIDesktopUCP.cs b/Client/Simitone/Simitone.Client/UI/Panels/Desktop/UIDesktopUCP.cs index 276395b..efd47e7 100644 --- a/Client/Simitone/Simitone.Client/UI/Panels/Desktop/UIDesktopUCP.cs +++ b/Client/Simitone/Simitone.Client/UI/Panels/Desktop/UIDesktopUCP.cs @@ -94,7 +94,7 @@ public UIDesktopUCP(TS1GameScreen screen) // Eyedropper button - visible only in Buy/Build mode Add(EyedropperButton = new UIStencilButton(ui.Get("d_live_eyedropper.png").Get(gd)) { - Position = new Vector2(196, 186), + Position = new Vector2(196, 180), Shadow = true, ShadowParam = sDir }); @@ -421,8 +421,8 @@ public void SetMode(UIMainPanelMode mode) BuildButton.Selected = mode == UIMainPanelMode.BUILD; OptionsButton.Selected = mode == UIMainPanelMode.OPTIONS; - // Show eyedropper only in BUY or BUILD mode - EyedropperButton.Visible = (mode == UIMainPanelMode.BUY || mode == UIMainPanelMode.BUILD); + // Show eyedropper only in BUY mode (Build mode not yet supported) + EyedropperButton.Visible = (mode == UIMainPanelMode.BUY); // Reset selection when changing modes if (!EyedropperButton.Visible) { diff --git a/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs b/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs index f0644be..f10316e 100644 --- a/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs +++ b/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs @@ -395,6 +395,24 @@ public void SelectItemByGUID(uint guid) // Check if item is in a different category if (targetCategory != Category) { + // Validate that the target category is valid for the current mode + // Buy mode uses categories 0-7, Build mode uses different categories + if (Mode == UICatalogMode.Build) + { + // Build mode - can't switch to buy categories via eyedropper + // Build categories are handled differently (13-18), so just return + return; + } + else + { + // Buy mode - only categories 0-7 are valid + if (targetCategory < 0 || targetCategory > 7) + { + // This item is in a build category, can't select in buy mode + return; + } + } + // Store the GUID to select after category switch var mainPanel = Parent as UIMainPanel; if (mainPanel != null) diff --git a/README.md b/README.md index 54e32fd..9a16c53 100644 --- a/README.md +++ b/README.md @@ -27,3 +27,7 @@ On modern operating systems, The Sims has a few nagging issues that make it less Simitone -> Semitone -> musical term -> C# -> a note Further questions can be directed at my PR manager, uh, ... burglar cop. + +# Attributions + +Icon for Eyedropper tool: eyedropper icon by Icons8 From a37123abbd9648a55a668d2f3c4318785cab6013 Mon Sep 17 00:00:00 2001 From: Alex Yong Date: Wed, 21 Jan 2026 17:47:01 +0000 Subject: [PATCH 08/26] adding cursor icon for eyedropper, tool tips, and more UI adjustments. --- .../Content/Cursors/eyedropper.cur | Bin 0 -> 3782 bytes .../uigraphics/desktop/d_live_eyedropper.png | Bin 351 -> 956 bytes .../Simitone/Simitone.Client/SimitoneGame.cs | 2 + .../UI/Panels/Desktop/UIDesktopUCP.cs | 39 ++++++------ .../UI/Panels/UIObjectHolder.cs | 10 ++- .../Simitone.Client/Utils/SimitoneCursors.cs | 59 ++++++++++++++++++ 6 files changed, 91 insertions(+), 19 deletions(-) create mode 100644 Client/Simitone/Simitone.Client/Content/Cursors/eyedropper.cur create mode 100644 Client/Simitone/Simitone.Client/Utils/SimitoneCursors.cs diff --git a/Client/Simitone/Simitone.Client/Content/Cursors/eyedropper.cur b/Client/Simitone/Simitone.Client/Content/Cursors/eyedropper.cur new file mode 100644 index 0000000000000000000000000000000000000000..692bbc10ad1e397e80c222d695302aacfd5550a3 GIT binary patch literal 3782 zcmeH}-%1-n6vn4`5sKE-TEz=7r6BYnLi;M}a|lW5QwTnUzJc+=QY(eF6fg85`UKId zMTbs*pYF=K*-4Ic*WP%-Z)bAm=Nf*@L5@LT;I9fS?3m=b02_|YlHHN*Nn|i z-*KCGzWlcp2q7%OH#mm(PjMru=P%kU5<0uE0dMQN2edUug}gg(1zFtC*@l^_{n7lM znslU1e-74R4>Whh56~l1S)jKH(!YdeUN{~5O{>s)RPC*~Dc177>XXx%%l5xgZ*+Br z6njPAI=%5*0G%PNcj3l~^RsYd(H~cX#>BNO)W}!wzBl7^En@}U2m4(Z zfc^7i`hPh5v-o>+)J;OPxk?vPAFUQTvGKNOpd&DG?=vuTV)u4S8 z(T}B1Cuv&75kCMur`dmnm|vDpEV|YjMCXj&XI4#Xozc5z)q6}`>aBBI7KbGN`8>z& zN80ci&fq$2d$hHA#_r`gf42gxz0VlZAGDwm{KZu8=l$R>2QcjTVe-i*n=PMwYW0$H z3m6e5E?|PI7FxiJV1u+}PQD)oq&N#aB8wRqxP?KOkzv*x37~0_nIRD+5xzcF$@#f@ zi7EL>sd^Q;1t47vHWgMtW^QUpqC!P(PF}H9g{=};g%ywu64qBz04piUwpEJo4N!2- zFG^J~(=*UBP_pAvP*AWbN=dT{a&d!d2l8x{GD=Dctn~HE%ggo3jrH=2()A53EiLs8 zjP#9+bb%^#i!1X=5-W7`ij^UTz|3(;Elw`VEGWs$&r<-Io0ybeT4JlD1hNPYAnq*5 zOhed|R}A$Q(1ZFQ8GS=N1AVyJK&>_)Q7iwV%v7MwAoJ}EZNMr~#Gv-r=z}araty?$ zU{Rn~?YM08;lXCdB^mdS9T>6LyI8u2RA5JJe*Im7$Y-`-$tlA$Q`}dWvFYnY-T!Kz3mKB|Q z{c>l>I<*RMlisz0aoV{JJD&OKelpxp!KfY=Zzp-^=H(OT8y?*jHtwFRJBQ2h4*MNO z?gm?o_b}%H9iZvAFwOAM-y1@QWNY|yHkn^2di?0W%h{wO zQCs$1V>Ycm+cx`1^-FKVv(+mD_&u}#Kg$%Z6xE5(I`@%3)NRkdS8HvOK}p!t)z4*} HQ$iB}hUqhH delta 338 zcmV-Y0j>VL2j2oAiBL{Q4GJ0x0000DNk~Le0000U0000U2nGNE06Q?QqLIEBe-TMU zK~#90?Ulhw!$1%~_bMn-HKAw0|I+?Q8vjD@BlH(~uq29LL-El3gq}nXPX^`?>^5xL z$x0ya64+hl?QCXeof{bE8#+gAyVPX zN?g*7jfFG?s*d8hrL+p%Kygr2e*+1y(qQkv^v{@&SK!=Kx??Vmv_&q`GiUfu(|~nC z8CzQE-Cp)~yj|7>0h|DDdt~Ze7YuN(!PYUQ3GgGm6}XHd6@Zxrd~~s-f(SL}wTmdd z(j)!=PCavLbE%-+M#vhE{EeQ1YzVD(JE_ok)Fq<~_-u3{DY!e}*jdANDBF`PAGF{u k%!MBLvu5wTP9uZj7ZaS2