diff --git a/Assets/Scenes/Neighborhood.unity b/Assets/Scenes/Neighborhood.unity index 7f390557..e2598f50 100644 --- a/Assets/Scenes/Neighborhood.unity +++ b/Assets/Scenes/Neighborhood.unity @@ -302,6 +302,51 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: e110dc8964f72d54e82c846c2a6a37c8, type: 3} m_Name: m_EditorClassIdentifier: +--- !u!1 &641803311 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 641803313} + - component: {fileID: 641803312} + m_Layer: 0 + m_Name: Neighborhood Simulation + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &641803312 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 641803311} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 695349b682404ab4281ad85c1612b41d, type: 3} + m_Name: + m_EditorClassIdentifier: + SimulationContext: 2 + TickRate: 20 +--- !u!4 &641803313 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 641803311} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 384.94568, y: 523.42, z: 24.198853} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 8 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &779771439 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scenes/Startup.unity b/Assets/Scenes/Startup.unity index c5f1a95c..feafcc97 100644 --- a/Assets/Scenes/Startup.unity +++ b/Assets/Scenes/Startup.unity @@ -1376,7 +1376,7 @@ MonoBehaviour: InitialLoadScreenReiaPlayer: {fileID: 1690878381} EnableReia: 1 LoadObjects: 1 - StreamReia: 0 + StreamReia: 1 --- !u!1 &1690878377 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/OpenTS2/Content/DBPF/ObjectDefinitionAsset.cs b/Assets/Scripts/OpenTS2/Content/DBPF/ObjectDefinitionAsset.cs index 70252b20..f20218c5 100644 --- a/Assets/Scripts/OpenTS2/Content/DBPF/ObjectDefinitionAsset.cs +++ b/Assets/Scripts/OpenTS2/Content/DBPF/ObjectDefinitionAsset.cs @@ -12,6 +12,7 @@ public class ObjectDefinitionAsset : AbstractAsset { // TODO: Person and Template object types seem to use 0x80 as the SG instance id for some reason. public SemiGlobalAsset SemiGlobal => ContentProvider.Get().GetAsset(new ResourceKey(1, GlobalTGI.GroupID, TypeIDs.SEMIGLOBAL)); + public ObjectFunctionsAsset Functions => ContentProvider.Get().GetAsset(new ResourceKey(GlobalTGI.InstanceID, GlobalTGI.GroupID, TypeIDs.OBJF)); public string FileName; public enum FieldNames @@ -58,7 +59,7 @@ public enum FieldNames RoomSortflags, FunctionSortFlags, CatalogStringsIDPointer, - IsGlobalSimObj, + GlobalSimObject, ToolTipNameType, TemplateVer, NicenessMultiplier, @@ -73,15 +74,15 @@ public enum FieldNames CatalogPopupID, IgnoreCurrentModelIndexInIcons, LevelOffset, - HasShadow, + ShadowType, NumAttributes, NumObjArrays, - unused1, + ForSaleFlags, FrontDirection, unused2, - MultiTileLeadObj, - ExpansionFlag, - unused3, + MultiTileLeadObject, + ValidEPFlags1, + ValidEPFlags2, ChairEntryFlags, TileWidth, InhibitSuitCopying, @@ -91,41 +92,43 @@ public enum FieldNames ObjModelGUID1, ObjModelGUID2, BuildModeSubsort, - unused4, - unused5, + SelectorCategory, + SelectorSubCategory, FootprintMask, - unused6, - unused7, + ExtendFootprint, + ObjectSize, unused8, unused9, - HungerRating, - ComfortRating, - HygieneRating, - BladderRating, - EnergyRating, - FunRating, - RoomRating, - SkillFlags, + RatingHunger, + RatingComfort, + RatingHygiene, + RatingBladder, + RatingEnergy, + RatingFun, + RatingRoom, + RatingSkillFlags, NumTypeAttributes, MiscFlags, unused10, unused11, - FunctionPointerSubSortAndSubtype, + FunctionSubSort, DowntownSort, KeepBuying, VacationSort, ResetLotAction, - ObjType3d, + ObjType3D, CommunitySort, DreamFlags, - /// - /// Just indicates the number of fields in the OBJD field array. Don't use for get/set. - /// - FIELD_COUNT + ThumbnailFlags, + Unused103, + Unused104, + Unused105, + Unused106, + Unused107, } // Makes fields a lot easier to serialize and deserialize, and also to set and get from the VM. - public ushort[] Fields = new ushort[(int)FieldNames.FIELD_COUNT]; + public ushort[] Fields = new ushort[108]; private ushort this[FieldNames fieldName] { @@ -239,7 +242,7 @@ public uint ObjectModelGUID public ushort RoomSortflags { get { return this[FieldNames.RoomSortflags]; } set { this[FieldNames.RoomSortflags] = value; } } public ushort FunctionSortFlags { get { return this[FieldNames.AspirationFlags]; } set { this[FieldNames.FunctionSortFlags] = value; } } public ushort CatalogStringsIDPointer { get { return this[FieldNames.CatalogStringsIDPointer]; } set { this[FieldNames.CatalogStringsIDPointer] = value; } } - public ushort IsGlobalSimObj { get { return this[FieldNames.IsGlobalSimObj]; } set { this[FieldNames.IsGlobalSimObj] = value; } } + public ushort GlobalSimObject { get { return this[FieldNames.GlobalSimObject]; } set { this[FieldNames.GlobalSimObject] = value; } } public ushort ToolTipNameType { get { return this[FieldNames.ToolTipNameType]; } set { this[FieldNames.ToolTipNameType] = value; } } public ushort TemplateVer { get { return this[FieldNames.TemplateVer]; } set { this[FieldNames.TemplateVer] = value; } } public ushort NicenessMultiplier { get { return this[FieldNames.NicenessMultiplier]; } set { this[FieldNames.NicenessMultiplier] = value; } } @@ -252,34 +255,34 @@ public uint ObjectModelGUID public ushort CatalogPopupID { get { return this[FieldNames.CatalogPopupID]; } set { this[FieldNames.CatalogPopupID] = value; } } public ushort IgnoreCurrentModelIndexInIcons { get { return this[FieldNames.IgnoreCurrentModelIndexInIcons]; } set { this[FieldNames.IgnoreCurrentModelIndexInIcons] = value; } } public ushort LevelOffset { get { return this[FieldNames.LevelOffset]; } set { this[FieldNames.LevelOffset] = value; } } - public ushort HasShadow { get { return this[FieldNames.HasShadow]; } set { this[FieldNames.HasShadow] = value; } } + public ushort ShadowType { get { return this[FieldNames.ShadowType]; } set { this[FieldNames.ShadowType] = value; } } public ushort NumAttributes { get { return this[FieldNames.NumAttributes]; } set { this[FieldNames.NumAttributes] = value; } } public ushort NumObjArrays { get { return this[FieldNames.NumObjArrays]; } set { this[FieldNames.NumObjArrays] = value; } } public ushort FrontDirection { get { return this[FieldNames.FrontDirection]; } set { this[FieldNames.FrontDirection] = value; } } - public ushort MultiTileLeadObj { get { return this[FieldNames.MultiTileLeadObj]; } set { this[FieldNames.MultiTileLeadObj] = value; } } - public ushort ExpansionFlag { get { return this[FieldNames.ExpansionFlag]; } set { this[FieldNames.ExpansionFlag] = value; } } + public ushort MultiTileLeadObject { get { return this[FieldNames.MultiTileLeadObject]; } set { this[FieldNames.MultiTileLeadObject] = value; } } + public ushort ValidEPFlags1 { get { return this[FieldNames.ValidEPFlags1]; } set { this[FieldNames.ValidEPFlags1] = value; } } public ushort ChairEntryFlags { get { return this[FieldNames.ChairEntryFlags]; } set { this[FieldNames.ChairEntryFlags] = value; } } public ushort TileWidth { get { return this[FieldNames.TileWidth]; } set { this[FieldNames.TileWidth] = value; } } public ushort InhibitSuitCopying { get { return this[FieldNames.InhibitSuitCopying]; } set { this[FieldNames.InhibitSuitCopying] = value; } } public ushort BuildModeType { get { return this[FieldNames.BuildModeType]; } set { this[FieldNames.BuildModeType] = value; } } public ushort BuildModeSubsort { get { return this[FieldNames.BuildModeSubsort]; } set { this[FieldNames.BuildModeSubsort] = value; } } public ushort FootprintMask { get { return this[FieldNames.FootprintMask]; } set { this[FieldNames.FootprintMask] = value; } } - public ushort HungerRating { get { return this[FieldNames.HungerRating]; } set { this[FieldNames.HungerRating] = value; } } - public ushort ComfortRating { get { return this[FieldNames.ComfortRating]; } set { this[FieldNames.ComfortRating] = value; } } - public ushort HygieneRating { get { return this[FieldNames.HygieneRating]; } set { this[FieldNames.HygieneRating] = value; } } - public ushort BladderRating { get { return this[FieldNames.BladderRating]; } set { this[FieldNames.BladderRating] = value; } } - public ushort EnergyRating { get { return this[FieldNames.EnergyRating]; } set { this[FieldNames.EnergyRating] = value; } } - public ushort FunRating { get { return this[FieldNames.FunRating]; } set { this[FieldNames.FunRating] = value; } } - public ushort RoomRating { get { return this[FieldNames.RoomRating]; } set { this[FieldNames.RoomRating] = value; } } - public ushort SkillFlags { get { return this[FieldNames.SkillFlags]; } set { this[FieldNames.SkillFlags] = value; } } + public ushort RatingHunger { get { return this[FieldNames.RatingHunger]; } set { this[FieldNames.RatingHunger] = value; } } + public ushort RatingComfort { get { return this[FieldNames.RatingComfort]; } set { this[FieldNames.RatingComfort] = value; } } + public ushort RatingHygiene { get { return this[FieldNames.RatingHygiene]; } set { this[FieldNames.RatingHygiene] = value; } } + public ushort RatingBladder { get { return this[FieldNames.RatingBladder]; } set { this[FieldNames.RatingBladder] = value; } } + public ushort RatingEnergy { get { return this[FieldNames.RatingEnergy]; } set { this[FieldNames.RatingEnergy] = value; } } + public ushort RatingFun { get { return this[FieldNames.RatingFun]; } set { this[FieldNames.RatingFun] = value; } } + public ushort RatingRoom { get { return this[FieldNames.RatingRoom]; } set { this[FieldNames.RatingRoom] = value; } } + public ushort RatingSkillFlags { get { return this[FieldNames.RatingSkillFlags]; } set { this[FieldNames.RatingSkillFlags] = value; } } public ushort NumTypeAttributes { get { return this[FieldNames.NumTypeAttributes]; } set { this[FieldNames.NumTypeAttributes] = value; } } public ushort MiscFlags { get { return this[FieldNames.MiscFlags]; } set { this[FieldNames.MiscFlags] = value; } } - public ushort FunctionPointerSubSortAndSubtype { get { return this[FieldNames.FunctionPointerSubSortAndSubtype]; } set { this[FieldNames.FunctionPointerSubSortAndSubtype] = value; } } + public ushort FunctionSubSort { get { return this[FieldNames.FunctionSubSort]; } set { this[FieldNames.FunctionSubSort] = value; } } public ushort DowntownSort { get { return this[FieldNames.DowntownSort]; } set { this[FieldNames.DowntownSort] = value; } } public ushort KeepBuying { get { return this[FieldNames.KeepBuying]; } set { this[FieldNames.KeepBuying] = value; } } public ushort VacationSort { get { return this[FieldNames.VacationSort]; } set { this[FieldNames.VacationSort] = value; } } public ushort ResetLotAction { get { return this[FieldNames.ResetLotAction]; } set { this[FieldNames.ResetLotAction] = value; } } - public ushort ObjType3d { get { return this[FieldNames.ObjType3d]; } set { this[FieldNames.ObjType3d] = value; } } + public ushort ObjType3D { get { return this[FieldNames.ObjType3D]; } set { this[FieldNames.ObjType3D] = value; } } public ushort CommunitySort { get { return this[FieldNames.CommunitySort]; } set { this[FieldNames.CommunitySort] = value; } } public ushort DreamFlags { get { return this[FieldNames.DreamFlags]; } set { this[FieldNames.DreamFlags] = value; } } } diff --git a/Assets/Scripts/OpenTS2/Content/DBPF/ObjectFunctionsAsset.cs b/Assets/Scripts/OpenTS2/Content/DBPF/ObjectFunctionsAsset.cs new file mode 100644 index 00000000..42bfae1a --- /dev/null +++ b/Assets/Scripts/OpenTS2/Content/DBPF/ObjectFunctionsAsset.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenTS2.Content.DBPF +{ + public struct ObjectFunction + { + public ushort CheckTree; + public ushort ActionTree; + public static ObjectFunction Default => _default; + private static ObjectFunction _default = new ObjectFunction(0, 0); + + public ObjectFunction(ushort actionTree, ushort checkTree) + { + ActionTree = actionTree; + CheckTree = checkTree; + } + } + public class ObjectFunctionsAsset : AbstractAsset + { + public string FileName; + + public enum FunctionNames + { + Init, + Main, + Load, + Cleanup, + QueueSkipped, + AllowIntersection, + WallAdjacencyChanged, + RoomChanged, + DynamicMultiTileUpdate, + Placement, + Pickup, + UserPlacement, + UserPickup, + LevelInfoRequest, + ServingSurface, + Portal, + Gardening, + WashHands, + Prep, + Cook, + Surface, + Dispose, + Food, + PickupFromSlot, + WashDish, + EatingSurface, + Sit, + Stand, + Clean, + Repair, + UIEvent, + Restock, + WashClothes, + StartLiveMode, + StopLiveMode, + LinkObjects, + MessageHandler, + PreRoute, + PostRoute, + GoalCheck, + ReactionHandler, + AlongRouteCallback, + Awareness, + Reset, + LookAtTarget, + WalkOver, + UtilityStateChange, + SetModelByType, + GetModelType, + Delete, + UserDelete, + JustMovedIn, + PreventPlaceInSlot, + GlobalAwareness, + ObjectUpdatedByDesignMode + } + + public ObjectFunction[] Functions; + + public bool HasFunction(FunctionNames function) + { + if ((int)function >= Functions.Length) + return false; + return true; + } + + public ObjectFunction GetFunction(FunctionNames function) + { + if (!HasFunction(function)) + return ObjectFunction.Default; + return Functions[(int)function]; + } + } +} diff --git a/Assets/Scripts/OpenTS2/Content/DBPF/ObjectFunctionsAsset.cs.meta b/Assets/Scripts/OpenTS2/Content/DBPF/ObjectFunctionsAsset.cs.meta new file mode 100644 index 00000000..79c7cc74 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Content/DBPF/ObjectFunctionsAsset.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6b143334ed58e344db7da34e645d3f71 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Content/EPManager.cs b/Assets/Scripts/OpenTS2/Content/EPManager.cs index 59aa8c91..0f5a26ec 100644 --- a/Assets/Scripts/OpenTS2/Content/EPManager.cs +++ b/Assets/Scripts/OpenTS2/Content/EPManager.cs @@ -28,8 +28,8 @@ public int InstalledProducts } } - //mask of all products, minus store. - int _installedProducts = 0x3EFFF; + //mask of all products + int _installedProducts = 0x3FFFF; public EPManager() { s_instance = this; diff --git a/Assets/Scripts/OpenTS2/Content/ObjectManager.cs b/Assets/Scripts/OpenTS2/Content/ObjectManager.cs index 63cb079b..2876a0ae 100644 --- a/Assets/Scripts/OpenTS2/Content/ObjectManager.cs +++ b/Assets/Scripts/OpenTS2/Content/ObjectManager.cs @@ -48,5 +48,12 @@ void RegisterObject(ObjectDefinitionAsset objd) { _objectByGUID[objd.GUID] = objd; } + + public ObjectDefinitionAsset GetObjectByGUID(uint guid) + { + if (_objectByGUID.TryGetValue(guid, out ObjectDefinitionAsset obj)) + return obj; + return null; + } } } diff --git a/Assets/Scripts/OpenTS2/Files/Formats/DBPF/GroupsTypes.cs b/Assets/Scripts/OpenTS2/Files/Formats/DBPF/GroupsTypes.cs index aca35f2a..6d2f2042 100644 --- a/Assets/Scripts/OpenTS2/Files/Formats/DBPF/GroupsTypes.cs +++ b/Assets/Scripts/OpenTS2/Files/Formats/DBPF/GroupsTypes.cs @@ -66,6 +66,7 @@ public static class TypeIDs public const uint LUA_GLOBAL = 0x9012468A; public const uint LUA_LOCAL = 0x9012468B; + public const uint OBJF = 0x4F424A66; } public static class GroupIDs { diff --git a/Assets/Scripts/OpenTS2/Files/Formats/DBPF/OBJDCodec.cs b/Assets/Scripts/OpenTS2/Files/Formats/DBPF/OBJDCodec.cs index 9a0e1c4a..87531ba5 100644 --- a/Assets/Scripts/OpenTS2/Files/Formats/DBPF/OBJDCodec.cs +++ b/Assets/Scripts/OpenTS2/Files/Formats/DBPF/OBJDCodec.cs @@ -34,12 +34,15 @@ public override AbstractAsset Deserialize(byte[] bytes, ResourceKey tgi, DBPFFil reader.Seek(SeekOrigin.Begin, 64); - for(var i=0;i<(int)ObjectDefinitionAsset.FieldNames.FIELD_COUNT;i++) + for(var i=0;i<(int)108;i++) { var value = reader.ReadUInt16(); asset.Fields[i] = value; } + stream.Dispose(); + reader.Dispose(); + return asset; } } diff --git a/Assets/Scripts/OpenTS2/Files/Formats/DBPF/OBJFCodec.cs b/Assets/Scripts/OpenTS2/Files/Formats/DBPF/OBJFCodec.cs new file mode 100644 index 00000000..2bcb13aa --- /dev/null +++ b/Assets/Scripts/OpenTS2/Files/Formats/DBPF/OBJFCodec.cs @@ -0,0 +1,53 @@ +using OpenTS2.Common; +using OpenTS2.Content; +using OpenTS2.Content.DBPF; +using OpenTS2.Files.Utils; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenTS2.Files.Formats.DBPF +{ + [Codec(TypeIDs.OBJF)] + public class OBJFCodec : AbstractCodec + { + public override AbstractAsset Deserialize(byte[] bytes, ResourceKey tgi, DBPFFile sourceFile) + { + var asset = new ObjectFunctionsAsset(); + var stream = new MemoryStream(bytes); + var reader = IoBuffer.FromStream(stream, ByteOrder.LITTLE_ENDIAN); + + asset.FileName = reader.ReadNullTerminatedUTF8(); + + reader.Seek(SeekOrigin.Begin, 64); + + var unk = reader.ReadBytes(8); + + var magic = reader.ReadBytes(4); + + if (magic[0] != 'f' || magic[1] != 'J' || magic[2] != 'B' || magic[3] != 'O') + throw new IOException("Invalid magic for OBJF resource"); + + var entryAmount = reader.ReadUInt32(); + + asset.Functions = new ObjectFunction[entryAmount]; + + for(var i=0;i _virtualMachine; + private VM _virtualMachine; + public Context SimulationContext = Context.Neighborhood; + public enum Context + { + Lot = 1, + Neighborhood = 2 + } + /// + /// Number of ticks to run per second. + /// + public int TickRate = 20; + private static Simulator _instance; + + private void Awake() + { + _instance = this; + _virtualMachine = new VM(); + } + + private void Start() + { + CreateGlobalObjects(); + } + + public Simulator Get() + { + if (_instance != null) + return this; + return null; + } + + private void CreateGlobalObjects() + { + var objManager = ObjectManager.Get(); + if (objManager == null) + throw new NullReferenceException("Can't create global objects, Object Manager not constructed!"); + + var objects = objManager.Objects; + + foreach(var obj in objects) + { + if ((obj.GlobalSimObject & (int)SimulationContext) == (int)SimulationContext) + { + CreateObject(obj); + } + } + } + + public VMEntity CreateObject(ObjectDefinitionAsset objectDefinition) + { + var entity = new VMEntity(objectDefinition); + try + { + _virtualMachine.AddEntity(entity); + + UpdateObjectData(entity); + + var initFunction = entity.ObjectDefinition.Functions.GetFunction(ObjectFunctionsAsset.FunctionNames.Init); + + if (initFunction.ActionTree != 0) + entity.RunTreeImmediately(initFunction.ActionTree); + } + catch(SimAnticsException e) + { + HandleSimAnticsException(e); + } + return entity; + } + + void UpdateObjectData(VMEntity entity) + { + entity.SetObjectData(VMObjectData.Room, -1); + entity.SetObjectData(VMObjectData.ObjectID, entity.ID); + } + + public void HandleSimAnticsException(SimAnticsException exception) + { + Debug.LogError(exception.ToString()); + } + } +} diff --git a/Assets/Scripts/OpenTS2/Game/Simulator.cs.meta b/Assets/Scripts/OpenTS2/Game/Simulator.cs.meta new file mode 100644 index 00000000..77dfa544 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Game/Simulator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 695349b682404ab4281ad85c1612b41d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Lua/API/ObjectAPI.cs b/Assets/Scripts/OpenTS2/Lua/API/ObjectAPI.cs new file mode 100644 index 00000000..7fc00c72 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Lua/API/ObjectAPI.cs @@ -0,0 +1,43 @@ +using MoonSharp.Interpreter; +using OpenTS2.Content; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace OpenTS2.Lua.API +{ + [MoonSharpUserData] + class GlobalObjManager + { + public bool isValidObjectGUID(uint guid) + { + var objManager = ObjectManager.Get(); + if (objManager == null) + throw new ScriptRuntimeException("ObjectManager has not been constructed!"); + if (objManager.GetObjectByGUID(guid) != null) + return true; + return false; + } + } + public class ObjectAPI : LuaAPI + { + void SetObjectDefinitionField(uint guid, int field, ushort value) + { + var objManager = ObjectManager.Get(); + if (objManager == null) + throw new ScriptRuntimeException("ObjectManager has not been constructed!"); + var obj = objManager.GetObjectByGUID(guid); + if (obj == null) + throw new ScriptRuntimeException($"Object with GUID {guid} does not exist."); + obj.Fields[field] = value; + } + public override void OnRegister(Script script) + { + script.Globals["GlobalObjManager"] = new GlobalObjManager(); + script.Globals["SetObjectDefinitionField"] = (Action)SetObjectDefinitionField; + } + } +} diff --git a/Assets/Scripts/OpenTS2/Lua/API/ObjectAPI.cs.meta b/Assets/Scripts/OpenTS2/Lua/API/ObjectAPI.cs.meta new file mode 100644 index 00000000..ef296c62 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Lua/API/ObjectAPI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ab5ab5d3ebca8b84fa109aa906936568 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Lua/Disassembly/LuaC50.cs b/Assets/Scripts/OpenTS2/Lua/Disassembly/LuaC50.cs index a7ec6f4d..318ef209 100644 --- a/Assets/Scripts/OpenTS2/Lua/Disassembly/LuaC50.cs +++ b/Assets/Scripts/OpenTS2/Lua/Disassembly/LuaC50.cs @@ -516,6 +516,7 @@ public class Context public Function Function; public List JumpLabels = new List(); public List ForLoops = new List(); + public HashSet ThisCallRegisters = new HashSet(); public RETURN ReturnOpCode; public int Level = 0; public Context Parent; @@ -527,6 +528,21 @@ public class Context public string ReturnTable => $"ReturnTable_{Level}"; private static readonly int RK_OFFSET = 250; + public void UnmarkThisCall(ushort register) + { + ThisCallRegisters.Remove(register); + } + + public void MarkThisCall(ushort register) + { + ThisCallRegisters.Add(register); + } + + public bool IsThisCall(ushort register) + { + return ThisCallRegisters.Contains(register); + } + public List GetJumpLabelsHere(int pc) { var outputs = new List(); diff --git a/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/CALL.cs b/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/CALL.cs index 2881a97a..1dc9f613 100644 --- a/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/CALL.cs +++ b/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/CALL.cs @@ -36,6 +36,12 @@ public override void Disassemble(LuaC50.Context context) start = (ushort)(A + 1); end = A + B - 1; + if (context.IsThisCall(A)) + { + context.Code.WriteLine("-- THISCALL"); + start++; + } + if (start > end+1 && WorkaroundRegisterAmount > 0) { context.Code.WriteLine("-- HACK: CALL instruction had invalid start and end for arg values."); @@ -58,6 +64,7 @@ public override void Disassemble(LuaC50.Context context) context.Code.WriteLine(context.R(A) + "(" + callValues + ")"); else context.Code.WriteLine(retValues + " = " + context.R(A) + "("+callValues+")"); + context.UnmarkThisCall(A); } } } diff --git a/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/SELF.cs b/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/SELF.cs index ce6fec81..4aeaaf1a 100644 --- a/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/SELF.cs +++ b/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/SELF.cs @@ -12,6 +12,7 @@ public override void Disassemble(LuaC50.Context context) { context.Code.WriteLine(context.R((ushort)(A + 1)) + " = " + context.R(B)); context.Code.WriteLine(context.R(A) + " = " + context.R(B) + "[" + context.RKAsString(C) + "]"); + context.MarkThisCall(A); } } } diff --git a/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/TAILCALL.cs b/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/TAILCALL.cs index 443b0ba9..0a05483d 100644 --- a/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/TAILCALL.cs +++ b/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/TAILCALL.cs @@ -13,6 +13,13 @@ public override void Disassemble(LuaC50.Context context) var start = (ushort)(A + 1); var end = A + B - 1; var callValues = ""; + + if (context.IsThisCall(A)) + { + context.Code.WriteLine("-- THISCALL"); + start++; + } + for (var i = start; i <= end; i++) { if (i > start) diff --git a/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/TEST.cs b/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/TEST.cs index 72167809..cf3e9d90 100644 --- a/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/TEST.cs +++ b/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/TEST.cs @@ -10,7 +10,10 @@ public class TEST : LuaC50.OpCode { public override void Disassemble(LuaC50.Context context) { - context.Code.WriteLine($"if ({context.R(B)} == {C}) then"); + if (C == 0) + context.Code.WriteLine($"if not {context.R(B)} then"); + else + context.Code.WriteLine($"if ({context.R(B)} == {C}) then"); context.Code.Indentation++; context.Code.WriteLine($"{context.R(A)} = {context.R(B)}"); context.Code.Indentation--; diff --git a/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/TFORPREP.cs b/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/TFORPREP.cs index c08d5509..ce9a8eed 100644 --- a/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/TFORPREP.cs +++ b/Assets/Scripts/OpenTS2/Lua/Disassembly/OpCodes/TFORPREP.cs @@ -41,6 +41,12 @@ public void LinkToTFORLOOP() public override void Disassemble(LuaC50.Context context) { + context.Code.WriteLine("if type(" + context.R(A) + ") == \"table\" then"); + context.Code.Indentation++; + context.Code.WriteLine(context.R((ushort)(A + 1)) + " = " + context.R(A)); + context.Code.WriteLine(context.R(A) + " = next"); + context.Code.Indentation--; + context.Code.WriteEnd(); context.Code.WriteLine("for "+ context.R((ushort)(A + 2)) + ", "+ context.R((ushort)(A + 3)) + " in "+ context.R(A) + ", "+ context.R((ushort)(A + 1)) + " do"); context.Code.Indentation++; //context.Code.WriteLine("-- A: " + A); diff --git a/Assets/Scripts/OpenTS2/Lua/LuaManager.cs b/Assets/Scripts/OpenTS2/Lua/LuaManager.cs index 07986613..33ae5e25 100644 --- a/Assets/Scripts/OpenTS2/Lua/LuaManager.cs +++ b/Assets/Scripts/OpenTS2/Lua/LuaManager.cs @@ -42,6 +42,11 @@ public LuaManager() { _instance = this; _script = new Script(); + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (var element in assemblies) + { + UserData.RegisterAssembly(element); + } LoadAPIs(); } @@ -53,9 +58,9 @@ void PrepGlobalsForPrimitive(short param0, short param1, short param2, VMContext api.PrepareLuaPrimitive(param0, param1, param2); } - void ThrowLuaPrimitiveException(ScriptRuntimeException exception) + void ThrowLuaPrimitiveException(ScriptRuntimeException exception, string name) { - throw new SimAnticsException($"Problem executing Lua script:{Environment.NewLine}{exception.DecoratedMessage}", Context.StackFrame); + throw new SimAnticsException($"Problem executing Lua script {name}:{Environment.NewLine}{exception.DecoratedMessage}", Context.StackFrame); } /// @@ -78,14 +83,21 @@ public void InitializeObjectScripts() try { var luaAsset = entry.GetAsset(); - switch(entry.TGI.TypeID) + try + { + switch (entry.TGI.TypeID) + { + case TypeIDs.LUA_GLOBAL: + RunScript(luaAsset.Source); + break; + case TypeIDs.LUA_LOCAL: + _objectScriptsByName[luaAsset.FileName] = luaAsset; + break; + } + } + catch(InterpreterException e) { - case TypeIDs.LUA_GLOBAL: - RunScript(luaAsset.Source); - break; - case TypeIDs.LUA_LOCAL: - _objectScriptsByName[luaAsset.FileName] = luaAsset; - break; + Debug.LogError($"LuaManager: Failed to run object script {luaAsset.FileName}:{Environment.NewLine}{e.DecoratedMessage}"); } } @@ -125,7 +137,7 @@ public void RunScript(string lua) /// Parameter 2 /// SimAntics Context /// The exit code for this primitive. - public VMExitCode RunScriptPrimitive(string lua, short param0, short param1, short param2, VMContext ctx) + public VMExitCode RunScriptPrimitive(string name, string lua, short param0, short param1, short param2, VMContext ctx) { PrepGlobalsForPrimitive(param0, param1, param2, ctx); try @@ -134,7 +146,7 @@ public VMExitCode RunScriptPrimitive(string lua, short param0, short param1, sho } catch(ScriptRuntimeException e) { - ThrowLuaPrimitiveException(e); + ThrowLuaPrimitiveException(e, name); } return ExitCode; } @@ -142,6 +154,7 @@ public VMExitCode RunScriptPrimitive(string lua, short param0, short param1, sho void LoadAPIs() { RegisterAPI(new MainAPI()); + RegisterAPI(new ObjectAPI()); } public void RegisterAPI(LuaAPI api) diff --git a/Assets/Scripts/OpenTS2/Scenes/StartupController.cs b/Assets/Scripts/OpenTS2/Scenes/StartupController.cs index 7fd81976..c60c68e1 100644 --- a/Assets/Scripts/OpenTS2/Scenes/StartupController.cs +++ b/Assets/Scripts/OpenTS2/Scenes/StartupController.cs @@ -14,6 +14,7 @@ using UnityEngine.UI; using System.Collections; using OpenTS2.UI.Layouts; +using OpenTS2.Lua; namespace OpenTS2.Scenes { @@ -106,6 +107,8 @@ private void Update() private void OnFinishLoading() { + var luaMgr = LuaManager.Get(); + luaMgr.InitializeObjectScripts(); CursorController.Cursor = CursorController.CursorType.Hourglass; if (LoadObjects && !GameLoaded) { diff --git a/Assets/Scripts/OpenTS2/SimAntics/Primitives/VMGenericSimCall.cs b/Assets/Scripts/OpenTS2/SimAntics/Primitives/VMGenericSimCall.cs new file mode 100644 index 00000000..bda1a1e2 --- /dev/null +++ b/Assets/Scripts/OpenTS2/SimAntics/Primitives/VMGenericSimCall.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenTS2.SimAntics.Primitives +{ + public class VMGenericSimCall : VMPrimitive + { + public override VMReturnValue Execute(VMContext ctx) + { + var callID = ctx.Node.GetOperand(0); + + switch(callID) + { + // NodeConsolidation_GlobalInitObject + case 0x41: + ctx.Entity.SetObjectData(VMObjectData.RotationNotches, 2); + ctx.Entity.SetObjectData(VMObjectData.RoomPlacement, 0); + // Bit 1 floor + ctx.Entity.SetObjectFlags(VMObjectData.AllowedHeightFlags, 1); + // Bit 6 burns + ctx.Entity.SetObjectFlags(VMObjectData.FlagField2, 32); + ctx.Entity.SetObjectData(VMObjectData.TrashUnits, 1); + // Bit 1 floor, bit 2 terrain + ctx.Entity.SetObjectFlags(VMObjectData.PlacementFlags, 1 | 2); + // Bit 2 player can move, Bit 4 player can delete + ctx.Entity.SetObjectFlags(VMObjectData.MovementFlags, 8 | 2); + // Bit 14 can't be billed + ctx.Entity.ClearObjectFlags(VMObjectData.FlagField2, 8192); + ctx.Entity.SetObjectData(VMObjectData.LookAtScore, 5); + ctx.StackFrame.StackObjectID = ctx.Entity.ID; + return VMReturnValue.ReturnTrue; + } + throw new SimAnticsException($"Invalid Generic Sim Call ID {callID}", ctx.StackFrame); + } + } +} diff --git a/Assets/Scripts/OpenTS2/SimAntics/Primitives/VMGenericSimCall.cs.meta b/Assets/Scripts/OpenTS2/SimAntics/Primitives/VMGenericSimCall.cs.meta new file mode 100644 index 00000000..3878b5f4 --- /dev/null +++ b/Assets/Scripts/OpenTS2/SimAntics/Primitives/VMGenericSimCall.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5006767cf2fff4448b1d4788f438908c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/SimAntics/Primitives/VMLua.cs b/Assets/Scripts/OpenTS2/SimAntics/Primitives/VMLua.cs index 9f41aff5..c1432de7 100644 --- a/Assets/Scripts/OpenTS2/SimAntics/Primitives/VMLua.cs +++ b/Assets/Scripts/OpenTS2/SimAntics/Primitives/VMLua.cs @@ -71,7 +71,7 @@ public override VMReturnValue Execute(VMContext ctx) throw new SimAnticsException($"Can't find Lua object script named {scriptName}", ctx.StackFrame); } - var luaResult = luaManager.RunScriptPrimitive(scriptSource, param0, param1, param2, ctx); + var luaResult = luaManager.RunScriptPrimitive(scriptName, scriptSource, param0, param1, param2, ctx); return new VMReturnValue(luaResult); } diff --git a/Assets/Scripts/OpenTS2/SimAntics/Primitives/VMPrimitiveRegistry.cs b/Assets/Scripts/OpenTS2/SimAntics/Primitives/VMPrimitiveRegistry.cs index 5dfba657..691ae17f 100644 --- a/Assets/Scripts/OpenTS2/SimAntics/Primitives/VMPrimitiveRegistry.cs +++ b/Assets/Scripts/OpenTS2/SimAntics/Primitives/VMPrimitiveRegistry.cs @@ -13,6 +13,7 @@ public static class VMPrimitiveRegistry public static void Initialize() { RegisterPrimitive(0x0); + RegisterPrimitive(0x1); RegisterPrimitive(0x2); RegisterPrimitive(0x8); RegisterPrimitive(0x12); diff --git a/Assets/Scripts/OpenTS2/SimAntics/SimAnticsException.cs b/Assets/Scripts/OpenTS2/SimAntics/SimAnticsException.cs index 3b0f500c..8140aeed 100644 --- a/Assets/Scripts/OpenTS2/SimAntics/SimAnticsException.cs +++ b/Assets/Scripts/OpenTS2/SimAntics/SimAnticsException.cs @@ -13,5 +13,26 @@ public SimAnticsException(string message, VMStackFrame stackFrame) : base(messag { StackFrame = stackFrame; } + + public override string ToString() + { + var trace = ""; + if (StackFrame != null) + { + trace += Environment.NewLine; + trace += "Object: " + StackFrame.Thread.Entity.ObjectDefinition.FileName+" ("+StackFrame.Thread.Entity.ID+")"; + trace += Environment.NewLine; + trace += "Stack Trace:"; + trace += Environment.NewLine; + foreach(var elem in StackFrame.Thread.Frames) + { + trace += "Frame" + Environment.NewLine; + trace += "Stack Object ID: " + elem.StackObjectID + Environment.NewLine; + trace += "Node: " + elem.CurrentNode + Environment.NewLine; + trace += "Tree: " + elem.BHAV.FileName + " ("+elem.BHAV.GlobalTGI.InstanceID+ ")" + Environment.NewLine; + } + } + return Message + trace; + } } } diff --git a/Assets/Scripts/OpenTS2/SimAntics/VM.cs b/Assets/Scripts/OpenTS2/SimAntics/VM.cs index 5c207e63..780b7958 100644 --- a/Assets/Scripts/OpenTS2/SimAntics/VM.cs +++ b/Assets/Scripts/OpenTS2/SimAntics/VM.cs @@ -1,3 +1,4 @@ +using OpenTS2.Client; using OpenTS2.Common; using OpenTS2.Content; using OpenTS2.Files.Formats.DBPF; @@ -13,12 +14,50 @@ namespace OpenTS2.SimAntics /// public class VM { + public short[] GlobalState; public VMScheduler Scheduler = new VMScheduler(); public List Entities = new List(); public uint CurrentTick = 0; private Dictionary _entitiesByID = new Dictionary(); + public VM() + { + GlobalState = new short[60]; + InitializeGlobalState(); + } + + public short GetGlobal(ushort id) + { + return GlobalState[id]; + } + + public void SetGlobal(ushort id, short value) + { + GlobalState[id] = value; + } + + public short GetGlobal(VMGlobals global) + { + return GetGlobal((ushort)global); + } + + public void SetGlobal(VMGlobals global, short value) + { + SetGlobal((ushort)global, value); + } + + void InitializeGlobalState() + { + var epManager = EPManager.Get(); + var epFlags1 = (short)(epManager.InstalledProducts & 0xFFFF); + var epFlags2 = (short)(epManager.InstalledProducts >> 16); + SetGlobal(VMGlobals.GameEditionFlags1, epFlags1); + SetGlobal(VMGlobals.GameEditionFlags2, epFlags2); + var settings = Settings.Get(); + SetGlobal(VMGlobals.CurrentLanguage, (short)settings.Language); + } + /// /// Ticks all entities and advances the Simulation by 1 tick. /// diff --git a/Assets/Scripts/OpenTS2/SimAntics/VMContext.cs b/Assets/Scripts/OpenTS2/SimAntics/VMContext.cs index 934003ce..87afb5a9 100644 --- a/Assets/Scripts/OpenTS2/SimAntics/VMContext.cs +++ b/Assets/Scripts/OpenTS2/SimAntics/VMContext.cs @@ -25,6 +25,10 @@ public short GetData(VMDataSource source, short dataIndex) { return source switch { + VMDataSource.Globals => VM.GetGlobal((ushort)dataIndex), + VMDataSource.MyObjectsAttributes => Entity.Attributes[dataIndex], + VMDataSource.MyObject => Entity.ObjectData[dataIndex], + VMDataSource.StackObject => StackObjectEntity.ObjectData[dataIndex], VMDataSource.Literal => dataIndex, VMDataSource.Temps => Entity.Temps[dataIndex], VMDataSource.Params => StackFrame.Arguments[dataIndex], @@ -33,12 +37,13 @@ public short GetData(VMDataSource source, short dataIndex) VMDataSource.StackObjectsTemp => StackObjectEntity.Temps[dataIndex], VMDataSource.Local => StackFrame.Locals[dataIndex], VMDataSource.StackObjectsDefinition => (short)StackObjectEntity.ObjectDefinition.Fields[dataIndex], - _ => throw new SimAnticsException("Attempted to retrieve a variable from an out of range data source.", StackFrame) + VMDataSource.StackObjectsAttributes => StackObjectEntity.Attributes[dataIndex], + _ => throw new SimAnticsException($"Attempted to retrieve a variable from an out of range data source ({source}[{dataIndex}]).", StackFrame) }; } catch (ArgumentOutOfRangeException) { - throw new SimAnticsException("Attempted to retrieve a variable from an out of range data index.", StackFrame); + throw new SimAnticsException($"Attempted to retrieve a variable from an out of range data index ({source}[{dataIndex}]).", StackFrame); } } @@ -48,6 +53,18 @@ public void SetData(VMDataSource source, short dataIndex, short value) { switch (source) { + case VMDataSource.Globals: + VM.SetGlobal((ushort)dataIndex, value); + return; + case VMDataSource.MyObjectsAttributes: + Entity.Attributes[dataIndex] = value; + return; + case VMDataSource.MyObject: + Entity.ObjectData[dataIndex] = value; + return; + case VMDataSource.StackObject: + StackObjectEntity.ObjectData[dataIndex] = value; + return; case VMDataSource.Temps: Entity.Temps[dataIndex] = value; return; @@ -66,12 +83,15 @@ public void SetData(VMDataSource source, short dataIndex, short value) case VMDataSource.Local: StackFrame.Locals[dataIndex] = value; return; + case VMDataSource.StackObjectsAttributes: + StackObjectEntity.Attributes[dataIndex] = value; + return; } - throw new SimAnticsException("Attempted to modify a variable from an out of range data source.", StackFrame); + throw new SimAnticsException($"Attempted to modify a variable from an out of range data source ({source}[{dataIndex}]).", StackFrame); } catch (ArgumentOutOfRangeException) { - throw new SimAnticsException("Attempted to modify a variable from an out of range data index.", StackFrame); + throw new SimAnticsException($"Attempted to modify a variable from an out of range data index ({source}[{dataIndex}]).", StackFrame); } } } diff --git a/Assets/Scripts/OpenTS2/SimAntics/VMEntity.cs b/Assets/Scripts/OpenTS2/SimAntics/VMEntity.cs index 4fa17773..0396e98f 100644 --- a/Assets/Scripts/OpenTS2/SimAntics/VMEntity.cs +++ b/Assets/Scripts/OpenTS2/SimAntics/VMEntity.cs @@ -1,4 +1,5 @@ using OpenTS2.Content.DBPF; +using OpenTS2.Files.Formats.DBPF; using System; using System.Collections.Generic; using System.Linq; @@ -19,6 +20,8 @@ public class VMEntity public VMThread MainThread; public VM VM; public ObjectDefinitionAsset ObjectDefinition; + public short[] Attributes; + public short[] ObjectData = new short[114]; public uint PrivateGroupID => ObjectDefinition.GlobalTGI.GroupID; public uint SemiGlobalGroupID { @@ -39,6 +42,27 @@ protected VMEntity() public VMEntity(ObjectDefinitionAsset objectDefinition) : this() { ObjectDefinition = objectDefinition; + Attributes = new short[objectDefinition.NumAttributes]; + } + + public short GetObjectData(VMObjectData field) + { + return ObjectData[(int)field]; + } + + public void ClearObjectFlags(VMObjectData field, short value) + { + ObjectData[(int)field] = (short)(ObjectData[(int)field] ^ value); + } + + public void SetObjectFlags(VMObjectData field, short value) + { + ObjectData[(int)field] = (short)(ObjectData[(int)field] | value); + } + + public void SetObjectData(VMObjectData field, short value) + { + ObjectData[(int)field] = value; } public void Tick() @@ -57,5 +81,31 @@ public void Delete() } VM.RemoveEntity(ID); } + + public BHAVAsset GetBHAV(ushort treeID) + { + // 0x0XXX is global scope, 0x1XXX is private scope and 0x2XXX is semiglobal scope. + var groupid = SemiGlobalGroupID; + + if (treeID < 0x1000) + groupid = GroupIDs.Global; + else if (treeID < 0x2000) + groupid = PrivateGroupID; + + return VM.GetBHAV(treeID, groupid); + } + + public VMExitCode RunTreeImmediately(ushort treeID) + { + var thread = new VMThread(this); + thread.CanYield = false; + + var bhav = GetBHAV(treeID); + + var stackFrame = new VMStackFrame(bhav, thread); + thread.Frames.Push(stackFrame); + + return thread.Tick(); + } } } diff --git a/Assets/Scripts/OpenTS2/SimAntics/VMGlobals.cs b/Assets/Scripts/OpenTS2/SimAntics/VMGlobals.cs new file mode 100644 index 00000000..83adf5b0 --- /dev/null +++ b/Assets/Scripts/OpenTS2/SimAntics/VMGlobals.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenTS2.SimAntics +{ + public enum VMGlobals + { + Hour, + DayOfMonth, + ZoomLevel, + SelectedPersonID, + TimeOfDay, + Minute, + Second, + Month, + Year, + CurrentFamily, + CurrentHouse, + Unused, + LastGZButtonID, + BudgetMod10000, + BudgetDiv10000, + CurrentLanguage, + Speed, + Paused, + HeldSimSpeed, + Mode, + GameEditionFlags1, + InhibitMoveIn, + LotHasHouse, + LotSize, + CurrentSKU, + DebugFlags, + TutorialState, + IndoorTiles, + DaysRunning, + MaximumDayNumber, + FreeWill, + HouseRadioStation, + InterruptOn, + DemoFlags, + DayOfWeek, + ParentalControl, + CurrentInteractionGUID, + UtilityAvailableFlags, + UtilityBillEnergy, + UtilityBillWater, + CurrentNeighborhoodID, + LotZoning, + AllowAging, + LastNeighborhoodID, + LotEntryMethod, + CinematicCamera, + LotValueDiv1000, + LotValueMod1000, + ObjectsValueDiv1000, + ObjectsValueMod1000, + MaxVisitors, + ObjectErrorFlags, + CameraBusy, + InstantWriteOnceFlags, + NeighborhoodType, + Unused2, + PuppyKittenAgingDisabled, + BugJarTimeDecay, + GameEditionFlags2, + LotClass + } +} diff --git a/Assets/Scripts/OpenTS2/SimAntics/VMGlobals.cs.meta b/Assets/Scripts/OpenTS2/SimAntics/VMGlobals.cs.meta new file mode 100644 index 00000000..7af2ba14 --- /dev/null +++ b/Assets/Scripts/OpenTS2/SimAntics/VMGlobals.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b3aae747f423f924bb52c08047b8fd87 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/SimAntics/VMObjectData.cs b/Assets/Scripts/OpenTS2/SimAntics/VMObjectData.cs new file mode 100644 index 00000000..135d6f6f --- /dev/null +++ b/Assets/Scripts/OpenTS2/SimAntics/VMObjectData.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenTS2.SimAntics +{ + public enum VMObjectData + { + Graphic, + Direction, + ContainerID, + SlotNumber, + AllowedHeightFlags, + WallAdjacencyFlags, + StopBaseInteraction, + NicenessScore, + Flags, + FootprintIndex, + RoutePenalty, + ObjectID, + TargetID, // pretty sure this is not used + WallPlacementFlags, + MaintainLevel, + RepairLevel, + LightSource, + WalkStyle, + SimAge, + LeadTileObjectID, + TreeTableEntry, + BirthMinute, + DoorRouteBlockerFlags, + RotationNotches, + BirthHour, + LockoutCount, + SittingValue, + Weight, + SupportStrength, + Room, + RoomPlacement, + PrepValue, + CookValue, + SurfaceValue, + HiddenFlags, + Temperature, + DisposeValue, + WashDishValue, + EatingSurfaceValue, + DirtyLevel, + FlagField2, + CurrentValue, + PlacementFlags, + MovementFlags, + MaximumGrade, + BirthYear, + BirthMonth, + BirthDay, + AgeInMonths, + TrashUnits, + HideInteraction, + LightingContribution, + PrimitiveResult, + PortalPenalty, + PrimitiveResultID, + TargetSlotCount, + Disabled, + RenderAlphaMax, + ObjectVersion, + Category, + SimulateOnPause, + ServingSurfaceValue, + UseCount, + ExclusivePlacementFlags, + GardeningValue, + WashHandsValue, + FunctionScore, + SlotCount, + ShadowType, + WallCutoutFlags, + VehicleSpeed, + RenderFlags, + RenderAlpha, + RenderRed, + RenderGreen, + RenderBlue, + WaterUtilityUsagePerHour, + EnergyUtilityUsagePerHour, + LookAtScore, + SemiAttributeCount, + ObjectLinkForward, + ObjectLinkBackward, + PlacementFlags2, + DaysTillBreak, + Flammibility, + HurrySpeed, + CrapnessScore, + DisableAllInteractions, + PortalLink1FromSlotID, + PortalLink1ToSlotID, + PortalLink1ToObjectID, + PortalLink1ToObjectGUIDLowerWord, + PortalLink1ToObjectGUIDUpperWord, + PortalLink2FromSlotID, + PortalLink2ToSlotID, + PortalLink2ToObjectID, + PortalLink2ToObjectGUIDLowerWord, + PortalLink2ToObjectGUIDUpperWord, + FlagsField3, + GiftFromNeighborID, + GiftToNeighborID, + UIIconFlags, + LastTickRendered, + InvisibleWallPlacementFlags, + InteractionUIPriority, + WholesalePurchaseCost, + ForSaleFlags, + TreeTableOverrideData, + AnimOffsetX, + AnimOffsetY, + AnimOffsetZ, + AnimOffsetZRotation, + PetAwarenessValue, + IgnoreAptRestrictionGrab + } +} diff --git a/Assets/Scripts/OpenTS2/SimAntics/VMObjectData.cs.meta b/Assets/Scripts/OpenTS2/SimAntics/VMObjectData.cs.meta new file mode 100644 index 00000000..a0a9a81a --- /dev/null +++ b/Assets/Scripts/OpenTS2/SimAntics/VMObjectData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 08d80c45e7d891b498e0358734e25ac2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/SimAntics/VMStackFrame.cs b/Assets/Scripts/OpenTS2/SimAntics/VMStackFrame.cs index ddacead2..52f3027d 100644 --- a/Assets/Scripts/OpenTS2/SimAntics/VMStackFrame.cs +++ b/Assets/Scripts/OpenTS2/SimAntics/VMStackFrame.cs @@ -36,20 +36,19 @@ public VMStackFrame(BHAVAsset bhav, VMThread thread) public VMExitCode Tick() { var currentIterations = 0; - VMExitCode result; - var currentNode = GetCurrentNode(); + VMExitCode result = VMExitCode.True; + var returnTarget = CurrentNode; if (CurrentContinueHandler != null) { result = CurrentContinueHandler.Tick(); + if (result == VMExitCode.Continue) + return result; + else + { + returnTarget = GetNodeReturnTarget(GetCurrentNode(), result); + } } - else - result = ExecuteNode(currentNode); - - if (result == VMExitCode.Continue) - return result; - - var returnTarget = GetNodeReturnTarget(currentNode, result); while (returnTarget != BHAVAsset.Node.ErrorReturnValue && returnTarget != BHAVAsset.Node.TrueReturnValue && returnTarget != BHAVAsset.Node.FalseReturnValue) { @@ -59,7 +58,7 @@ public VMExitCode Tick() throw new SimAnticsException($"Thread entered infinite loop! ( >{MaxIterations} primitives )", this); } SetCurrentNode(returnTarget); - currentNode = GetCurrentNode(); + var currentNode = GetCurrentNode(); result = ExecuteNode(currentNode); if (result == VMExitCode.Continue) return result; @@ -120,7 +119,10 @@ VMExitCode ExecuteNode(BHAVAsset.Node node) if (newStackFrame != null) { Thread.Frames.Push(newStackFrame); - return newStackFrame.Tick(); + var res = newStackFrame.Tick(); + if (res != VMExitCode.Continue) + Thread.Frames.Pop(); + return res; } else throw new SimAnticsException("Attempted to GoSub to invalid tree, or called unknown primitive.", this); @@ -140,7 +142,7 @@ enum GoSubFormat // Creates a new stack frame, to push onto the stack to run other scripts. VMStackFrame CreateStackFrameForNode(VMContext ctx) { - var bhav = GetBHAVForOpCode(ctx.Node.OpCode); + var bhav = Thread.Entity.GetBHAV(ctx.Node.OpCode); if (bhav == null) return null; diff --git a/Assets/Scripts/OpenTS2/SimAntics/VMThread.cs b/Assets/Scripts/OpenTS2/SimAntics/VMThread.cs index d5c63134..fb86a106 100644 --- a/Assets/Scripts/OpenTS2/SimAntics/VMThread.cs +++ b/Assets/Scripts/OpenTS2/SimAntics/VMThread.cs @@ -36,6 +36,7 @@ VMExitCode TickInternal() var returnValue = currentFrame.Tick(); if (returnValue == VMExitCode.Continue && !CanYield) throw new SimAnticsException("Attempted to yield in a non-yielding thread.", currentFrame); + while (returnValue != VMExitCode.Continue) { Frames.Pop();