using System; using FSO.Content; using FSO.SimAntics.Marshals.Threads; namespace FSO.SimAntics.Engine { public enum VMSpecialResult { Normal, Interaction, Retry } /// /// Holds information about the execution of a routine /// public class VMStackFrame { public VMStackFrame() { } /** Thread executing this routine **/ public VMThread Thread; /** Routine that this context relates to **/ public VMRoutine Routine; /** Current instruction **/ public byte InstructionPointer; /** The object who executed this behavior **/ public VMEntity Caller; /** The object the code is running on **/ public VMEntity Callee; /** An object selected by the code to perform operations on. **/ public VMEntity StackObject { get { return _StackObject; } set { _StackObject = value; _StackObjectID = value?.ObjectID ?? 0; } } public short StackObjectID { get { return _StackObjectID; } set { _StackObjectID = value; _StackObject = VM.GetObjectById(value); } } private VMEntity _StackObject; public short _StackObjectID; /** * Normally, returning true or false from a stack frame goes to the corresponding node on the parent frame. * This works differently for interactions, and for TS1 loaded trees. (no routing frames, needs to rerun the route primitive) */ public VMSpecialResult SpecialResult; /** Indicates that the current stack frame is part of an action tree. ** Set by "idle for input, allow push", when an interaction is selected. ** Used to stop recursive interactions, is only false when within "main". **/ public bool ActionTree; /** Used to get strings and other resources (for primitives) from the code owner, as it may not be the callee but instead a semiglobal or global. **/ public GameIffResource ScopeResource { get { return CodeOwner.Resource; } } public GameObject CodeOwner; /** * Routine locals */ public short[] Locals; /** * Arguments */ public short[] Args; public GameObjectResource CallerPrivate { get { return Caller.Object.Resource; } } public GameObjectResource CalleePrivate { get { return Callee.Object.Resource; } } public GameObjectResource StackObjPrivate { get { return StackObject.Object.Resource; } } public GameGlobal Global { get { return Thread.Context.Globals; } } public VM VM { get { return Thread.Context.VM; } } /** Utilities **/ public VMInstruction GetCurrentInstruction(){ return Routine.Instructions[InstructionPointer]; } public T GetCurrentOperand(){ return (T)GetCurrentInstruction().Operand; } #region VM Marshalling Functions public virtual VMStackFrameMarshal Save() { return new VMStackFrameMarshal { RoutineID = Routine?.ID ?? 0, InstructionPointer = InstructionPointer, Caller = (Caller == null) ? (short)0 : Caller.ObjectID, Callee = (Callee == null) ? (short)0 : Callee.ObjectID, StackObject = StackObjectID, CodeOwnerGUID = CodeOwner.OBJ.GUID, Locals = (short[])Locals?.Clone(), Args = (short[])Args?.Clone(), SpecialResult = SpecialResult, ActionTree = ActionTree, }; } public virtual void Load(VMStackFrameMarshal input, VMContext context) { CodeOwner = FSO.Content.Content.Get().WorldObjects.Get(input.CodeOwnerGUID); Routine = null; if (input.RoutineID >= 8192) Routine = (VMRoutine)ScopeResource.SemiGlobal.GetRoutine(input.RoutineID); else if (input.RoutineID >= 4096) Routine = (VMRoutine)ScopeResource.GetRoutine(input.RoutineID); else Routine = (VMRoutine)Global.Resource.GetRoutine(input.RoutineID); InstructionPointer = (byte)input.InstructionPointer; Caller = context.VM.GetObjectById(input.Caller); Callee = context.VM.GetObjectById(input.Callee); StackObjectID = input.StackObject; if (Routine != null && input.Locals != null && Routine.Locals > input.Locals.Length) { Locals = new short[Routine.Locals]; Array.Copy(input.Locals, Locals, input.Locals.Length); } else { Locals = input.Locals; } Args = input.Args; SpecialResult = input.SpecialResult; ActionTree = input.ActionTree; } public VMStackFrame(VMStackFrameMarshal input, VMContext context, VMThread thread) { Thread = thread; Load(input, context); } #endregion } }