From 8a00a7807a975a88c979b5bbc8be7b9dc52ae8ee Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 9 Aug 2026 20:20:18 +0100 Subject: [PATCH] fix: object crash when GetSuit called by nightclub npcs --- .../tso.simantics/Engine/VMSuitProvider.cs | 28 +++++++++++++------ TSOClient/tso.simantics/Entities/VMAvatar.cs | 2 +- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/TSOClient/tso.simantics/Engine/VMSuitProvider.cs b/TSOClient/tso.simantics/Engine/VMSuitProvider.cs index cc35a6713..26fd19f3d 100644 --- a/TSOClient/tso.simantics/Engine/VMSuitProvider.cs +++ b/TSOClient/tso.simantics/Engine/VMSuitProvider.cs @@ -1,7 +1,7 @@ -using FSO.Files.Formats.IFF.Chunks; +using FSO.Content; +using FSO.Files.Formats.IFF.Chunks; using FSO.SimAntics.Engine.Scopes; using FSO.SimAntics.Model; -using System; namespace FSO.SimAntics.Engine { @@ -178,23 +178,35 @@ namespace FSO.SimAntics.Engine return !(suitName == null || suitName == "" || suitName == "ADDED"); } + public static object GetSuit(VMAvatar avatar, VMSuitScope scope, ushort id) + { + // This isn't fantastic - when this is called from SetPersonData we don't have info on the active thread. + // The object scope can be lost if coming from another thread right now, but other scopes will work. + var context = avatar.Thread.Stack.LastOrDefault(); + return GetSuit(avatar, context?.CodeOwner, avatar.Thread.Context.Globals, scope, id); + } + public static object GetSuit(VMStackFrame context, VMSuitScope scope, ushort id) { - STR suitTable = null; + return GetSuit((VMAvatar)context.Caller, context.CodeOwner, context.Global, scope, id); + } - var avatar = (VMAvatar)context.Caller; + public static object GetSuit(VMAvatar avatar, GameObject codeOwner, GameGlobal global, VMSuitScope scope, ushort id) + { + STR suitTable = null; + var vm = avatar.Thread.Context.VM; switch (scope) { case VMSuitScope.Object: - suitTable = context.CodeOwner.Resource.Get(304); + suitTable = codeOwner.Resource.Get(304); break; case VMSuitScope.Global: - suitTable = context.Global.Resource.Get(304); + suitTable = global.Resource.Get(304); break; case VMSuitScope.Person: //get outfit from person - if (context.VM.TS1) return GetPersonSuitTS1((VMAvatar)context.Caller, id); + if (vm.TS1) return GetPersonSuitTS1(avatar, id); var type = (VMPersonSuits)id; bool male = (avatar.GetPersonData(VMPersonDataVariable.Gender) == 0); @@ -209,7 +221,7 @@ namespace FSO.SimAntics.Engine case VMPersonSuits.DefaultSwimwear: return avatar.DefaultSuits.Swimwear.ID; case VMPersonSuits.JobOutfit: - if (context.VM.TS1) return null; + if (vm.TS1) return null; var job = avatar.GetPersonData(VMPersonDataVariable.OnlineJobID); if (job < 1 || job > 5) return null; var level = Math.Max(0, Math.Min(2, ((int)avatar.GetPersonData(VMPersonDataVariable.OnlineJobGrade) + 1) / 4)); diff --git a/TSOClient/tso.simantics/Entities/VMAvatar.cs b/TSOClient/tso.simantics/Entities/VMAvatar.cs index 774c57a93..b922feeb5 100644 --- a/TSOClient/tso.simantics/Entities/VMAvatar.cs +++ b/TSOClient/tso.simantics/Entities/VMAvatar.cs @@ -949,7 +949,7 @@ namespace FSO.SimAntics if (Thread.Context.VM.TS1) BodyOutfit = VMSuitProvider.GetPersonSuitTS1(this, (ushort)value); else { - var suit = VMSuitProvider.GetSuit(Thread.Stack.LastOrDefault(), Engine.Scopes.VMSuitScope.Person, (ushort)value); + var suit = VMSuitProvider.GetSuit(this, Engine.Scopes.VMSuitScope.Person, (ushort)value); if (suit is VMOutfitReference) BodyOutfit = suit as VMOutfitReference; if (suit is ulong) BodyOutfit = new VMOutfitReference((ulong)suit); }