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 diff --git a/Client/Simitone/Simitone.Client/UI/Panels/UILotControl.cs b/Client/Simitone/Simitone.Client/UI/Panels/UILotControl.cs index 5e73ba1..9f23c8a 100644 --- a/Client/Simitone/Simitone.Client/UI/Panels/UILotControl.cs +++ b/Client/Simitone/Simitone.Client/UI/Panels/UILotControl.cs @@ -757,10 +757,51 @@ public override void Update(UpdateState state) if (!FoundMe && ActiveEntity != null) { - vm.Context.World.State.CenterTile = new Vector2(ActiveEntity.VisualPosition.X, ActiveEntity.VisualPosition.Y); + // Send change control command to ensure sim is properly selected + // This sets PersistID, registers in ObjectQueries, sets Global 3, and centers camera + vm.SendCommand(new VMNetChangeControlCmd() { TargetID = ActiveEntity.ObjectID }); + FoundMe = true; + } + + // Fallback: if no sim with expected PersistID, take control of any available avatar + if (!FoundMe && ActiveEntity == null && vm.Context.ObjectQueries.Avatars.Count > 0) + { + var fallbackAvatar = vm.Context.ObjectQueries.Avatars.FirstOrDefault(); + if (fallbackAvatar != null) + { + // Send change control command to properly select this sim + // This sets PersistID, registers in ObjectQueries, sets Global 3, and centers camera + vm.SendCommand(new VMNetChangeControlCmd() { TargetID = fallbackAvatar.ObjectID }); + FoundMe = true; + } + } + + // Fallback for empty lots (no avatars): center on mailbox, any placed object, or lot center + if (!FoundMe && vm.Context.ObjectQueries.Avatars.Count == 0) + { + // Try to find mailbox first (GUIDs: 0xEF121974 or 0x1D95C9B0) + var landmark = vm.Entities.FirstOrDefault(x => x.Object.OBJ.GUID == 0xEF121974 || x.Object.OBJ.GUID == 0x1D95C9B0); + + // If no mailbox, try to find any object that's placed on the lot (for community lots) + if (landmark == null) + { + landmark = vm.Entities.FirstOrDefault(x => x.Position != LotTilePos.OUT_OF_WORLD && x.Position.Level == 1); + } + + if (landmark != null) + { + vm.Context.World.State.CenterTile = new Vector2(landmark.VisualPosition.X, landmark.VisualPosition.Y); + } + else + { + // Default to lot center + var lotSize = vm.Context.Architecture.Width; + vm.Context.World.State.CenterTile = new Vector2(lotSize / 2f, lotSize / 2f); + } vm.Context.World.State.ScrollAnchor = null; FoundMe = true; } + Queue.QueueOwner = ActiveEntity; }