public static Vector3 ToGlobal(this Position p) { var landblock = LScape.get_landblock(p.LandblockId.Raw); if (landblock.IsDungeon) { return(p.Pos); } var x = p.LandblockId.LandblockX * Position.BlockLength + p.PositionX; var y = p.LandblockId.LandblockY * Position.BlockLength + p.PositionY; var z = p.PositionZ; return(new Vector3(x, y, z)); }
/// <summary> /// Spawns the semi-randomized monsters scattered around the outdoors<para /> /// This will be called from a separate task from our constructor. Use thread safety when interacting with this landblock. /// </summary> private void SpawnEncounters() { // get the encounter spawns for this landblock var encounters = DatabaseManager.World.GetCachedEncountersByLandblock(Id.Landblock); foreach (var encounter in encounters) { var wo = WorldObjectFactory.CreateNewWorldObject(encounter.WeenieClassId); if (wo == null) { continue; } var xPos = Math.Clamp(encounter.CellX * 24.0f, 0.5f, 191.5f); var yPos = Math.Clamp(encounter.CellY * 24.0f, 0.5f, 191.5f); var pos = new Physics.Common.Position(); pos.ObjCellID = (uint)(Id.Landblock << 16) | 1; pos.Frame = new Physics.Animation.AFrame(new Vector3(xPos, yPos, 0), Quaternion.Identity); pos.adjust_to_outside(); pos.Frame.Origin.Z = _landblock.GetZ(pos.Frame.Origin); wo.Location = new Position(pos.ObjCellID, pos.Frame.Origin, pos.Frame.Orientation); var sortCell = LScape.get_landcell(pos.ObjCellID) as SortCell; if (sortCell != null && sortCell.has_building()) { continue; } if (PropertyManager.GetBool("override_encounter_spawn_rates").Item) { wo.RegenerationInterval = PropertyManager.GetDouble("encounter_regen_interval").Item; foreach (var profile in wo.Biota.BiotaPropertiesGenerator) { profile.Delay = (float)PropertyManager.GetDouble("encounter_delay").Item; } } actionQueue.EnqueueAction(new ActionEventDelegate(() => { AddWorldObject(wo); })); } }
/// <summary> /// Handles the cleanup process for a landblock /// This method is called by LandblockManager /// </summary> public void Unload() { var landblockID = Id.Raw | 0xFFFF; log.Debug($"Landblock.Unload({landblockID:X})"); SaveDB(); // remove all objects foreach (var wo in worldObjects.Keys.ToList()) { RemoveWorldObjectInternal(wo); } // remove physics landblock LScape.unload_landblock(landblockID); }
public static Vector3 ToGlobal(this Position p) { var landblock = LScape.get_landblock(p.LandblockId.Raw); // TODO: investigate dungeons that are below actual traversable overworld terrain // ex., 010AFFFF //if (landblock.IsDungeon) if (p.Indoors) { return(p.Pos); } var x = p.LandblockId.LandblockX * Position.BlockLength + p.PositionX; var y = p.LandblockId.LandblockY * Position.BlockLength + p.PositionY; var z = p.PositionZ; return(new Vector3(x, y, z)); }
/// <summary> /// Returns TRUE if outdoor position is located on walkable slope /// </summary> public static bool IsWalkable(this Position p) { if (p.Indoors) { return(true); } var landcell = (LandCell)LScape.get_landcell(p.Cell); Physics.Polygon walkable = null; var terrainPoly = landcell.find_terrain_poly(p.Pos, ref walkable); if (walkable == null) { return(false); } return(Physics.PhysicsObj.is_valid_walkable(walkable.Plane.Normal)); }
public void UpdatePosition_PhysicsInner(Vector3 newPos, Vector3 dir) { Location.Rotate(dir); PhysicsObj.Position.Frame.Orientation = Location.Rotation; var cell = LScape.get_landcell(Location.Cell); PhysicsObj.set_request_pos(newPos, Location.Rotation, cell); // simulate running forward for this amount of time PhysicsObj.update_object_server(false); // was the position successfully moved to? // use the physics position as the source-of-truth? Location.Pos = PhysicsObj.Position.Frame.Origin; if (PhysicsObj.CurCell != null && PhysicsObj.CurCell.ID != Location.Cell) { Location.LandblockId = new LandblockId(PhysicsObj.CurCell.ID); } }
public Landblock(LandblockId id) { Id = id; CellLandblock = DatManager.CellDat.ReadFromDat <CellLandblock>(Id.Raw >> 16 | 0xFFFF); LandblockInfo = DatManager.CellDat.ReadFromDat <LandblockInfo>((uint)Id.Landblock << 16 | 0xFFFE); lastActiveTime = DateTime.UtcNow; Task.Run(() => { _landblock = LScape.get_landblock(Id.Raw); CreateWorldObjects(); SpawnDynamicShardObjects(); SpawnEncounters(); }); //LoadMeshes(objects); }
public static void AdjustMapCoords(this Position pos) { // adjust Z to terrain height pos.PositionZ = pos.GetTerrainZ(); // adjust to building height, if applicable var sortCell = LScape.get_landcell(pos.Cell) as SortCell; if (sortCell != null && sortCell.has_building()) { var building = sortCell.Building; var minZ = building.GetMinZ(); if (minZ > 0 && minZ < float.MaxValue) { pos.PositionZ += minZ; } pos.LandblockId = new LandblockId(pos.GetCell()); } }
public void BuildEnvCells() { EnvCells = new List <R_EnvCell>(); if (Landblock.Info == null || Landblock.Info.NumCells == 0) { return; } var numCells = Landblock.Info.NumCells; var landblockID = Landblock.ID & 0xFFFF0000; for (uint i = 0; i < numCells; i++) { var envCellID = landblockID | (0x100 + i); var envCell = (EnvCell)LScape.get_landcell(envCellID); EnvCells.Add(new R_EnvCell(envCell)); } }
public static float GetTerrainZ(this Position p) { var cellID = GetOutdoorCell(p); var landcell = (LandCell)LScape.get_landcell(cellID); if (landcell == null) { return(p.Pos.Z); } Physics.Polygon walkable = null; if (!landcell.find_terrain_poly(p.Pos, ref walkable)) { return(p.Pos.Z); } Vector3 terrainPos = p.Pos; walkable.Plane.set_height(ref terrainPos); return(terrainPos.Z); }
/// <summary> /// Spawns the semi-randomized monsters scattered around the outdoors<para /> /// This will be called from a separate task from our constructor. Use thread safety when interacting with this landblock. /// </summary> private void SpawnEncounters() { // get the encounter spawns for this landblock var encounters = DatabaseManager.World.GetCachedEncountersByLandblock(Id.Landblock); foreach (var encounter in encounters) { var wo = WorldObjectFactory.CreateNewWorldObject(encounter.WeenieClassId); if (wo == null) { continue; } var xPos = Math.Clamp(encounter.CellX * 24.0f, 0.5f, 191.5f); var yPos = Math.Clamp(encounter.CellY * 24.0f, 0.5f, 191.5f); var pos = new Physics.Common.Position(); pos.ObjCellID = (uint)(Id.Landblock << 16) | 1; pos.Frame = new Physics.Animation.AFrame(new Vector3(xPos, yPos, 0), Quaternion.Identity); pos.adjust_to_outside(); pos.Frame.Origin.Z = _landblock.GetZ(pos.Frame.Origin); wo.Location = new Position(pos.ObjCellID, pos.Frame.Origin, pos.Frame.Orientation); var sortCell = LScape.get_landcell(pos.ObjCellID) as SortCell; if (sortCell != null && sortCell.has_building()) { continue; } actionQueue.EnqueueAction(new ActionEventDelegate(() => { AddWorldObject(wo); })); } }
private void AddWorldObjectInternal(WorldObject wo) { Log($"adding {wo.Guid.Full:X}"); if (!worldObjects.ContainsKey(wo.Guid)) { worldObjects[wo.Guid] = wo; } wo.SetParent(this); wo.PhysicsObj.Position.Frame.Origin = wo.Location.Pos; wo.PhysicsObj.Position.Frame.Orientation = wo.Location.Rotation; //wo.AdjustDungeonCells(wo.Location); var cell = LScape.get_landcell(wo.Location.Cell); if (cell != null) { wo.PhysicsObj.enter_cell(cell); wo.PhysicsObj.add_shadows_to_cell(cell); } // var args = BroadcastEventArgs.CreateAction(BroadcastAction.AddOrUpdate, wo); // Broadcast(args, true, Quadrant.All); // Alert all nearby players of the object EnqueueActionBroadcast(wo.Location, MaxObjectRange, (Player p) => p.TrackObject(wo)); // if this is a player, tell them about everything else we have in range of them. if (wo is Player) { List <WorldObject> wolist = null; wolist = GetWorldObjectsInRange(wo, MaxObjectRange); AddPlayerTracking(wolist, ((Player)wo)); } }
public Landblock(LandblockId id) { Id = id; UpdateStatus(LandBlockStatusFlag.IdleUnloaded); // initialize adjacency array adjacencies.Add(Adjacency.North, null); adjacencies.Add(Adjacency.NorthEast, null); adjacencies.Add(Adjacency.East, null); adjacencies.Add(Adjacency.SouthEast, null); adjacencies.Add(Adjacency.South, null); adjacencies.Add(Adjacency.SouthWest, null); adjacencies.Add(Adjacency.West, null); adjacencies.Add(Adjacency.NorthWest, null); UpdateStatus(LandBlockStatusFlag.IdleLoading); actionQueue = new NestedActionQueue(WorldManager.ActionQueue); var objects = DatabaseManager.World.GetCachedInstancesByLandblock(Id.Landblock); // Instances var factoryObjects = WorldObjectFactory.CreateNewWorldObjects(objects); factoryObjects.ForEach(fo => { AddWorldObject(fo); fo.ActivateLinks(); }); _landblock = LScape.get_landblock(Id.Raw); // Many thanks for GDL cache and GDL coding for loading into landblock and gmriggs assistance with taking the byte arrays and turning them in to more easy to follow (for me) data structures var encounters = DatabaseManager.World.GetCachedEncountersByLandblock(Id.Landblock); encounters.ForEach(encounter => { var wo = WorldObjectFactory.CreateNewWorldObject(encounter.WeenieClassId); if (wo != null) { float x_shift = 24.0f * encounter.CellX; float y_shift = 24.0f * encounter.CellY; var pos = new Physics.Common.Position(); pos.ObjCellID = (uint)(id.Landblock << 16) | 1; pos.Frame = new Physics.Animation.AFrame(new Vector3(x_shift, y_shift, 0), new Quaternion(0, 0, 0, 1)); pos.adjust_to_outside(); pos.Frame.Origin.Z = _landblock.GetZ(pos.Frame.Origin); wo.Location = new Position(pos.ObjCellID, pos.Frame.Origin.X, pos.Frame.Origin.Y, pos.Frame.Origin.Z, pos.Frame.Orientation.X, pos.Frame.Orientation.Y, pos.Frame.Orientation.Z, pos.Frame.Orientation.W); if (!worldObjects.ContainsKey(wo.Guid)) { AddWorldObject(wo); } } }); //LoadMeshes(objects); UpdateStatus(LandBlockStatusFlag.IdleLoaded); // FIXME(ddevec): Goal: get rid of UseTime() function... actionQueue.EnqueueAction(new ActionEventDelegate(() => UseTimeWrapper())); motionQueue = new NestedActionQueue(WorldManager.MotionQueue); }
public async void LoadLandblocks(Vector2 startBlock, Vector2 endBlock) { //Console.WriteLine($"LoadLandblocks({startBlock}, {endBlock})"); if (PlayerMode) { ExitPlayerMode(); InitPlayerMode = true; } Render.Buffer.ClearBuffer(); Server.Init(); TextureCache.Init(); LScape.unload_landblocks_all(); DungeonMode = false; var dx = (int)(endBlock.X - startBlock.X) + 1; var dy = (int)(startBlock.Y - endBlock.Y) + 1; var numBlocks = dx * dy; var size = endBlock - startBlock; var center = startBlock + size / 2; var centerX = (uint)Math.Round(center.X); var centerY = (uint)Math.Round(center.Y); var landblockID = centerX << 24 | centerY << 16 | 0xFFFF; MainWindow.Status.WriteLine($"Loading {numBlocks} landblocks"); await Task.Delay(1); //Landblocks = new Dictionary<uint, R_Landblock>(); R_Landblock r_landblock = null; R_Landblock centerBlock = null; for (var lbx = (uint)startBlock.X; lbx <= endBlock.X; lbx++) { if (lbx < 0 || lbx > 254) { continue; } for (var lby = (uint)endBlock.Y; lby <= startBlock.Y; lby++) { if (lby < 0 || lby > 254) { continue; } var lbid = lbx << 24 | lby << 16 | 0xFFFF; var timer = Stopwatch.StartNew(); var landblock = LScape.get_landblock(lbid); timer.Stop(); r_landblock = new R_Landblock(landblock); //LScape.unload_landblock(lbid); if (lbid == landblockID) { centerBlock = r_landblock; } MainWindow.Status.WriteLine($"Loaded {lbid:X8} in {timer.Elapsed.TotalMilliseconds}ms"); //Landblocks.Add(lbid, new R_Landblock(landblock)); } } Render.Buffer.BuildBuffers(); Render.InitEmitters(); Camera.InitLandblock(centerBlock); GameView.ViewMode = ViewMode.World; SingleBlock = uint.MaxValue; FreeResources(); Server.OnLoadWorld(); }
/// <summary> /// Used by physics engine to actually update a player position /// Automatically notifies clients of updated position /// </summary> /// <param name="newPosition">The new position being requested, before verification through physics engine</param> /// <returns>TRUE if object moves to a different landblock</returns> public bool UpdatePlayerPhysics(ACE.Entity.Position newPosition, bool forceUpdate = false) { //Console.WriteLine($"{Name}.UpdatePlayerPhysics({newPosition}, {forceUpdate}, {Teleporting})"); // possible bug: while teleporting, client can still send AutoPos packets from old landblock if (Teleporting && !forceUpdate) { return(false); } // pre-validate movement if (!ValidateMovement(newPosition)) { log.Error($"{Name}.UpdatePlayerPhysics() - movement pre-validation failed from {Location} to {newPosition}"); return(false); } try { if (!forceUpdate) // This is needed beacuse this function might be called recursively { stopwatch.Restart(); } var success = true; if (PhysicsObj != null) { var distSq = Location.SquaredDistanceTo(newPosition); if (distSq > PhysicsGlobals.EpsilonSq) { var curCell = LScape.get_landcell(newPosition.Cell); if (curCell != null) { //if (PhysicsObj.CurCell == null || curCell.ID != PhysicsObj.CurCell.ID) //PhysicsObj.change_cell_server(curCell); PhysicsObj.set_request_pos(newPosition.Pos, newPosition.Rotation, curCell, Location.LandblockId.Raw); success = PhysicsObj.update_object_server(); if (PhysicsObj.CurCell == null && curCell.ID >> 16 != 0x18A) { PhysicsObj.CurCell = curCell; } CheckMonsters(); } } } // double update path: landblock physics update -> updateplayerphysics() -> update_object_server() -> Teleport() -> updateplayerphysics() -> return to end of original branch if (Teleporting && !forceUpdate) { return(true); } if (!success) { return(false); } var landblockUpdate = Location.Cell >> 16 != newPosition.Cell >> 16; Location = newPosition; SendUpdatePosition(); if (!InUpdate) { LandblockManager.RelocateObjectForPhysics(this, true); } return(landblockUpdate); } finally { if (!forceUpdate) // This is needed beacuse this function might be called recursively { var elapsedSeconds = stopwatch.Elapsed.TotalSeconds; ServerPerformanceMonitor.AddToCumulativeEvent(ServerPerformanceMonitor.CumulativeEventHistoryType.WorldObject_Tick_UpdatePlayerPhysics, elapsedSeconds); if (elapsedSeconds >= 1) // Yea, that ain't good.... { log.Warn($"[PERFORMANCE][PHYSICS] {Guid}:{Name} took {(elapsedSeconds * 1000):N1} ms to process UpdatePlayerPhysics() at loc: {Location}"); } else if (elapsedSeconds >= 0.010) { log.Debug($"[PERFORMANCE][PHYSICS] {Guid}:{Name} took {(elapsedSeconds * 1000):N1} ms to process UpdatePlayerPhysics() at loc: {Location}"); } } } }
public void LoadModel(uint wcid) { Server.Initting = true; Buffer.ClearBuffer(); TextureCache.Init(); LScape.unload_landblocks_all(); Console.WriteLine($"Loading {wcid}"); GfxObjMode = false; var wo = WorldObjectFactory.CreateNewWorldObject(wcid); if (wo == null) { return; } wo.InitPhysicsObj(); var location = new Position(); location.ObjCellID = 0x00000001; location.Frame.Origin = new System.Numerics.Vector3(12, 12, 0); var success = wo.PhysicsObj.enter_world(location); if (!success || wo.PhysicsObj.CurCell == null) { wo.PhysicsObj.DestroyObject(); wo.PhysicsObj = null; Console.WriteLine($"WorldObjectViewer.LoadModel({wcid}).AddPhysicsObj({wo.Name}, {location}) - failed to spawn"); return; } Console.WriteLine($"Spawned {wcid} - {wo.Name} @ {location}"); var objDesc = new ObjDesc(wo.SetupTableId, wo.ClothingBase ?? 0, (PaletteTemplate)(wo.PaletteTemplate ?? 0), (float)(wo.Shade ?? 0.0)); if (wo is Creature creature) { objDesc.AddBaseModelData(wo); var equippedObjects = creature.EquippedObjects.Values.OrderBy(i => (int)(i.ClothingPriority ?? 0)).ToList(); foreach (var equippedObject in equippedObjects) { if ((equippedObject.CurrentWieldedLocation & EquipMask.Selectable) != 0) { continue; } objDesc.Add(equippedObject.ClothingBase ?? 0, (PaletteTemplate)(equippedObject.PaletteTemplate ?? 0), (float)(equippedObject.Shade ?? 0.0)); } } wo.PhysicsObj.UpdateObjDesc(objDesc); var r_PhysicsObj = new R_PhysicsObj(wo.PhysicsObj); Buffer.AddInstance(r_PhysicsObj, objDesc); if (!LoadedOnce) { //Camera.Position = Vector3.Zero; //Camera.Dir = Vector3.Normalize(new Vector3(1, 1, 0)); Camera.Position = new Vector3(11.782367f, 12.763985f, 1.6514041f); Camera.Dir = new Vector3(0.30761153f, -0.94673103f, 0.093334414f); Camera.Up = Vector3.UnitZ; Camera.CreateLookAt(); Camera.Speed = Camera.Model_Speed; } Buffer.BuildTextureAtlases(Buffer.InstanceTextureAtlasChains); Buffer.BuildBuffer(Buffer.RB_Instances); Server.InstancesLoaded = true; Server.Initting = false; LoadedOnce = true; }
public void LoadLandblock(uint landblockID, uint radius = 1) { if (PlayerMode) { ExitPlayerMode(); InitPlayerMode = true; } Render.Buffer.ClearBuffer(); Server.Init(); TextureCache.Init(); LScape.unload_landblocks_all(); var landblock = LScape.get_landblock(landblockID); if (landblock.HasDungeon) { radius = 0; } DungeonMode = landblock.HasDungeon; var center_lbx = landblockID >> 24; var center_lby = landblockID >> 16 & 0xFF; //Landblocks = new Dictionary<uint, R_Landblock>(); R_Landblock r_landblock = null; R_Landblock centerBlock = null; for (var lbx = (int)(center_lbx - radius); lbx <= center_lbx + radius; lbx++) { if (lbx < 0 || lbx > 254) { continue; } for (var lby = (int)(center_lby - radius); lby <= center_lby + radius; lby++) { if (lby < 0 || lby > 254) { continue; } var lbid = (uint)(lbx << 24 | lby << 16 | 0xFFFF); var timer = Stopwatch.StartNew(); landblock = LScape.get_landblock(lbid); timer.Stop(); r_landblock = new R_Landblock(landblock); //LScape.unload_landblock(lbid); if (landblockID == lbid) { centerBlock = r_landblock; } MainWindow.Status.WriteLine($"Loaded {lbid:X8} in {timer.Elapsed.TotalMilliseconds}ms"); //Landblocks.Add(lbid, new R_Landblock(landblock)); } } Render.Buffer.BuildBuffers(); Render.InitEmitters(); if (FileExplorer.Instance.TeleportMode) { var zBump = DungeonMode ? 1.775f : 2.775f; if (InitPlayerMode) { zBump = 0.0f; } Camera.InitTeleport(centerBlock, zBump); FileExplorer.Instance.TeleportMode = false; } else if (DungeonMode) { BoundingBox = new Model.BoundingBox(Render.Buffer.RB_EnvCell); Camera.InitDungeon(r_landblock, BoundingBox); } else { Camera.InitLandblock(r_landblock); } SingleBlock = landblock.ID; FreeResources(); Server.OnLoadWorld(); }
/// <summary> /// Used by physics engine to actually update a player position /// Automatically notifies clients of updated position /// </summary> /// <param name="newPosition">The new position being requested, before verification through physics engine</param> /// <returns>TRUE if object moves to a different landblock</returns> public bool UpdatePlayerPhysics(ACE.Entity.Position newPosition, bool forceUpdate = false) { //Console.WriteLine($"UpdatePlayerPhysics: {newPosition.Cell:X8}, {newPosition.Pos}"); var player = this as Player; // only handles player movement if (player == null) { return(false); } // possible bug: while teleporting, client can still send AutoPos packets from old landblock if (Teleporting && !forceUpdate) { return(false); } if (PhysicsObj != null) { var dist = (newPosition.Pos - PhysicsObj.Position.Frame.Origin).Length(); if (dist > PhysicsGlobals.EPSILON) { var curCell = LScape.get_landcell(newPosition.Cell); if (curCell != null) { //if (PhysicsObj.CurCell == null || curCell.ID != PhysicsObj.CurCell.ID) //PhysicsObj.change_cell_server(curCell); PhysicsObj.set_request_pos(newPosition.Pos, newPosition.Rotation, curCell, Location.LandblockId.Raw); PhysicsObj.update_object_server(); if (PhysicsObj.CurCell == null) { PhysicsObj.CurCell = curCell; } player.CheckMonsters(); if (curCell.ID != prevCell) { //prevCell = curCell.ID; //Console.WriteLine("Player cell: " + curCell.ID.ToString("X8")); //var envCell = curCell as Physics.Common.EnvCell; //var seenOutside = envCell != null ? envCell.SeenOutside : true; //Console.WriteLine($"CurCell: {curCell.ID:X8}, SeenOutside: {seenOutside}"); } } } } // double update path: landblock physics update -> updateplayerphysics() -> update_object_server() -> Teleport() -> updateplayerphysics() -> return to end of original branch if (Teleporting && !forceUpdate) { return(true); } var landblockUpdate = Location.Cell >> 16 != newPosition.Cell >> 16; Location = newPosition; SendUpdatePosition(); if (!InUpdate) { LandblockManager.RelocateObjectForPhysics(this, true); } return(landblockUpdate); }
public void ProcessGeneratorQueue() { var index = 0; while (index < GeneratorQueue.Count) { double ts = Common.Time.GetTimestamp(); if (ts >= GeneratorQueue[index].When) { if (GeneratorRegistry.Count >= MaxGeneratedObjects) { // System.Diagnostics.Debug.WriteLine($"GeneratorRegistry for {Guid.Full} is at MaxGeneratedObjects {MaxGeneratedObjects}"); // System.Diagnostics.Debug.WriteLine($"Removing {GeneratorQueue[index].Slot} from GeneratorQueue for {Guid.Full}"); GeneratorQueue.RemoveAt(index); index++; continue; } var profile = GeneratorProfiles[(int)GeneratorQueue[index].Slot]; var rNode = new GeneratorRegistryNode(); rNode.WeenieClassId = profile.WeenieClassId; rNode.Timestamp = Common.Time.GetTimestamp(); rNode.Slot = GeneratorQueue[index].Slot; var wo = WorldObjectFactory.CreateNewWorldObject(profile.WeenieClassId); if (wo != null) { switch ((RegenLocationType)profile.WhereCreate) { case RegenLocationType.SpecificTreasure: case RegenLocationType.Specific: if ((profile.ObjCellId ?? 0) > 0) { wo.Location = new ACE.Entity.Position(profile.ObjCellId ?? 0, profile.OriginX ?? 0, profile.OriginY ?? 0, profile.OriginZ ?? 0, profile.AnglesX ?? 0, profile.AnglesY ?? 0, profile.AnglesZ ?? 0, profile.AnglesW ?? 0); } else { wo.Location = new ACE.Entity.Position(Location.Cell, Location.PositionX + profile.OriginX ?? 0, Location.PositionY + profile.OriginY ?? 0, Location.PositionZ + profile.OriginZ ?? 0, profile.AnglesX ?? 0, profile.AnglesY ?? 0, profile.AnglesZ ?? 0, profile.AnglesW ?? 0); } break; case RegenLocationType.ScatterTreasure: case RegenLocationType.Scatter: float genRadius = (float)(GetProperty(PropertyFloat.GeneratorRadius) ?? 0f); var random_x = Physics.Common.Random.RollDice(genRadius * -1, genRadius); var random_y = Physics.Common.Random.RollDice(genRadius * -1, genRadius); var pos = new Physics.Common.Position(Location); wo.Location = new ACE.Entity.Position(pos.ObjCellID, pos.Frame.Origin.X, pos.Frame.Origin.Y, pos.Frame.Origin.Z, pos.Frame.Orientation.X, pos.Frame.Orientation.Y, pos.Frame.Orientation.Z, pos.Frame.Orientation.W); var newPos = wo.Location.Pos + new Vector3(random_x, random_y, 0.0f); if (!Location.Indoors) { // Based on GDL scatter newPos.X = Math.Clamp(newPos.X, 0.5f, 191.5f); newPos.Y = Math.Clamp(newPos.Y, 0.5f, 191.5f); wo.Location.SetPosition(newPos); newPos.Z = LScape.get_landblock(wo.Location.Cell).GetZ(newPos); } wo.Location.SetPosition(newPos); break; default: wo.Location = Location; break; } wo.Generator = this; wo.GeneratorId = Guid.Full; // System.Diagnostics.Debug.WriteLine($"Adding {wo.Guid.Full} | {rNode.Slot} in GeneratorRegistry for {Guid.Full}"); GeneratorRegistry.Add(wo.Guid.Full, rNode); GeneratorCache.Add(wo.Guid.Full, wo); // System.Diagnostics.Debug.WriteLine($"Spawning {GeneratorQueue[index].Slot} in GeneratorQueue for {Guid.Full}"); wo.EnterWorld(); // System.Diagnostics.Debug.WriteLine($"Removing {GeneratorQueue[index].Slot} from GeneratorQueue for {Guid.Full}"); GeneratorQueue.RemoveAt(index); } else { // System.Diagnostics.Debug.WriteLine($"Removing {GeneratorQueue[index].Slot} from GeneratorQueue for {Guid.Full} because wcid {rNode.WeenieClassId} is not in the database"); GeneratorQueue.RemoveAt(index); } } else { index++; } } }
/// <summary> /// Used by physics engine to actually update a player position /// Automatically notifies clients of updated position /// </summary> /// <param name="newPosition">The new position being requested, before verification through physics engine</param> /// <returns>TRUE if object moves to a different landblock</returns> public bool UpdatePlayerPosition(ACE.Entity.Position newPosition, bool forceUpdate = false) { //Console.WriteLine($"{Name}.UpdatePlayerPhysics({newPosition}, {forceUpdate}, {Teleporting})"); bool verifyContact = false; // possible bug: while teleporting, client can still send AutoPos packets from old landblock if (Teleporting && !forceUpdate) { return(false); } // pre-validate movement if (!ValidateMovement(newPosition)) { log.Error($"{Name}.UpdatePlayerPosition() - movement pre-validation failed from {Location} to {newPosition}"); return(false); } try { if (!forceUpdate) // This is needed beacuse this function might be called recursively { stopwatch.Restart(); } var success = true; if (PhysicsObj != null) { var distSq = Location.SquaredDistanceTo(newPosition); if (distSq > PhysicsGlobals.EpsilonSq) { /*var p = new Physics.Common.Position(newPosition); * var dist = PhysicsObj.Position.Distance(p); * Console.WriteLine($"Dist: {dist}");*/ if (newPosition.Landblock == 0x18A && Location.Landblock != 0x18A) { log.Info($"{Name} is getting swanky"); } if (!Teleporting) { var blockDist = PhysicsObj.GetBlockDist(Location.ObjCellID, newPosition.ObjCellID); // verify movement if (distSq > MaxSpeedSq && blockDist > 1) { //Session.Network.EnqueueSend(new GameMessageSystemChat("Movement error", ChatMessageType.Broadcast)); log.Warn($"MOVEMENT SPEED: {Name} trying to move from {Location} to {newPosition}, speed: {Math.Sqrt(distSq)}"); return(false); } // verify z-pos if (blockDist == 0 && LastGroundPos != null && newPosition.Pos.Z - LastGroundPos.Pos.Z > 10 && DateTime.UtcNow - LastJumpTime > TimeSpan.FromSeconds(1) && GetCreatureSkill(Skill.Jump).Current < 1000) { verifyContact = true; } } var curCell = LScape.get_landcell(newPosition.LongObjCellID); if (curCell != null) { //if (PhysicsObj.CurCell == null || curCell.ID != PhysicsObj.CurCell.ID) //PhysicsObj.change_cell_server(curCell); PhysicsObj.set_request_pos(newPosition.Pos, newPosition.Rotation, newPosition.Instance, curCell, Location.ObjCellID); if (FastTick) { success = PhysicsObj.update_object_server_new(true, newPosition.Instance); } else { success = PhysicsObj.update_object_server(true, newPosition.Instance); } if (PhysicsObj.CurCell == null && curCell.ID >> 16 != 0x18A) { PhysicsObj.CurCell = curCell; } if (verifyContact && IsJumping) { log.Warn($"z-pos hacking detected for {Name}, lastGroundPos: {LastGroundPos.ToLOCString()} - requestPos: {newPosition.ToLOCString()}"); Location = new ACE.Entity.Position(LastGroundPos); Sequences.GetNextSequence(SequenceType.ObjectForcePosition); SendUpdatePosition(); return(false); } CheckMonsters(); } } else { PhysicsObj.Position.Frame.Orientation = newPosition.Rotation; } } // double update path: landblock physics update -> updateplayerphysics() -> update_object_server() -> Teleport() -> updateplayerphysics() -> return to end of original branch if (Teleporting && !forceUpdate) { return(true); } if (!success) { return(false); } var landblockUpdate = Location.Landblock != newPosition.Landblock; Location = newPosition; if (RecordCast.Enabled) { RecordCast.Log($"CurPos: {Location.ToLOCString()}"); } if (RequestedLocationBroadcast || DateTime.UtcNow - LastUpdatePosition >= MoveToState_UpdatePosition_Threshold) { SendUpdatePosition(); } else { Session.Network.EnqueueSend(new GameMessageUpdatePosition(this)); } if (!InUpdate) { // todo: improve this logic /*if (CurrentLandblock.Instance > 0) * { * ClearInstance(CurrentLandblock.LongId); * }*/ LandblockManager.RelocateObjectForPhysics(this, true); } return(landblockUpdate); } finally { if (!forceUpdate) // This is needed beacuse this function might be called recursively { var elapsedSeconds = stopwatch.Elapsed.TotalSeconds; ServerPerformanceMonitor.AddToCumulativeEvent(ServerPerformanceMonitor.CumulativeEventHistoryType.Player_Tick_UpdateObjectPhysics, elapsedSeconds); if (elapsedSeconds >= 0.100) // Yea, that ain't good.... { log.Warn($"[PERFORMANCE][PHYSICS] {Guid}:{Name} took {(elapsedSeconds * 1000):N1} ms to process UpdatePlayerPosition() at loc: {Location}"); } else if (elapsedSeconds >= 0.010) { log.Debug($"[PERFORMANCE][PHYSICS] {Guid}:{Name} took {(elapsedSeconds * 1000):N1} ms to process UpdatePlayerPosition() at loc: {Location}"); } } } }
/// <summary> /// Gets the cell ID for a position within a landblock /// </summary> public static uint GetCell(this Position p) { var landblock = LScape.get_landblock(p.LongObjCellID); // dungeons // TODO: investigate dungeons that are below actual traversable overworld terrain // ex., 010AFFFF //if (landblock.IsDungeon) if (p.Indoors) { return(GetIndoorCell(p)); } // outside - could be on landscape, in building, or underground cave var cellID = GetOutdoorCell(p); var longcellID = ((ulong)p.Instance << 32) | cellID; var landcell = LScape.get_landcell(longcellID) as LandCell; if (landcell == null) { return(cellID); } if (landcell.has_building()) { var envCells = landcell.Building.get_building_cells(); foreach (var envCell in envCells) { if (envCell.point_in_cell(p.Pos)) { return(envCell.ID); } } } // handle underground areas ie. caves // get the terrain Z-height for this X/Y Physics.Polygon walkable = null; var terrainPoly = landcell.find_terrain_poly(p.Pos, ref walkable); if (walkable != null) { Vector3 terrainPos = p.Pos; walkable.Plane.set_height(ref terrainPos); // are we below ground? if so, search all of the indoor cells for this landblock if (terrainPos.Z > p.Pos.Z) { var envCells = landblock.get_envcells(); foreach (var envCell in envCells) { if (envCell.point_in_cell(p.Pos)) { return(envCell.ID); } } } } return(cellID); }
public static void TryFindObject(Vector3 dir) { Dir = dir; LastPickResult = PickResult; ClearSelection(); PickResult = new PickResult(); // first try using physics engine for this // try spawning a tiny 'projectile' object at the camera position // if successfully spawned, simulate proceeding in that direction until something is hit var startPos = Camera.GetPosition(); if (startPos == null) { Console.WriteLine($"Couldn't find current camera position in world!"); return; } var maxSteps = 500; var stepSize = 1.0f; var i = 0; var stepDir = (dir * stepSize).ToNumerics(); var singleBlock = WorldViewer.Instance.SingleBlock; if (singleBlock != uint.MaxValue) { var landblock = LScape.get_landblock(singleBlock); // custom for single landblock IsDungeon if (landblock.IsDungeon) { if (startPos.Landblock != singleBlock >> 16) { startPos.Reframe(singleBlock); } var adjustCell = AdjustCell.Get(startPos.Landblock); for ( ; i < maxSteps; i++) { var foundCell = adjustCell.GetCell(startPos.Frame.Origin); if (foundCell != null) { startPos.ObjCellID = foundCell.Value; break; } startPos.Frame.Origin += stepDir; } } } // todo: make this static var pickerObj = PhysicsObj.makeObject(setupId, pickerGuid.Full, true); pickerObj.State |= PhysicsState.PathClipped; pickerObj.State &= ~PhysicsState.Gravity; pickerObj.set_object_guid(pickerGuid); var worldObj = new WorldObject(); //worldObj.Name = "Picker"; var weenie = new WeenieObject(worldObj); pickerObj.set_weenie_obj(weenie); // perform transition PhysicsObj.IsPicking = true; var showedMsg = false; var spawned = false; for ( ; i < maxSteps; i++) { if (!spawned) { var success = pickerObj.enter_world(startPos); if (!success) { startPos.Frame.Origin += stepDir; continue; } else { //Console.WriteLine($"Successfully spawned picker @ {startPos}"); spawned = true; } } var nextPos = new ACE.Server.Physics.Common.Position(pickerObj.Position); nextPos.Frame.Origin += stepDir; var transition = pickerObj.transition(pickerObj.Position, nextPos, false); // debug collision info if (transition == null) { Console.WriteLine($"Null transition result!"); showedMsg = true; break; } else if (transition.CollisionInfo.CollidedWithEnvironment || transition.CollisionInfo.CollideObject.Count > 0) { /*if (transition.CollisionInfo.CollidedWithEnvironment) * Console.WriteLine($"CollidedWithEnvironment"); * * if (transition.CollisionInfo.CollideObject.Count > 0) * { * Console.WriteLine($"CollideObjs:"); * foreach (var collideObj in transition.CollisionInfo.CollideObject) * Console.WriteLine($"{collideObj.PartArray.Setup._dat.Id:X8} @ {collideObj.Position.ShortLoc()}"); * }*/ BuildHitPolys(); showedMsg = true; break; } else { pickerObj.SetPositionInternal(transition); } } PhysicsObj.IsPicking = false; if (!spawned) { Console.WriteLine($"Failed to spawn picker @ {Camera.GetPosition()}"); } else if (!showedMsg) { Console.WriteLine($"No collisions"); } // cleanup pickerObj.DestroyObject(); }
public static void LoadEncounters() { Initting = true; MainWindow.Instance.SuppressStatusText = true; SetDatabaseConfig(); Encounters = new List <WorldObject>(); var timer = Stopwatch.StartNew(); // get the list of loaded landblocks foreach (var lbid in LScape.Landblocks.Keys) { var encounters = DatabaseManager.World.GetCachedEncountersByLandblock((ushort)(lbid >> 16)); Console.WriteLine($"Found {encounters.Count:N0} encounters for {lbid:X8}"); var landblock = LScape.get_landblock(lbid); foreach (var encounter in encounters) { var wo = WorldObjectFactory.CreateNewWorldObject(encounter.WeenieClassId); if (wo == null) { continue; } wo.InitPhysicsObj(); var xPos = Math.Clamp(encounter.CellX * 24.0f, 0.5f, 191.5f); var yPos = Math.Clamp(encounter.CellY * 24.0f, 0.5f, 191.5f); var pos = new Position(); pos.ObjCellID = lbid & 0xFFFF0000 | 1; pos.Frame = new AFrame(new Vector3(xPos, yPos, 0), Quaternion.Identity); pos.adjust_to_outside(); pos.Frame.Origin.Z = landblock.GetZ(pos.Frame.Origin); wo.Location = new ACE.Entity.Position(pos.ObjCellID, pos.Frame.Origin, pos.Frame.Orientation); var sortCell = LScape.get_landcell(pos.ObjCellID) as SortCell; if (sortCell != null && sortCell.has_building()) { continue; } var success = wo.AddPhysicsObj(pos); if (!success) { Console.WriteLine($"LoadEncounters({lbid:X8}).AddPhysicsObj({wo.Name}, {pos}) - failed to spawn"); continue; } Console.WriteLine($"Spawned {encounter.WeenieClassId} - {wo.Name} @ {pos}"); AddEncounter(wo); } } TickGenerators(GeneratorTickMode.Encounters); timer.Stop(); Console.WriteLine($"Completed in {timer.Elapsed.TotalSeconds}s"); }