private void SetFail(VMRouteFailCode code, VMEntity blocker) { if (Array.IndexOf(FailPrio, code) > Array.IndexOf(FailPrio, FailCode)) { FailCode = code; Blocker = blocker; } }
// TODO: float values may desync if devices are not both x86 or using a different C# library. // Might need to replace with fixed point library for position and rotation /// <summary> /// This method will find all the avaliable locations within the criteria ordered by proximity to the optimal proximity /// External functions can then decide which is most desirable. E.g. the nearest slot to the object may be the longest route if /// its in another room. /// </summary> /// <param name="obj"></param> /// <param name="slot"></param> /// <param name="context"></param> /// <returns></returns> public List <VMFindLocationResult> FindAvaliableLocations(VMEntity obj, VMContext context, VMEntity caller) { /** * Start at min proximity and circle around the object to find the avaliable locations. * Then pick the one nearest to the optimal value */ /** * ------ MAJOR TODO: ------ * Avoid vector math at all costs! Small differences in hardware could cause desyncs. * This really goes for all areas of the SimAntics engine, but here it's particularly bad. */ Vector2 center; if (OnlySit) { FailCode = VMRouteFailCode.NoChair; } // if we need to use the average location of an object group, it needs to be calculated. if (((Flags & SLOTFlags.UseAverageObjectLocation) > 0) && (obj.MultitileGroup.MultiTile)) { center = new Vector2(0, 0); var objs = obj.MultitileGroup.Objects; for (int i = 0; i < objs.Count; i++) { center += new Vector2(objs[i].Position.x / 16f, objs[i].Position.y / 16f); } center /= objs.Count; } else { center = new Vector2(obj.Position.x / 16f, obj.Position.y / 16f); } //add offset of slot if it exists. must be rotated to be relative to object var rotOff = Vector3.Transform(Slot.Offset, Matrix.CreateRotationZ(obj.RadianDirection)); var circleCtr = new Vector2(center.X + rotOff.X / 16, center.Y + rotOff.Y / 16); ushort room = context.VM.Context.GetRoomAt(obj.Position); Results = new List <VMFindLocationResult>(); if ((Flags & SLOTFlags.SnapToDirection) > 0) { //snap to the specified direction, on the specified point. double baseRot; if (Slot.Facing > SLOTFacing.FaceAwayFromObject) { // bit of a legacy thing here. Facing field did not use to exist, // which is why SnapToDirection was hacked to use the directional flags. // now that it exists, it is used instead, to encode the same information... // just transform back into old format. Flags |= (SLOTFlags)(1 << (int)Slot.Facing); } else { if (((int)Flags & 255) == 0) { Flags |= SLOTFlags.NORTH; } } var flagRot = DirectionUtils.PosMod(obj.RadianDirection + FlagsAsRad(Flags), Math.PI * 2); if (flagRot > Math.PI) { flagRot -= Math.PI * 2; } VerifyAndAddLocation(obj, circleCtr, center, Flags, Double.MaxValue, context, caller, (float)flagRot); return(Results); } else { if (((int)Flags & 255) == 0 || Slot.Offset != new Vector3()) { //exact position //Flags |= (SLOTFlags)255; // special case, walk directly to point. VerifyAndAddLocation(obj, circleCtr, center, Flags, Double.MaxValue, context, caller, float.NaN); return(Results); } var maxScore = Math.Max(DesiredProximity - MinProximity, MaxProximity - DesiredProximity) + (LotTilePos.Distance(obj.Position, caller.Position) + MaxProximity) / 3 + 2; var ignoreRooms = (Flags & SLOTFlags.IgnoreRooms) > 0; SLOTEnumerationFunction((x, y, distance) => { var pos = new Vector2(circleCtr.X + x / 16.0f, circleCtr.Y + y / 16.0f); if (distance >= MinProximity - 0.5 && distance <= MaxProximity + 0.5 && (ignoreRooms || context.VM.Context.GetRoomAt(new LotTilePos((short)Math.Round(pos.X * 16), (short)Math.Round(pos.Y * 16), obj.Position.Level)) == room)) //slot is within proximity { var routeEntryFlags = (GetSearchDirection(circleCtr, pos, obj.RadianDirection) & Flags); //the route needs to know what conditions it fulfilled if (routeEntryFlags > 0) //within search location { double baseScore = ((maxScore - Math.Abs(DesiredProximity - distance)) + context.VM.Context.NextRandom(1024) / 1024.0f); VerifyAndAddLocation(obj, pos, center, routeEntryFlags, baseScore, context, caller, float.NaN); } } }); } /** Sort by how close they are to desired proximity **/ if (Results.Count > 1) { Results = Results.OrderBy(x => - x.Score).ToList(); //avoid sort because it acts incredibly unusually } if (Results.Count > 0) { FailCode = VMRouteFailCode.Success; } return(Results); }
// TODO: float values may desync if devices are not both x86 or using a different C# library. // Might need to replace with fixed point library for position and rotation /// <summary> /// This method will find all the avaliable locations within the criteria ordered by proximity to the optimal proximity /// External functions can then decide which is most desirable. E.g. the nearest slot to the object may be the longest route if /// its in another room. /// </summary> /// <param name="obj"></param> /// <param name="slot"></param> /// <param name="context"></param> /// <returns></returns> public List<VMFindLocationResult> FindAvaliableLocations(VMEntity obj, VMContext context, VMEntity caller) { /** * Start at min proximity and circle around the object to find the avaliable locations. * Then pick the one nearest to the optimal value */ /** * ------ MAJOR TODO: ------ * Avoid vector math at all costs! Small differences in hardware could cause desyncs. * This really goes for all areas of the SimAntics engine, but here it's particularly bad. */ Vector2 center; if (OnlySit) FailCode = VMRouteFailCode.NoChair; // if we need to use the average location of an object group, it needs to be calculated. if (((Flags & SLOTFlags.UseAverageObjectLocation) > 0) && (obj.MultitileGroup.MultiTile)) { center = new Vector2(0, 0); var objs = obj.MultitileGroup.Objects; for (int i = 0; i < objs.Count; i++) { center += new Vector2(objs[i].Position.x/16f, objs[i].Position.y/16f); } center /= objs.Count; } else center = new Vector2(obj.Position.x/16f, obj.Position.y/16f); //add offset of slot if it exists. must be rotated to be relative to object var rotOff = Vector3.Transform(Slot.Offset, Matrix.CreateRotationZ(obj.RadianDirection)); var circleCtr = new Vector2(center.X + rotOff.X / 16, center.Y + rotOff.Y / 16); ushort room = context.VM.Context.GetRoomAt(obj.Position); Results = new List<VMFindLocationResult>(); if ((Flags & SLOTFlags.SnapToDirection) > 0) { //snap to the specified direction, on the specified point. double baseRot; if (Slot.Facing > SLOTFacing.FaceAwayFromObject) { // bit of a legacy thing here. Facing field did not use to exist, // which is why SnapToDirection was hacked to use the directional flags. // now that it exists, it is used instead, to encode the same information... // just transform back into old format. Flags |= (SLOTFlags)(1 << (int)Slot.Facing); } else { if (((int)Flags & 255) == 0) Flags |= SLOTFlags.NORTH; } var flagRot = DirectionUtils.PosMod(obj.RadianDirection+FlagsAsRad(Flags), Math.PI*2); if (flagRot > Math.PI) flagRot -= Math.PI * 2; VerifyAndAddLocation(obj, circleCtr, center, Flags, Double.MaxValue, context, caller, (float)flagRot); return Results; } else { if (((int)Flags & 255) == 0 || Slot.Offset != new Vector3()) { //exact position //Flags |= (SLOTFlags)255; // special case, walk directly to point. VerifyAndAddLocation(obj, circleCtr, center, Flags, Double.MaxValue, context, caller, float.NaN); return Results; } var maxScore = Math.Max(DesiredProximity - MinProximity, MaxProximity - DesiredProximity) + (LotTilePos.Distance(obj.Position, caller.Position)+MaxProximity)/3 + 2; var ignoreRooms = (Flags & SLOTFlags.IgnoreRooms) > 0; var resolutionBound = (MaxProximity / Slot.Resolution) * Slot.Resolution; for (int x = -resolutionBound; x <= resolutionBound; x += Slot.Resolution) { for (int y = -resolutionBound; y <= resolutionBound; y += Slot.Resolution) { var pos = new Vector2(circleCtr.X + x / 16.0f, circleCtr.Y + y / 16.0f); double distance = Math.Sqrt(x * x + y * y); if (distance >= MinProximity - 0.01 && distance <= MaxProximity + 0.01 && (ignoreRooms || context.VM.Context.GetRoomAt(new LotTilePos((short)Math.Round(pos.X * 16), (short)Math.Round(pos.Y * 16), obj.Position.Level)) == room)) //slot is within proximity { var routeEntryFlags = (GetSearchDirection(circleCtr, pos, obj.RadianDirection) & Flags); //the route needs to know what conditions it fulfilled if (routeEntryFlags > 0) //within search location { double baseScore = ((maxScore - Math.Abs(DesiredProximity - distance)) + context.VM.Context.NextRandom(1024) / 1024.0f); VerifyAndAddLocation(obj, pos, center, routeEntryFlags, baseScore, context, caller, float.NaN); } } } } } /** Sort by how close they are to desired proximity **/ if (Results.Count > 1) Results = Results.OrderBy(x => -x.Score).ToList(); //avoid sort because it acts incredibly unusually if (Results.Count > 0) FailCode = VMRouteFailCode.Success; return Results; }
public void SoftFail(VMRouteFailCode code, VMEntity blocker) { var found = VMRouteFailCode.NoValidGoals; while (found != VMRouteFailCode.Success && Choices != null && Choices.Count > 0) { found = AttemptRoute(Choices[0]); Choices.RemoveAt(0); } if (found != VMRouteFailCode.Success) HardFail(code, blocker); }
private void HardFail(VMRouteFailCode code, VMEntity blocker) { State = VMRoutingFrameState.FAILED; var avatar = (VMAvatar)Caller; if (CallFailureTrees) { avatar.SetPersonData(VMPersonDataVariable.Priority, 100); //TODO: what is this meant to be? what dictates it? //probably has to do with interaction priority. //we just set it to 100 here so that failure trees work. var bhav = Global.Resource.Get<BHAV>(ROUTE_FAIL_TREE); Thread.ExecuteSubRoutine(this, bhav, CodeOwner, new VMSubRoutineOperand(new short[] { (short)code, (blocker==null)?(short)0:blocker.ObjectID, 0, 0 })); } avatar.SetPersonData(VMPersonDataVariable.RouteResult, (short)code); }