private Point RectIntersect(VMObstacle r1, VMObstacle r2, Point destPoint) { bool vert = true; int d1, d2, p = 0; if (r1.x1 == r2.x2) { vert = false; p = r1.x1; } if (r1.x2 == r2.x1) { vert = false; p = r1.x2; } if (r1.y1 == r2.y2) { vert = true; p = r1.y1; } if (r1.y2 == r2.y1) { vert = true; p = r1.y2; } if (vert) { d1 = Math.Max(r1.x1, r2.x1); d2 = Math.Min(r1.x2, r2.x2); return(new Point(Math.Max(d1, Math.Min(d2, destPoint.X)), p)); } else { d1 = Math.Max(r1.y1, r2.y1); d2 = Math.Min(r1.y2, r2.y2); return(new Point(p, Math.Max(d1, Math.Min(d2, destPoint.Y)))); } }
public virtual void PositionChange(VMContext context,bool noEntryPoint) { Footprint = GetObstacle(Position,Direction); if (!(GhostImage || noEntryPoint)) { ExecuteEntryPoint(9,context,true); //Placement } }
public void SetFlag(VMEntityFlags flag,bool set) { if (set) { ObjectData[(int)VMStackObjectVariable.Flags] |= (short)(flag); } else { ObjectData[(int)VMStackObjectVariable.Flags] &= ((short)~(flag)); } if (flag == VMEntityFlags.HasZeroExtent) { Footprint = GetObstacle(Position,Direction); } return; }
public VMPlacementResult GetAvatarPlace(VMEntity target, LotTilePos pos, Direction dir) { //avatars cannot be placed in slots under any circumstances, so we skip a few steps. VMObstacle footprint = target.GetObstacle(pos, dir); ushort room = GetRoomAt(pos); VMPlacementError status = VMPlacementError.Success; VMEntity statusObj = null; if (footprint == null || pos.Level < 1) { return(new VMPlacementResult(status)); } var objs = RoomInfo[room].Entities; foreach (var obj in objs) { if (obj.MultitileGroup == target.MultitileGroup) { continue; } var oFoot = obj.Footprint; if (oFoot != null && oFoot.Intersects(footprint) && (!(target.ExecuteEntryPoint(5, this, true, obj, new short[] { obj.ObjectID, 0, 0, 0 }) || obj.ExecuteEntryPoint(5, this, true, target, new short[] { target.ObjectID, 0, 0, 0 }))) ) { var flags = (VMEntityFlags)obj.GetValue(VMStackObjectVariable.Flags); bool allowAvatars = ((flags & VMEntityFlags.DisallowPersonIntersection) == 0) && ((flags & VMEntityFlags.AllowPersonIntersection) > 0); if (!allowAvatars) { status = VMPlacementError.CantIntersectOtherObjects; statusObj = obj; if (obj.EntryPoints[26].ActionFunction != 0) { break; //select chairs immediately. } } } } return(new VMPlacementResult(status, statusObj)); }
public List <VMObstacle> GenerateRoomObs(ushort room, Rectangle bounds) { var result = new List <VMObstacle>(); var x1 = Math.Max(0, bounds.X - 1); var x2 = Math.Min(Width, bounds.Right + 1); var y1 = Math.Max(0, bounds.Y - 1); var y2 = Math.Min(Height, bounds.Bottom + 1); for (int y = y1; y < y2; y++) { VMObstacle next = null; for (int x = x1; x < x2; x++) { int tRoom = (ushort)Map[x + y * Width]; if (tRoom != room) { if (next != null) { next.x2 += 16; } else { next = new VMObstacle((x << 4) - 3, (y << 4) - 3, (x << 4) + 19, (y << 4) + 19); result.Add(next); } } else { if (next != null) { next = null; } } } } return(result); }
public VMPlacementResult GetObjPlace(VMEntity target, LotTilePos pos, Direction dir) { //ok, this might be confusing... short allowedHeights = target.GetValue(VMStackObjectVariable.AllowedHeightFlags); short weight = target.GetValue(VMStackObjectVariable.Weight); bool noFloor = (allowedHeights & 1) == 0; var flags = (VMEntityFlags)target.GetValue(VMStackObjectVariable.Flags); bool allowAvatars = ((flags & VMEntityFlags.DisallowPersonIntersection) == 0) && ((flags & VMEntityFlags.AllowPersonIntersection) > 0); VMObstacle footprint = target.GetObstacle(pos, dir); ushort room = GetRoomAt(pos); VMPlacementError status = (noFloor)?VMPlacementError.HeightNotAllowed:VMPlacementError.Success; VMEntity statusObj = null; if (footprint == null || pos.Level < 1) { return(new VMPlacementResult { Status = status }); } var objs = RoomInfo[room].Entities; foreach (var obj in objs) { if (obj.MultitileGroup == target.MultitileGroup || (obj is VMAvatar && allowAvatars) || (target.GhostImage && target.GhostOriginal != null && target.GhostOriginal.Objects.Contains(obj))) { continue; } var oFoot = obj.Footprint; if (oFoot != null && oFoot.Intersects(footprint) && (!(target.ExecuteEntryPoint(5, this, true, obj, new short[] { obj.ObjectID, 0, 0, 0 }) || obj.ExecuteEntryPoint(5, this, true, target, new short[] { target.ObjectID, 0, 0, 0 }))) ) { statusObj = obj; status = VMPlacementError.CantIntersectOtherObjects; //this object is technically solid. Check if we can place on top of it if (allowedHeights > 1 && obj.TotalSlots() > 0 && (obj.GetSlot(0) == null || obj.GetSlot(0) == target)) { //first check if we have a slot 0, which is what we place onto. then check if it's empty, //then check if the object can support this one's weight. //we also need to make sure that the height of this specific slot is allowed. if (((1 << (obj.GetSlotHeight(0) - 1)) & allowedHeights) > 0) { if (weight < obj.GetValue(VMStackObjectVariable.SupportStrength)) { return(new VMPlacementResult(VMPlacementError.Success, obj)); } else { status = VMPlacementError.CantSupportWeight; } } else { if (noFloor) { if ((allowedHeights & (1 << 3)) > 0) { status = VMPlacementError.CounterHeight; } else { status = (obj.GetSlotHeight(0) == 8) ? VMPlacementError.CannotPlaceComputerOnEndTable : VMPlacementError.HeightNotAllowed; } } } } } } return(new VMPlacementResult(status, statusObj)); }
public List <VMObstacle> GenerateRoomObs(ushort room, sbyte level, Rectangle bounds, VMContext context) { var result = new List <VMObstacle>(); if (room == 0) { bounds = new Rectangle(1, 1, Width - 2, Height - 2); } var x1 = Math.Max(0, bounds.X - 1); var x2 = Math.Min(Width, bounds.Right + 1); var y1 = Math.Max(0, bounds.Y - 1); var y2 = Math.Min(Height, bounds.Bottom + 1); for (int y = y1; y < y2; y++) { VMObstacle next = null; for (int x = x1; x < x2; x++) { uint tRoom = Map[x + y * Width]; if ((ushort)tRoom != room && ((tRoom >> 16) & 0x7FFF) != room) { //is there a door on this tile? var door = (context.ObjectQueries.GetObjectsAt(LotTilePos.FromBigTile((short)x, (short)y, level))?.FirstOrDefault( o => ((VMEntityFlags2)(o.GetValue(VMStackObjectVariable.FlagField2)) & VMEntityFlags2.ArchitectualDoor) > 0) ); if (door != null) { //ok... is is a portal to this room? block all sides that are not a portal to this room var otherSide = door.MultitileGroup.Objects.FirstOrDefault(o => context.GetObjectRoom(o) == room && o.EntryPoints[15].ActionFunction != 0); if (otherSide != null) { //make a hole for this door if (next != null) { next = null; } // note: the sims 1 stops here. this creates issues where sims can walk through doors in some circumstance // eg. two doors back to back into the same room. The sim will not perform a room route to the middle room, they will just walk through the door. // like, through it. This also works for pools but some additional rules prevent you from doing anything too silly. // we want to create 1 unit thick walls blocking each non-portal side. // todo: fix for this continue; } } if (next != null) { next.x2 += 16; } else { next = new VMObstacle((x << 4) - 3, (y << 4) - 3, (x << 4) + 19, (y << 4) + 19); result.Add(next); } } else { if (next != null) { next = null; } } } } OptimizeObstacles(result); return(result); }
public VMExtendRegion(int a, int b, VMObstacle rect) { this.a = a; this.b = b; this.rect = rect; }
// STATIC private static void GenerateBezierControl(VMIPathSegment fromSegI, VMIPathSegment toSegI, VMWalkableRect from, VMWalkableRect to) { //find average direction line var fromSlice = new VMObstacle(from.x1 * 0x8000, from.y1 * 0x8000, from.x2 * 0x8000, from.y2 * 0x8000); //new VMObstacle(fromSegI.Source, fromSegI.Destination); var toSlice = new VMObstacle(to.x1 * 0x8000, to.y1 * 0x8000, to.x2 * 0x8000, to.y2 * 0x8000); //new VMObstacle(toSegI.Source, toSegI.Destination); if (fromSegI is VMPathBezierSegment) { var fromSeg = (VMPathBezierSegment)fromSegI; if (toSegI is VMPathBezierSegment) { var toSeg = (VMPathBezierSegment)toSegI; var fromDiff = (fromSeg.D - fromSeg.A).ToVector2(); var toDiff = (toSeg.D - toSeg.A).ToVector2(); //essentially normalizing, but keeping the lengths stored var fromLength = fromDiff.Length(); var toLength = toDiff.Length(); fromDiff /= fromLength; toDiff /= toLength; //our control points should create a line with average direction between its bordering lines. var avg = fromDiff + toDiff; avg.Normalize(); var controlStrength = Math.Min(fromLength, toLength) / 2; toSeg.B = toSeg.A + (avg * toLength / 3).ToPoint(); fromSeg.C = fromSeg.D - (avg * fromLength / 3).ToPoint(); int changed = 2; bool testTo = true; int emergency = 0; while (emergency++ < 1000 && changed-- > 0) { if (testTo) { //make sure to is within rect bounds var n = toSlice.Closest(toSeg.B.X, toSeg.B.Y); if (n != toSeg.B) { //if we changed, we'll need to check the other side again. changed = 1; //multiply the other side by the change factors here var diff = (toSeg.B - toSeg.A); var diff2 = (n - toSeg.A); var otherDiff = (fromSeg.C - fromSeg.D); if (diff.X != 0) { otherDiff.X = (int)(((long)otherDiff.X * diff2.X) / diff.X); } if (diff.Y != 0) { otherDiff.Y = (int)(((long)otherDiff.Y * diff2.Y) / diff.Y); } toSeg.B = n; fromSeg.C = fromSeg.D + otherDiff; } } else { //make sure from is within rect bounds var n = fromSlice.Closest(fromSeg.C.X, fromSeg.C.Y); if (n != fromSeg.C) { //if we changed, we'll need to check the other side again. changed = 1; //multiply the other side by the change factors here var diff = (fromSeg.C - fromSeg.D); var diff2 = (n - fromSeg.D); var otherDiff = (toSeg.B - toSeg.A); if (diff.X != 0) { otherDiff.X = (int)(((long)otherDiff.X * diff2.X) / diff.X); } if (diff.Y != 0) { otherDiff.Y = (int)(((long)otherDiff.Y * diff2.Y) / diff.Y); } fromSeg.C = n; toSeg.B = toSeg.A + otherDiff; } } testTo = !testTo; } } else { var fromDiff = (fromSeg.D - fromSeg.A).ToVector2(); var toDiff = (toSegI.Destination - toSegI.Source).ToVector2(); //essentially normalizing, but keeping the lengths stored var fromLength = fromDiff.Length(); var toLength = toDiff.Length(); fromDiff /= fromLength; toDiff /= toLength; //our control points should create a line with average direction between its bordering lines. fromSeg.C = fromSeg.D - (toDiff * fromLength / 3).ToPoint(); fromSeg.C = fromSlice.Closest(fromSeg.C.X, fromSeg.C.Y); } } else if (toSegI is VMPathBezierSegment) { var toSeg = (VMPathBezierSegment)toSegI; var fromDiff = (fromSegI.Destination - fromSegI.Source).ToVector2(); var toDiff = (toSeg.D - toSeg.A).ToVector2(); //essentially normalizing, but keeping the lengths stored var fromLength = fromDiff.Length(); var toLength = toDiff.Length(); fromDiff /= fromLength; toDiff /= toLength; //our control points should create a line with average direction between its bordering lines. toSeg.B = toSeg.A + (fromDiff * toLength / 3).ToPoint(); toSeg.B = toSlice.Closest(toSeg.B.X, toSeg.B.Y); } }
private List <VMObstacle> EdgeSet(VMObstacle search) { return(TreeMap.OnEdge(search)); }
private List <VMObstacle> IntersectSet(VMObstacle search) { return(TreeMap.AllIntersect(search)); }
private VMExtendRectResult ExtendRect(int dir, int d1, int d2, int p) { int bestN = ((dir + 1) % 4 < 2) ? int.MinValue : int.MaxValue; var best = new List <VMExtendRegion>(); VMObstacle extendRange; switch (dir) { case 0: //top extendRange = new VMObstacle(d1, int.MinValue, d2, p); foreach (VMObstacle r in IntersectSet(extendRange)) { if (r.y2 > p) { continue; //bottom of rect lower than start point = no hit } if (r.x1 >= d2 || r.x2 <= d1) { continue; //does not intersect } if (r.y2 > bestN) { bestN = r.y2; best.Clear(); best.Add(new VMExtendRegion(r.x1, r.x2, r)); } else if (r.y2 == bestN) { best.Add(new VMExtendRegion(r.x1, r.x2, r)); } } break; case 1: //right extendRange = new VMObstacle(p, d1, int.MaxValue, d2); foreach (VMObstacle r in IntersectSet(extendRange)) { if (r.x1 < p) { continue; //left of rect lefter than start point = no hit } if (r.y1 >= d2 || r.y2 <= d1) { continue; //does not intersect } if (r.x1 < bestN) { bestN = r.x1; best.Clear(); best.Add(new VMExtendRegion(r.y1, r.y2, r)); } else if (r.x1 == bestN) { best.Add(new VMExtendRegion(r.y1, r.y2, r)); } } break; case 2: //bottom extendRange = new VMObstacle(d1, p, d2, int.MaxValue); foreach (VMObstacle r in IntersectSet(extendRange)) { if (r.y1 < p) { continue; //top of rect higher than start point = no hit } if (r.x1 >= d2 || r.x2 <= d1) { continue; //does not intersect } if (r.y1 < bestN) { bestN = r.y1; best.Clear(); best.Add(new VMExtendRegion(r.x1, r.x2, r)); } else if (r.y1 == bestN) { best.Add(new VMExtendRegion(r.x1, r.x2, r)); } } break; case 3: //left extendRange = new VMObstacle(int.MinValue, d1, p, d2); foreach (VMObstacle r in IntersectSet(extendRange)) { if (r.x2 > p) { continue; //right of rect righter than start point = no hit } if (r.y1 >= d2 || r.y2 <= d1) { continue; //does not intersect } if (r.x2 > bestN) { bestN = r.x2; best.Clear(); best.Add(new VMExtendRegion(r.y1, r.y2, r)); } else if (r.x2 == bestN) { best.Add(new VMExtendRegion(r.y1, r.y2, r)); } } break; } return(new VMExtendRectResult { Best = best, BestN = bestN }); }