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 VMPlacementResult ChangePosition(LotTilePos pos, Direction direction, VMContext context, VMPlaceRequestFlags flags) { if (pos.Level > context.Architecture.Stories) { return(new VMPlacementResult(VMPlacementError.NotAllowedOnFloor)); } if (Objects.Count == 0) { return(new VMPlacementResult(VMPlacementError.Success)); } var count = Objects.Count; VMEntity[] OldContainers = new VMEntity[count]; short[] OldSlotNum = new short[count]; bool[] RoomChange = new bool[count]; LotTilePos[] Targets = new LotTilePos[count]; for (int i = 0; i < count; i++) { OldContainers[i] = Objects[i].Container; OldSlotNum[i] = Objects[i].ContainerSlot; } int Dir = 0; switch (direction) { case Direction.NORTH: Dir = 0; break; case Direction.EAST: Dir = 2; break; case Direction.SOUTH: Dir = 4; break; case Direction.WEST: Dir = 6; break; } Matrix rotMat = Matrix.CreateRotationZ((float)(Dir * Math.PI / 4.0)); VMPlacementResult[] places = new VMPlacementResult[count]; var bObj = BaseObject; var bOff = Offsets[Objects.IndexOf(BaseObject)]; var leadOff = new Vector3(bOff.x, bOff.y, 0); var offTotal = new Vector3(); if (pos != LotTilePos.OUT_OF_WORLD) { for (int i = 0; i < count; i++) { var sub = Objects[i]; var off = new Vector3(Offsets[i].x, Offsets[i].y, 0); off = Vector3.Transform(off - leadOff, rotMat); offTotal += off; var offPos = new LotTilePos((short)Math.Round(pos.x + off.X), (short)Math.Round(pos.y + off.Y), (sbyte)(pos.Level + Offsets[i].Level)); Targets[i] = offPos; var roomChange = context.GetRoomAt(offPos) != context.GetObjectRoom(sub) || OldContainers[i] != null; RoomChange[i] = roomChange; sub.PrePositionChange(context, roomChange); places[i] = sub.PositionValid(offPos, direction, context, flags); if (places[i].Status != VMPlacementError.Success) { //go back to where we started: we're no longer out of world. for (int j = 0; j <= i; j++) { //need to restore slot we were in if (j < OldContainers.Length && OldContainers[j] != null) { OldContainers[j].PlaceInSlot(Objects[j], OldSlotNum[j], false, context); } Objects[j].PositionChange(context, false, RoomChange[j]); } return(places[i]); } else if (places[i].Object != null && !sub.StaticFootprint && !roomChange) { //edge case: we will be placed in a slot, but have a dynamic footprint still in the room. //when placed in a slot, our collision footprint should become null. //Static footprints are deleted before every move, but dynamic footprints are only removed when there is a room change. //if the flag was not set, then we still need to remove the dynamic footprint for this object entering a slot. sub.Footprint?.Unregister(); } } } else { for (int i = 0; i < count; i++) { Objects[i].PrePositionChange(context, true); RoomChange[i] = true; var off = new Vector3(Offsets[i].x, Offsets[i].y, 0); off = Vector3.Transform(off - leadOff, rotMat); offTotal += off; } } offTotal /= count; //this is now the average offset //verification success Matrix?groundAlign = null; //ground align if (VM.UseWorld && pos != LotTilePos.OUT_OF_WORLD && false) //wip ground alignment for carpool. currently disabled! { //find alignment var bp = context.Blueprint; var spos = (new Vector3(pos.x, pos.y, 0) + offTotal) / 16; var tBase = new Vector3(spos.X, bp.InterpAltitude(spos), spos.Y); var ty = new Vector3(spos.X, bp.InterpAltitude(spos + new Vector3(0, 1 / 32f, 0)), spos.Y + 1 / 32f); var tx = new Vector3(spos.X + 1 / 32f, bp.InterpAltitude(spos + new Vector3(1 / 32f, 0, 0)), spos.Y); var front = ty - tBase; front.Normalize(); var side = tx - tBase; side.Normalize(); var top = Vector3.Cross(front, side); groundAlign = Matrix.CreateLookAt(Vector3.Zero, new Vector3(front.X, front.Y, front.Z), top); //more direct coordinate creation - messes up with diagonally sloped tiles but could be useful for something else //groundAlign = new Matrix(new Vector4(side, 0), new Vector4(top, 0), new Vector4(front, 0), new Vector4(0, 0, 0, 1)); if (pos != LotTilePos.OUT_OF_WORLD) { } } for (int i = 0; i < count; i++) { var sub = Objects[i]; var offPos = (pos == LotTilePos.OUT_OF_WORLD) ? LotTilePos.OUT_OF_WORLD : Targets[i]; if (VM.UseWorld) { if (count > 0) { sub.WorldUI.MTOffset = Vector3.Transform(new Vector3(Offsets[i].x, Offsets[i].y, 0) - leadOff, rotMat) - offTotal; } if (groundAlign != null) { var mt = sub.WorldUI.MTOffset; mt = new Vector3(mt.X / 16, mt.Z, mt.Y / 16); mt = Vector3.Transform(mt, groundAlign.Value); mt = new Vector3(mt.X, mt.Z, mt.Y) * 16; sub.WorldUI.MTOffset = mt; } sub.WorldUI.GroundAlign = groundAlign; } sub.SetIndivPosition(offPos, direction, context, places[i]); } if (groundAlign != null) { //retarget floor var ctr = new Vector3(Offsets[0].x, Offsets[0].y, 0) - leadOff - offTotal; for (int i = 0; i < count; i++) { var sub = Objects[i]; var mo = Vector3.Transform(new Vector3(Offsets[i].x, Offsets[i].y, 0) - leadOff, rotMat) - offTotal; var mo2 = sub.WorldUI.MTOffset; if (mo != mo2) { } mo2.Z = 0; sub.VisualPosition = new Vector3(sub.VisualPosition.X, sub.VisualPosition.Y, 0) + (mo2 - mo) / 16; } } for (int i = 0; i < count; i++) { Objects[i].PositionChange(context, false, RoomChange[i]); } return(new VMPlacementResult(VMPlacementError.Success)); }