public override VMPrimitiveExitCode Execute(VMStackFrame context, VMPrimitiveOperand args) { var operand = (VMGetDirectionToOperand)args; var obj2 = context.StackObject; VMEntity obj1; //todo: wrong flag below? if ((operand.Flags & 1) == 0) { obj1 = context.Caller; } else { obj1 = context.VM.GetObjectById(VMMemory.GetVariable(context, operand.ObjectScope, operand.OScopeData)); } var pos1 = obj1.Position; var pos2 = obj2.Position; var direction = DirectionUtils.Normalize(Math.Atan2(pos2.x - pos1.x, pos1.y - pos2.y)); var result = Math.Round((DirectionUtils.PosMod(direction, Math.PI * 2) / Math.PI) * 4); VMMemory.SetVariable(context, operand.ResultOwner, operand.ResultData, (short)result); return(VMPrimitiveExitCode.GOTO_TRUE); }
//the point: //a lot of execution time is spent on if statements and switches that check the operand //a way to remove that entirely would be to write and compile c# functions with those *static* operands in mind. //the compiler can also optimise multiple consecutive expression primitives. This can lead to orders of magnitude speedups for simantics. //(also a point of interest for generating readable simantics code... and perhaps writing our own text language!) //on platforms without JIT, we can still AOT simantics scripts that we expect to be there, then swap to interpreter when //hash functions do not match. we usually do one cs file per iff, and one assembly per iff - but with the AOT setup you would include ALL cs files //in the same fully inclusive assembly. (eg. TS1.Scripts or FSO.Scripts) //building the code // - we build separate inst // - anything that uses a temp variable must be enclosed in a scope (see refresh) //partial implementation and fallbacks // - instructions fall back on their interpreter primitives, pulling from private static operands baked into the class. // - scopes fall back on VMMemory //function modes: // - async function: sets up full vm stack frame system, can yield. exits this function to enter the next, essentially yielding. // - inline function: function is small and guaranteed not to yield (or change). however i would like to extend this to yielding functions somehow, specifically Idle. // - sync function: function is guaranteed to not yield. as a separate function. //structure detection: // - structure types: if/else, switch, loop // - a structure is broken when: structure from a non-nested struct jumps back into a branch, a instruction in the structure yields // - broken structures only fully kill the loop primitive - for if/else and switch they will just return to the global switch statement. // - loop primitives will have to break out eventually regardless - the breaking point is code that jumps *in*. public JitReturnData main(VMStackFrame context, byte instruction) { VMPrimitiveExitCode eResult; while (true) { switch (instruction) { case 0: //0: expression context.Thread.TempRegisters[0] = 1; //1: set to next eResult = new VMSetToNext().Execute(context, context.Routine.Instructions[1].Operand); instruction = (byte)((eResult == VMPrimitiveExitCode.GOTO_TRUE) ? 2 : 4); break; case 2: //2: expression context.StackObject.SetValue(VMStackObjectVariable.BirthDay, VMMemory.GetVariable(context, Scopes.VMVariableScope.StackObjectList, 2)); //3: refresh { VMEntity target = context.Caller; if (target is VMGameObject) { var TargObj = (VMGameObject)target; TargObj.RefreshGraphic(); } } break; } } }
public VMState() { InternalState1 = MainModule.GetOrCreateInternalState1(); Memory1 = MainModule.GetOrCreateVMMemory(); memory2 = MainModule.GetOrCreateMemory2(); object_4 = MainModule.smethod_164(); }
public override VMPrimitiveExitCode Execute(VMStackFrame context, VMPrimitiveOperand args) { var operand = (VMGetDistanceToOperand)args; var obj1 = context.StackObject; VMEntity obj2; if ((operand.Flags & 1) == 0) { obj2 = context.Caller; } else { obj2 = context.VM.GetObjectById(VMMemory.GetVariable(context, (VMVariableScope)operand.ObjectScope, operand.OScopeData)); } var pos1 = obj1.Position; var pos2 = obj2.Position; var result = (short)Math.Floor(Math.Sqrt(Math.Pow(pos1.x - pos2.x, 2) + Math.Pow(pos1.y - pos2.y, 2)) / 16.0); var levelDiff = Math.Abs(pos2.Level - pos1.Level); if (context.VM.TS1) { context.Thread.TempRegisters[operand.TempNum] = (short)Math.Max(20 * levelDiff, result + 5 * levelDiff); } else { context.Thread.TempRegisters[operand.TempNum] = (short)(result + 20 * levelDiff); //FreeSO behaviour. consistently allows us to remove the level factor. } return(VMPrimitiveExitCode.GOTO_TRUE); }
public override VMPrimitiveExitCode Execute(VMStackFrame context, VMPrimitiveOperand args) { var operand = (VMTestObjectTypeOperand)args; var objectID = VMMemory.GetVariable(context, operand.IdOwner, operand.IdData); var obj = (operand.IdOwner == VMVariableScope.StackObjectID)?context.StackObject:context.VM.GetObjectById(objectID); //var obj = context.StackObject; if (obj == null) { return(VMPrimitiveExitCode.ERROR); } if (obj.Object.GUID == operand.GUID) { return(VMPrimitiveExitCode.GOTO_TRUE); //is my guid same? } else if (obj.MasterDefinition != null && (obj.MasterDefinition.GUID == operand.GUID)) { return(VMPrimitiveExitCode.GOTO_TRUE); //is master guid same? } else { return(VMPrimitiveExitCode.GOTO_FALSE); } }
public override VMPrimitiveExitCode Execute(VMStackFrame context) { var operand = context.GetCurrentOperand <VMGetDirectionToOperand>(); var obj2 = context.StackObject; VMEntity obj1; if ((operand.Flags & 1) > 0) { obj1 = context.Caller; } else { obj1 = context.VM.GetObjectById(VMMemory.GetVariable(context, (VMVariableScope)operand.ObjectScope, operand.OScopeData)); } //var pos1 = obj1.Position; var pos1 = context.Caller.Position; var pos2 = obj2.Position; var result = (Math.Round((Math.Atan2(Math.Floor(pos1.X) - Math.Floor(pos2.X), Math.Floor(pos2.Y) - Math.Floor(pos1.Y)) / (Math.PI * 2)) * 8) + 20) % 8; VMMemory.SetVariable(context, (VMVariableScope)operand.ResultOwner, operand.ResultData, (short)result); return(VMPrimitiveExitCode.GOTO_TRUE); }
public override VMPrimitiveExitCode Execute(VMStackFrame context) { var operand = context.GetCurrentOperand <VMChangeSuitOrAccessoryOperand>(); var avatar = (VMAvatar)context.Caller; if ((operand.Flags & VMChangeSuitOrAccessoryFlags.Update) == VMChangeSuitOrAccessoryFlags.Update) { //update outfit with outfit in stringset 304 with index in temp 0 avatar.BodyOutfit = Convert.ToUInt64(context.Callee.Object.Resource.Get <STR>(304).GetString((context.Thread.TempRegisters[0])), 16); } else { var suit = VMMemory.GetSuit(context, operand.SuitScope, operand.SuitData); if (suit == null) { return(VMPrimitiveExitCode.GOTO_TRUE); } if ((operand.Flags & VMChangeSuitOrAccessoryFlags.Remove) == VMChangeSuitOrAccessoryFlags.Remove) { avatar.Avatar.RemoveAccessory(suit); } else { avatar.Avatar.AddAccessory(suit); } } return(VMPrimitiveExitCode.GOTO_TRUE); }
public override VMPrimitiveExitCode Execute(VMStackFrame context, VMPrimitiveOperand args) { var operand = (VMTransferFundsOperand)args; var amount = VMMemory.GetBigVariable(context, operand.GetAmountOwner(), (short)operand.AmountData); if ((operand.Flags & VMTransferFundsFlags.Subtract) > 0) { amount = -amount; //instead of subtracting, we're adding } //weird terms for the flags here but ts1 is inverted //amount contains the amount we are subtracting from the budget. var oldBudget = context.VM.TS1State.CurrentFamily?.Budget ?? 0; var newBudget = oldBudget - amount; if (oldBudget < 0) { return(VMPrimitiveExitCode.GOTO_FALSE); } if ((operand.Flags & VMTransferFundsFlags.JustTest) == 0 && context.VM.TS1State.CurrentFamily != null) { context.VM.TS1State.CurrentFamily.Budget = newBudget; } return(VMPrimitiveExitCode.GOTO_TRUE); //ts1 does have expense types, which could be used for expenses monitoring (i do not think ts1 had this) }
public override VMPrimitiveExitCode Execute(VMStackFrame context) //TODO: Behaviour for being notified out of idle and interaction canceling { var operand = context.GetCurrentOperand <VMIdleForInputOperand>(); if (operand.AllowPush == 1 && context.Thread.Queue.Count > 1) { //if there are any more interactions, we have been interrupted return(VMPrimitiveExitCode.INTERRUPT); } if (context.Thread.Queue[0].Cancelled) { context.Caller.SetFlag(VMEntityFlags.NotifiedByIdleForInput, true); return(VMPrimitiveExitCode.GOTO_TRUE); } var ticks = VMMemory.GetVariable(context, TSO.Simantics.engine.scopes.VMVariableScope.Parameters, operand.StackVarToDec); ticks--; if (ticks < 0) { return(VMPrimitiveExitCode.GOTO_TRUE); } else { VMMemory.SetVariable(context, TSO.Simantics.engine.scopes.VMVariableScope.Parameters, operand.StackVarToDec, ticks); return(VMPrimitiveExitCode.CONTINUE_NEXT_TICK); } }
public override VMPrimitiveExitCode Execute(VMStackFrame context, VMPrimitiveOperand args) { var operand = (VMGotoRoutingSlotOperand)args; if (context.Thread.IsCheck) { return(VMPrimitiveExitCode.GOTO_FALSE); } var slot = VMMemory.GetSlot(context, operand.Type, operand.Data); if (slot == null) { return(VMPrimitiveExitCode.GOTO_FALSE); } var obj = context.StackObject; var avatar = context.Caller; //Routing slots must be type 3. if (slot.Type == 3) { var pathFinder = context.Thread.PushNewRoutingFrame(context, !operand.NoFailureTrees); var success = pathFinder.InitRoutes(slot, context.StackObject); return(VMPrimitiveExitCode.CONTINUE); } else { return(VMPrimitiveExitCode.GOTO_FALSE); } }
public override VMPrimitiveExitCode Execute(VMStackFrame context) { var operand = context.GetCurrentOperand <VMTestObjectTypeOperand>(); var objectID = VMMemory.GetVariable(context, operand.IdOwner, operand.IdData); var obj = context.VM.GetObjectById(objectID); if (obj == null) { return(VMPrimitiveExitCode.ERROR); } if (operand.GUID == 0xDC6D7898) { operand = operand; } if (obj.Object.GUID == operand.GUID) { return(VMPrimitiveExitCode.GOTO_TRUE); //is my guid same? } else if (obj.MasterDefinition != null && (obj.MasterDefinition.GUID == operand.GUID)) { return(VMPrimitiveExitCode.GOTO_TRUE); //is master guid same? } else { return(VMPrimitiveExitCode.GOTO_FALSE); } }
public override VMPrimitiveExitCode Execute(VMStackFrame context) { var operand = context.GetCurrentOperand <VMTransferFundsOperand>(); /** Bit of a legacy thing going on here so there is a helper to translate old owner values into the new scope handler **/ var ammount = VMMemory.GetVariable(context, operand.GetAmmountOwner(), operand.AmmountData); return(VMPrimitiveExitCode.GOTO_TRUE); }
public override VMPrimitiveExitCode Execute(VMStackFrame context, VMPrimitiveOperand args) { var operand = (VMRandomNumberOperand)args; var rangeValue = (ushort)VMMemory.GetVariable(context, operand.RangeScope, operand.RangeData); var result = context.VM.Context.NextRandom(rangeValue); VMMemory.SetVariable(context, operand.DestinationScope, operand.DestinationData, (short)result); return(VMPrimitiveExitCode.GOTO_TRUE); }
public override VMPrimitiveExitCode Execute(VMStackFrame context) { var operand = context.GetCurrentOperand <VMSetToNextOperand>(); var targetValue = VMMemory.GetVariable(context, operand.GetTargetOwner(), operand.GetTargetData()); if (operand.SearchType == VMSetToNextSearchType.ObjectOfType) { //TODO: Implement! return(VMPrimitiveExitCode.GOTO_FALSE); } throw new Exception("Unknown search type"); }
public override VMPrimitiveExitCode Execute(VMStackFrame context) { var operand = context.GetCurrentOperand <VMRandomNumberOperand>(); var rangeValue = (ushort)VMMemory.GetVariable(context, operand.RangeScope, operand.RangeData); var result = context.VM.Context.NextRandom(rangeValue); VMMemory.SetVariable(context, operand.DestinationScope, operand.DestinationData, (short)result); if (operand.RangeData == 8327) { result = 0; } return(VMPrimitiveExitCode.GOTO_TRUE); }
public override VMPrimitiveExitCode Execute(VMStackFrame context) { var operand = context.GetCurrentOperand <VMGotoRoutingSlotOperand>(); var slot = VMMemory.GetSlot(context, operand.Type, operand.Data); var obj = (VMGameObject)context.Callee; var avatar = (VMAvatar)context.Caller; //slot.Rsflags = tso.files.formats.iff.chunks.SLOTFlags.WEST; /** * Very little is kown about SLOTs so for now this is a place to dump comments * * Slots measure proximity in units of 16. 16 = 1 tile away from the object. * Global slots are in global.iff in a slot table with ID 100. * global.iff also has a string table #257 which provides labels for the SLOTs */ //Dont really know what 3 means, maybe relative to object? if (slot.Type == 3) { var tilePosition = new Vector2(obj.Position.X, obj.Position.Y); var min = slot.MinProximity; var max = slot.MaxProximity; var desired = slot.OptimalProximity; if (max == 0) { max = min; } if (desired == 0) { desired = min; } var possibleTargets = VMRouteFinder.FindAvaliableLocations(tilePosition, slot.Rsflags, min, max, desired); if (possibleTargets.Count == 0) { return(VMPrimitiveExitCode.GOTO_FALSE); } //TODO: Route finding and pick best route var target = possibleTargets[0]; avatar.Position = new Vector3(target.Position.X + 0.5f, target.Position.Y + 0.5f, 0.0f); } return(VMPrimitiveExitCode.GOTO_TRUE_NEXT_TICK); }
public override VMPrimitiveExitCode Execute(VMStackFrame context) { var operand = context.GetCurrentOperand <VMSetMotiveChangeOperand>(); var avatar = ((VMAvatar)context.Caller); if ((operand.Flags & VMSetMotiveChangeFlags.ClearAll) > 0) { avatar.ClearMotiveChanges(); } else { var PerHourChange = VMMemory.GetVariable(context, (VMVariableScope)operand.DeltaOwner, (ushort)operand.DeltaData); var MaxValue = VMMemory.GetVariable(context, (VMVariableScope)operand.MaxOwner, (ushort)operand.MaxData); avatar.SetMotiveChange(operand.Motive, PerHourChange, MaxValue); } return(VMPrimitiveExitCode.GOTO_TRUE); }
public override VMPrimitiveExitCode Execute(VMStackFrame context, VMPrimitiveOperand args) { var operand = (VMSetMotiveChangeOperand)args; var avatar = ((VMAvatar)context.Caller); if (operand.Once) { } if (operand.ClearAll) { avatar.ClearMotiveChanges(); } else { var rate = VMMemory.GetVariable(context, (VMVariableScope)operand.DeltaOwner, operand.DeltaData); var MaxValue = VMMemory.GetVariable(context, (VMVariableScope)operand.MaxOwner, operand.MaxData); if (operand.Once) { var motive = avatar.GetMotiveData(operand.Motive); if (((rate > 0) && (motive > MaxValue)) || ((rate < 0) && (motive < MaxValue))) { return(VMPrimitiveExitCode.GOTO_TRUE); } // ^ we're already over, do nothing. (do NOT clamp) motive += rate; if (((rate > 0) && (motive > MaxValue)) || ((rate < 0) && (motive < MaxValue))) { motive = MaxValue; } avatar.SetMotiveData(operand.Motive, motive); } else { avatar.SetMotiveChange(operand.Motive, rate, MaxValue); } } return(VMPrimitiveExitCode.GOTO_TRUE); }
public override VMPrimitiveExitCode Execute(VMStackFrame context) { var operand = context.GetCurrentOperand <VMRandomNumberOperand>(); var description = "random: less than " + VMMemory.DescribeVariable(context, operand.RangeScope, operand.RangeData); description += " written to " + VMMemory.DescribeVariable(context, operand.DestinationScope, operand.DestinationData); Trace(description); //TODO: Make this deterministic var rangeValue = VMMemory.GetVariable(context, operand.RangeScope, operand.RangeData); var rand = new Random(); var result = rand.Next(rangeValue); VMMemory.SetVariable(context, operand.DestinationScope, operand.DestinationData, (short)result); Trace("set " + operand.DestinationScope + " #" + operand.DestinationData + " to random " + result); return(VMPrimitiveExitCode.GOTO_TRUE); }
public override VMPrimitiveExitCode Execute(VMStackFrame context) { var operand = context.GetCurrentOperand <VMSleepOperand>(); var ticks = VMMemory.GetVariable(context, TSO.Simantics.engine.scopes.VMVariableScope.Parameters, operand.StackVarToDec); //if (ticks > 0) Trace("sleeping...") ; ticks--; if (ticks < 0) { return(VMPrimitiveExitCode.GOTO_TRUE); } else { VMMemory.SetVariable(context, TSO.Simantics.engine.scopes.VMVariableScope.Parameters, operand.StackVarToDec, ticks); return(VMPrimitiveExitCode.CONTINUE_NEXT_TICK); } }
public override VMPrimitiveExitCode Execute(VMStackFrame context) { var operand = context.GetCurrentOperand <VMSleepOperand>(); Trace("sleep: --(" + VMMemory.DescribeVariable(context, VMVariableScope.Local, operand.StackVarToDec) + ") != 0"); var ticks = VMMemory.GetVariable(context, tso.simantics.engine.scopes.VMVariableScope.Local, operand.StackVarToDec); ticks--; if (ticks < 0) { return(VMPrimitiveExitCode.GOTO_TRUE); } else { VMMemory.SetVariable(context, tso.simantics.engine.scopes.VMVariableScope.Local, operand.StackVarToDec, ticks); return(VMPrimitiveExitCode.CONTINUE_NEXT_TICK); } }
public override VMPrimitiveExitCode Execute(VMStackFrame context, VMPrimitiveOperand args) //TODO: Behaviour for being notified out of idle and interaction canceling { var operand = (VMIdleForInputOperand)args; //if we're main, attempt to run a queued interaction. We just idle if this fails. if (operand.AllowPush == 1 && !context.ActionTree && context.Thread.AttemptPush()) { return(VMPrimitiveExitCode.CONTINUE); //control handover //TODO: does this forcefully end the rest of the idle? (force a true return, must loop back to run again) } if (context.Thread.Queue.Count > 0) { if (context.ActionTree && context.Thread.Queue[0].Cancelled) { context.Caller.SetFlag(VMEntityFlags.NotifiedByIdleForInput, true); return(VMPrimitiveExitCode.GOTO_TRUE); } } if (context.Thread.Interrupt) { context.Thread.Interrupt = false; return(VMPrimitiveExitCode.GOTO_TRUE); } var ticks = VMMemory.GetVariable(context, FSO.SimAntics.Engine.Scopes.VMVariableScope.Parameters, operand.StackVarToDec); ticks--; if (ticks < 0) { return(VMPrimitiveExitCode.GOTO_TRUE); } else { VMMemory.SetVariable(context, FSO.SimAntics.Engine.Scopes.VMVariableScope.Parameters, operand.StackVarToDec, ticks); return(VMPrimitiveExitCode.CONTINUE_NEXT_TICK); } }
public override VMPrimitiveExitCode Execute(VMStackFrame context, VMPrimitiveOperand args) { var operand = (VMSetMotiveChangeOperand)args; var avatar = ((VMAvatar)context.Caller); if (operand.Once) { } if (operand.ClearAll) { avatar.ClearMotiveChanges(); } else { var PerHourChange = VMMemory.GetVariable(context, (VMVariableScope)operand.DeltaOwner, operand.DeltaData); var MaxValue = VMMemory.GetVariable(context, (VMVariableScope)operand.MaxOwner, operand.MaxData); avatar.SetMotiveChange(operand.Motive, PerHourChange, MaxValue); } return(VMPrimitiveExitCode.GOTO_TRUE); }
public override VMPrimitiveExitCode Execute(VMStackFrame context) { var operand = context.GetCurrentOperand <VMTestObjectTypeOperand>(); var objectID = VMMemory.GetVariable(context, operand.IdOwner, operand.IdData); var obj = context.VM.GetObjectById(objectID); if (obj == null) { return(VMPrimitiveExitCode.ERROR); } //TODO: This should check if obj or masterID is type not just single tile if (obj.Object.GUID == operand.GUID) { return(VMPrimitiveExitCode.GOTO_TRUE); } else { return(VMPrimitiveExitCode.GOTO_FALSE); } }
public override VMPrimitiveExitCode Execute(VMStackFrame context) { var operand = context.GetCurrentOperand <VMChangeSuitOrAccessoryOperand>(); var suit = VMMemory.GetSuit(context, operand.SuitScope, operand.SuitData); var avatar = (VMAvatar)context.Caller; if (suit == null) { return(VMPrimitiveExitCode.GOTO_FALSE); } if ((operand.Flags & VMChangeSuitOrAccessoryFlags.Remove) == VMChangeSuitOrAccessoryFlags.Remove) { avatar.Avatar.RemoveAccessory(suit); } else { avatar.Avatar.AddAccessory(suit); } return(VMPrimitiveExitCode.GOTO_TRUE_NEXT_TICK); }
public override VMPrimitiveExitCode Execute(VMStackFrame context, VMPrimitiveOperand args) { var operand = (VMGetDistanceToOperand)args; var obj1 = context.StackObject; VMEntity obj2; if ((operand.Flags & 1) == 0) { obj2 = context.Caller; } else { obj2 = context.VM.GetObjectById(VMMemory.GetVariable(context, (VMVariableScope)operand.ObjectScope, operand.OScopeData)); } var pos1 = obj1.Position; var pos2 = obj2.Position; var result = (short)Math.Floor(Math.Sqrt(Math.Pow(pos1.x - pos2.x, 2) + Math.Pow(pos1.y - pos2.y, 2)) / 16.0); context.Thread.TempRegisters[operand.TempNum] = result; return(VMPrimitiveExitCode.GOTO_TRUE); }
public override VMPrimitiveExitCode Execute(VMStackFrame context, VMPrimitiveOperand args) { var operand = (VMSleepOperand)args; if (context.Thread.Interrupt) { context.Thread.Interrupt = false; return(VMPrimitiveExitCode.GOTO_TRUE); } var ticks = VMMemory.GetVariable(context, FSO.SimAntics.Engine.Scopes.VMVariableScope.Parameters, operand.StackVarToDec); ticks--; if (ticks < 0) { return(VMPrimitiveExitCode.GOTO_TRUE); } else { VMMemory.SetVariable(context, FSO.SimAntics.Engine.Scopes.VMVariableScope.Parameters, operand.StackVarToDec, ticks); return(VMPrimitiveExitCode.CONTINUE_NEXT_TICK); } }
public override VMPrimitiveExitCode Execute(VMStackFrame context, VMPrimitiveOperand args) { var operand = (VMRelationshipOperand)args; VMEntity obj1; VMEntity obj2; switch (operand.Mode) { case 0: //from me to stack object obj1 = context.Caller; obj2 = context.StackObject; break; case 1: //from stack object to me obj1 = context.StackObject; obj2 = context.Caller; break; case 2: //from stack object to object in local/stack param obj1 = context.StackObject; obj2 = context.VM.GetObjectById((operand is VMOldRelationshipOperand) ? context.Args[0] : context.Locals[operand.Local]); break; case 3: //from object in local/stack param to stack object obj1 = context.VM.GetObjectById((operand is VMOldRelationshipOperand) ? context.Args[0] : context.Locals[operand.Local]); obj2 = context.StackObject; break; default: throw new VMSimanticsException("Invalid relationship type!", context); } var ts1 = context.VM.TS1; if ((obj1 == null || obj2 == null) && !(ts1 && operand.UseNeighbor)) { return(VMPrimitiveExitCode.GOTO_TRUE); } List <short> relToTarg; var myNID = (obj1 as VMAvatar)?.GetPersonData(Model.VMPersonDataVariable.NeighborId) ?? 0; var targNID = (obj2 as VMAvatar)?.GetPersonData(Model.VMPersonDataVariable.NeighborId) ?? 0; if (!ts1 && obj2.PersistID > 0 && !operand.FSONeverPersist) { //use persist matrix whenever possible. //ignores use neighbour flag so we can use str/ltr. var rels = obj1.MeToPersist; var targId = obj2.PersistID; if (!rels.ContainsKey(targId)) { if (operand.FailIfTooSmall) { return(VMPrimitiveExitCode.GOTO_FALSE); } else { rels.Add(targId, new List <short>()); } } if (operand.SetMode > 0) { obj1.ChangedRels.Add(targId); } relToTarg = rels[targId]; } else if (ts1 && (operand.UseNeighbor || (myNID > 0 && targNID > 0))) { //ts1 neighbour matrix if (operand.UseNeighbor) { switch (operand.Mode) { case 0: //from me to stack object myNID = ((VMAvatar)context.Caller).GetPersonData(Model.VMPersonDataVariable.NeighborId); targNID = context.StackObjectID; break; case 1: //from stack object to me myNID = context.StackObjectID; targNID = ((VMAvatar)context.Caller).GetPersonData(Model.VMPersonDataVariable.NeighborId); break; case 2: //from stack object to object in local/stack param myNID = context.StackObjectID; targNID = (operand is VMOldRelationshipOperand) ? context.Args[0] : context.Locals[operand.Local]; break; case 3: //from object in local/stack param to stack object myNID = (operand is VMOldRelationshipOperand) ? context.Args[0] : context.Locals[operand.Local]; targNID = context.StackObjectID; break; default: throw new VMSimanticsException("Invalid relationship type!", context); } } var rels = Content.Content.Get().Neighborhood.GetNeighborByID(myNID).Relationships; if (!rels.ContainsKey(targNID)) { if (operand.FailIfTooSmall) { return(VMPrimitiveExitCode.GOTO_FALSE); } else { rels.Add(targNID, new List <short>()); } } relToTarg = rels[targNID]; } else { var rels = obj1.MeToObject; var targId = (ushort)obj2.ObjectID; //check if exists if (!rels.ContainsKey(targId)) { if (operand.FailIfTooSmall) { return(VMPrimitiveExitCode.GOTO_FALSE); } else { rels.Add(targId, new List <short>()); } } relToTarg = rels[targId]; obj2.MayHaveRelToMe.Add((ushort)obj1.ObjectID); } if (relToTarg.Count <= operand.RelVar) { if (operand.FailIfTooSmall) { return(VMPrimitiveExitCode.GOTO_FALSE); } else { while (relToTarg.Count <= operand.RelVar) { relToTarg.Add(0); } } } //todo: move to tuning? var diffMultiplier = context.VM.Tuning?.GetTuning("category_mul", 0, context.VM.TSOState.PropertyCategory) ?? 1f; //0: relationship, 1: skill/money, 2: visitor hour scale if (operand.SetMode == 0) { VMMemory.SetVariable(context, operand.VarScope, operand.VarData, relToTarg[operand.RelVar]); } else if (operand.SetMode == 1) { var value = VMMemory.GetVariable(context, operand.VarScope, operand.VarData); relToTarg[operand.RelVar] = Math.Max((short)-100, Math.Min((short)100, value)); } else if (operand.SetMode == 2) { var value = VMMemory.GetVariable(context, operand.VarScope, operand.VarData); relToTarg[operand.RelVar] += (short)(value * diffMultiplier); relToTarg[operand.RelVar] = Math.Max((short)-100, Math.Min((short)100, relToTarg[operand.RelVar])); } return(VMPrimitiveExitCode.GOTO_TRUE); }
public override VMPrimitiveExitCode Execute(VMStackFrame context, VMPrimitiveOperand args) { var operand = (VMSetToNextOperand)args; var targetValue = VMMemory.GetVariable(context, operand.TargetOwner, operand.TargetData); var entities = context.VM.Entities; VMEntity Pointer = context.VM.GetObjectById(targetValue); //re-evaluation of what this actually does: //tries to find the next object id (from the previous) that meets a specific condition. //the previous object id is supplied via the target variable // //we should take the first result with object id > targetValue. if (operand.SearchType == VMSetToNextSearchType.PartOfAMultipartTile) { var target = context.VM.GetObjectById(targetValue); if (target == null || (!target.MultitileGroup.MultiTile)) { return(VMPrimitiveExitCode.GOTO_FALSE); //single part } else { var group = target.MultitileGroup.Objects; bool found = false; short bestID = 0; short smallestID = 0; for (int i = 0; i < group.Count; i++) { var temp = group[i]; if (temp.ObjectID < smallestID || smallestID == 0) { smallestID = temp.ObjectID; } if (temp.ObjectID > targetValue) { if ((!found) || (temp.ObjectID < bestID)) { found = true; bestID = temp.ObjectID; } } } if (found) { VMMemory.SetVariable(context, operand.TargetOwner, operand.TargetData, bestID); return(VMPrimitiveExitCode.GOTO_TRUE); } else { VMMemory.SetVariable(context, operand.TargetOwner, operand.TargetData, smallestID); return(VMPrimitiveExitCode.GOTO_TRUE); } } } else if (operand.SearchType == VMSetToNextSearchType.ObjectAdjacentToObjectInLocal) { VMEntity anchor = context.VM.GetObjectById((short)context.Locals[operand.Local]); int ptrDir = -1; targetValue = 0; if (Pointer != null) { ptrDir = getAdjDir(anchor, Pointer); if (ptrDir == 3) { return(VMPrimitiveExitCode.GOTO_FALSE); //reached end } } //iterate through all following dirs til we find an object for (int i = ptrDir + 1; i < 4; i++) { var off = AdjStep[i]; var adj = context.VM.Context.ObjectQueries.GetObjectsAt(LotTilePos.FromBigTile( (short)(anchor.Position.TileX + off.X), (short)(anchor.Position.TileY + off.Y), anchor.Position.Level)); if (adj != null && adj.Count > 0) { //lists are ordered by object id. first is the smallest. VMMemory.SetVariable(context, operand.TargetOwner, operand.TargetData, adj[0].ObjectID); return(VMPrimitiveExitCode.GOTO_TRUE); } } return(VMPrimitiveExitCode.GOTO_FALSE); } else if (operand.SearchType == VMSetToNextSearchType.Career) { var next = Content.Content.Get().Jobs.SetToNext(targetValue); if (next < 0) { return(VMPrimitiveExitCode.GOTO_FALSE); } VMMemory.SetVariable(context, operand.TargetOwner, operand.TargetData, next); return(VMPrimitiveExitCode.GOTO_TRUE); } else if (operand.SearchType == VMSetToNextSearchType.NeighborId) { var next = Content.Content.Get().Neighborhood.SetToNext(targetValue); if (next < 0) { return(VMPrimitiveExitCode.GOTO_FALSE); } VMMemory.SetVariable(context, operand.TargetOwner, operand.TargetData, next); return(VMPrimitiveExitCode.GOTO_TRUE); } else if (operand.SearchType == VMSetToNextSearchType.NeighborOfType) { var next = Content.Content.Get().Neighborhood.SetToNext(targetValue, operand.GUID); if (next < 0) { return(VMPrimitiveExitCode.GOTO_FALSE); } VMMemory.SetVariable(context, operand.TargetOwner, operand.TargetData, next); return(VMPrimitiveExitCode.GOTO_TRUE); } else { //if we've cached the search type, use that instead of all objects switch (operand.SearchType) { case VMSetToNextSearchType.ObjectOnSameTile: entities = context.VM.Context.ObjectQueries.GetObjectsAt(Pointer.Position); break; case VMSetToNextSearchType.Person: case VMSetToNextSearchType.FamilyMember: entities = context.VM.Context.ObjectQueries.Avatars; break; case VMSetToNextSearchType.ObjectOfType: entities = context.VM.Context.ObjectQueries.GetObjectsByGUID(operand.GUID); break; case VMSetToNextSearchType.ObjectWithCategoryEqualToSP0: entities = context.VM.Context.ObjectQueries.GetObjectsByCategory(context.Args[0]); break; default: break; } if (entities == null) { return(VMPrimitiveExitCode.GOTO_FALSE); } bool loop = (operand.SearchType == VMSetToNextSearchType.ObjectOnSameTile); VMEntity first = null; for (int i = 0; i < entities.Count; i++) //generic search through all objects { var temp = entities[i]; bool found = false; if (temp.ObjectID > targetValue || loop) { switch (operand.SearchType) { //manual search types case VMSetToNextSearchType.NonPerson: found = (temp is VMGameObject); break; case VMSetToNextSearchType.ClosestHouse: return(VMPrimitiveExitCode.GOTO_FALSE); throw new VMSimanticsException("Not implemented!", context); case VMSetToNextSearchType.FamilyMember: found = context.VM.CurrentFamily?.FamilyGUIDs?.Contains(((VMAvatar)temp).Object.OBJ.GUID) ?? false; break; default: //set to next object, or cached search. found = true; break; } if (temp.ObjectID <= targetValue && found) { //remember the first element in case we need to loop back to it (set to next tile on same location) if (first == null) { first = temp; } found = false; } } if (found) { VMMemory.SetVariable(context, operand.TargetOwner, operand.TargetData, temp.ObjectID); return(VMPrimitiveExitCode.GOTO_TRUE); } } if (loop) { if (first == null) { return(VMPrimitiveExitCode.GOTO_FALSE); //no elements of this kind at all. } else { VMMemory.SetVariable(context, operand.TargetOwner, operand.TargetData, first.ObjectID); //set to loop, so go back to lowest obj id. return(VMPrimitiveExitCode.GOTO_TRUE); } //loop around } } return(VMPrimitiveExitCode.GOTO_FALSE); //ran out of objects to test }
public static string ParseDialogString(VMStackFrame context, string input, STR source, int depth) { if (depth > 10) { return(input); } int state = 0; StringBuilder command = new StringBuilder(); StringBuilder output = new StringBuilder(); if (input == null) { return("Missing String!!!"); } for (int i = 0; i < input.Length; i++) { if (state == 0) { if (input[i] == '$') { state = 1; //start parsing string command.Clear(); } else { output.Append(input[i]); } } else { command.Append(input[i]); if (i == input.Length - 1 || !CommandSubstrValid(command.ToString())) { if (i != input.Length - 1 || char.IsDigit(input[i])) { command.Remove(command.Length - 1, 1); i--; } var cmdString = command.ToString(); short[] values = new short[3]; if (cmdString.Length > 1 && cmdString[cmdString.Length - 1] == ':') { try { if (cmdString == "DynamicStringLocal:" || cmdString == "TimeLocal:" || cmdString == "JobOffer:" || cmdString == "Job:" || cmdString == "JobDesc:" || cmdString == "DateLocal:") { values[1] = -1; values[2] = -1; for (int j = 0; j < 3; j++) { char next = input[++i]; string num = ""; while (char.IsDigit(next)) { num += next; next = (++i == input.Length) ? '!': input[i]; } if (num == "") { values[j] = -1; if (j == 1) { values[2] = -1; } break; } values[j] = short.Parse(num); if (i == input.Length || next != ':') { break; } } } else { char next = input[++i]; string num = ""; while (char.IsDigit(next)) { num += next; next = (++i == input.Length) ? '!' : input[i]; } values[0] = short.Parse(num); } i--; } catch (FormatException) { } } try { switch (cmdString) { case "Object": case "DynamicObjectName": //hack: if stack object doesn't exist and should contain owner's id, //try output the callee's owner id instead for tip jar. //special id for this is -1. if (context.StackObjectID == -1 && !context.VM.TS1) { //StackObjectOwnerID call sets the id to -1 if no owner found. (null is usually 0) output.Append(context.VM.TSOState.Names.GetNameForID( context.VM, (context.Callee.TSOState as VMTSOObjectState)?.OwnerID ?? 0 )); } else { output.Append(context.StackObject.ToString()); } break; case "Me": output.Append(context.Caller.ToString()); break; case "TempXL:": output.Append(VMMemory.GetBigVariable(context, Scopes.VMVariableScope.TempXL, values[0]).ToString()); break; case "MoneyXL:": output.Append("$" + VMMemory.GetBigVariable(context, Scopes.VMVariableScope.TempXL, values[0]).ToString("##,#0")); break; case "Temp:": output.Append(VMMemory.GetBigVariable(context, Scopes.VMVariableScope.Temps, values[0]).ToString()); break; case "$": output.Append("$"); i--; break; case "Attribute:": output.Append(VMMemory.GetBigVariable(context, Scopes.VMVariableScope.MyObjectAttributes, values[0]).ToString()); break; case "DynamicStringLocal:": STR res = null; if (values[2] != -1 && values[1] != -1) { VMEntity obj = context.VM.GetObjectById((short)context.Locals[values[2]]); if (obj == null) { break; } ushort tableID = (ushort)context.Locals[values[1]]; { //local if (obj.SemiGlobal != null) { res = obj.SemiGlobal.Get <STR>(tableID); } if (res == null) { res = obj.Object.Resource.Get <STR>(tableID); } if (res == null) { res = context.Global.Resource.Get <STR>(tableID); } } } else if (values[1] != -1) { //global table ushort tableID = (ushort)context.Locals[values[1]]; res = context.Global.Resource.Get <STR>(tableID); } else { res = source; } ushort index = (ushort)context.Locals[values[0]]; if (res != null) { var str = res.GetString(index); output.Append(ParseDialogString(context, str, res, depth++)); // recursive command parsing! // this is needed for the crafting table. // though it is also, completely insane? } break; case "Local:": output.Append(VMMemory.GetBigVariable(context, Scopes.VMVariableScope.Local, values[0]).ToString()); break; case "FixedLocal:": output.Append((VMMemory.GetBigVariable(context, Scopes.VMVariableScope.Local, values[0]) / 100f).ToString("F2")); break; case "TimeLocal:": var hours = VMMemory.GetBigVariable(context, Scopes.VMVariableScope.Local, values[0]); var mins = (values[1] == -1)?0:VMMemory.GetBigVariable(context, Scopes.VMVariableScope.Local, values[1]); var suffix = (hours > 11) ? "pm" : "am"; if (hours > 12) { hours -= 12; } output.Append(hours.ToString()); output.Append(":"); output.Append(mins.ToString().PadLeft(2, '0')); output.Append(suffix); break; case "JobOffer:": output.Append(Content.Content.Get().Jobs.JobOffer( (short)VMMemory.GetBigVariable(context, Scopes.VMVariableScope.Local, values[0]), VMMemory.GetBigVariable(context, Scopes.VMVariableScope.Local, values[1]))); break; case "Job:": case "JobDesc:": var level = VMMemory.GetBigVariable(context, Scopes.VMVariableScope.Local, values[1]); var jobStr = Content.Content.Get().Jobs.JobStrings( (short)VMMemory.GetBigVariable(context, Scopes.VMVariableScope.Local, values[0])); if (jobStr != null) { output.Append(jobStr.GetString(level * 3 + ((cmdString == "JobDesc:")?3:4))); } break; case "Param:": output.Append(VMMemory.GetBigVariable(context, Scopes.VMVariableScope.Parameters, values[0]).ToString()); break; case "NameLocal:": output.Append(context.VM.GetObjectById(VMMemory.GetVariable(context, Scopes.VMVariableScope.Local, values[0])).ToString()); break; case "Neighbor": //neighbour in stack object id if (!context.VM.TS1) { break; } var guid = Content.Content.Get().Neighborhood.GetNeighborByID(context.StackObjectID)?.GUID ?? 0; var gobj = Content.Content.Get().WorldObjects.Get(guid); if (gobj == null) { output.Append("Unknown"); } else { output.Append(gobj.Resource.Get <FSO.Files.Formats.IFF.Chunks.CTSS>(gobj.OBJ.CatalogStringsID)?.GetString(0) ?? "Unknown"); } break; case "ListObject": output.Append(new string(context.StackObject.MyList.Select(x => (char)x).ToArray())); break; case "CatalogLocal:": var catObj = context.VM.GetObjectById(VMMemory.GetVariable(context, Scopes.VMVariableScope.Local, values[0])); var cat = catObj.Object.Resource.Get <CTSS>(catObj.Object.OBJ.CatalogStringsID)?.GetString(1); output.Append(cat ?? ""); break; case "DateLocal:": var date = new DateTime(context.Locals[values[2]], context.Locals[values[1]], context.Locals[values[0]]); output.Append(date.ToLongDateString()); break; case "\\n": output.Append("\n"); break; default: output.Append(cmdString); break; } } catch (Exception) { //something went wrong. just skip command } state = 0; } } } output.Replace("\r\n", "\r\n\r\n"); return(output.ToString()); }