public void ActivateLinks(List <LandblockInstance> sourceObjects, List <Biota> biotas) { if (LinkedInstances.Count == 0) { return; } if (IsGenerator) { AddGeneratorLinks(); return; } foreach (var link in LinkedInstances) { WorldObject wo = null; var biota = biotas.FirstOrDefault(b => b.Id == link.Guid); if (biota == null) { wo = WorldObjectFactory.CreateWorldObject(DatabaseManager.World.GetCachedWeenie(link.WeenieClassId), new ObjectGuid(link.Guid)); } else { wo = WorldObjectFactory.CreateWorldObject(biota); //Console.WriteLine("Loaded child biota " + wo.Name); } if (wo == null) { continue; } wo.Location = new Position(link.ObjCellId, link.OriginX, link.OriginY, link.OriginZ, link.AnglesX, link.AnglesY, link.AnglesZ, link.AnglesW); SetLinkProperties(wo); CurrentLandblock?.AddWorldObject(wo); wo.ParentLink = this; ChildLinks.Add(wo); // process nested links recursively foreach (var subLink in link.LandblockInstanceLink) { var linkInstance = sourceObjects.FirstOrDefault(x => x.Guid == subLink.ChildGuid); if (linkInstance != null) { wo.LinkedInstances.Add(linkInstance); } } if (wo.LinkedInstances.Count > 0) { wo.ActivateLinks(sourceObjects, biotas); } } }
/// <summary> /// The only time this should be used is to populate EquippedObjects from the ctor. /// </summary> protected void AddBiotasToEquippedObjects(IEnumerable <Biota> wieldedItems) { foreach (var biota in wieldedItems) { var worldObject = WorldObjectFactory.CreateWorldObject(biota); EquippedObjects[worldObject.Guid] = worldObject; EncumbranceVal += worldObject.Burden; } EquippedObjectsLoaded = true; }
/// <summary> /// The only time this should be used is to populate EquippedObjects from the ctor. /// </summary> protected void AddBiotasToEquippedObjects(IEnumerable <ACE.Database.Models.Shard.Biota> wieldedItems) { foreach (var biota in wieldedItems) { var worldObject = WorldObjectFactory.CreateWorldObject(biota); EquippedObjects[worldObject.Guid] = worldObject; EncumbranceVal += (worldObject.EncumbranceVal ?? 0); } EquippedObjectsLoaded = true; SetChildren(); }
public GameEventApproachVendor(Session session, WorldObject vendor, List <WorldObject> items) : base(GameEventType.ApproachVendor, GameMessageGroup.Group09, session) { Writer.Write(vendor.Guid.Full); // merchant id // bit mask ? categories / mask may need figured out more. Writer.Write((uint)vendor.MerchandiseItemTypes); Writer.Write((uint)vendor.MerchandiseMinValue); Writer.Write((uint)vendor.MerchandiseMaxValue); Writer.Write((uint)(vendor.DealMagicalItems ?? false ? 1 : 0)); // magic Writer.Write((float)vendor.BuyPrice); // buy_price Writer.Write((float)vendor.SellPrice); // sell_price Writer.Write(vendor.AlternateCurrencyDID ?? 0u); // trade id .. wcid of currency vendor uses if (vendor.AlternateCurrencyDID > 0) { var currency = WorldObjectFactory.CreateWorldObject(Database.DatabaseManager.World.GetAceObjectByWeenie((uint)vendor.AlternateCurrencyDID)); string fixedPlural = currency.NamePlural; if (fixedPlural == null) { fixedPlural = currency.Name; if (fixedPlural.EndsWith("ch") || fixedPlural.EndsWith("s") || fixedPlural.EndsWith("sh") || fixedPlural.EndsWith("x") || fixedPlural.EndsWith("z")) { fixedPlural += "es"; } else { fixedPlural += "s"; } } Writer.Write((uint)0); // trade number .. current amount of that currency player has on hand, need a function to return # of items of specific wcid found in inventory Writer.WriteString16L(fixedPlural); // the name of that currency } else { Writer.Write((uint)0); // trade number .. current amount of that currency player has on hand, need a function to return # of items of specific wcid found in inventory Writer.WriteString16L(""); // the name of that currency } Writer.Write((uint)items.Count); // number of items foreach (WorldObject obj in items) { // Serialize Stream. Writer.Write(0xFFFFFFFF); // pretty sure this is either -1 (0xFFFFFFFF) or specific amount of item.. limited quanity obj.SerializeGameDataOnly(Writer); } Writer.Align(); }
/// <summary> /// The only time this should be used is to populate Inventory from the ctor. /// </summary> protected void SortBiotasIntoInventory(IEnumerable <Biota> biotas) { var worldObjects = new List <WorldObject>(); foreach (var biota in biotas) { worldObjects.Add(WorldObjectFactory.CreateWorldObject(biota)); } SortWorldObjectsIntoInventory(worldObjects); if (worldObjects.Count > 0) { log.Error("Inventory detected without a container to put it in to."); } }
/// <summary> /// The only time this should be used is to populate Inventory from the ctor. /// </summary> protected void SortBiotasIntoInventory(IEnumerable <ACE.Database.Models.Shard.Biota> biotas) { var worldObjects = new List <WorldObject>(); foreach (var biota in biotas) { worldObjects.Add(WorldObjectFactory.CreateWorldObject(biota)); } SortWorldObjectsIntoInventory(worldObjects); if (worldObjects.Count > 0) { Console.WriteLine("Inventory detected without a container to put it in to."); } }
/// <summary> /// On initial load, we will create all of the wielded items as world objects and add to dictionary for management. /// </summary> /// <param name="aceObject"></param> public Container(AceObject aceObject) : base(aceObject) { CoinValue = 0; log.Debug($"{aceObject.Name} CoinValue initialized to {CoinValue}"); Burden = 0; log.Debug($"{aceObject.Name} Burden initialized to {Burden}"); Burden += Weenie.EncumbranceVal ?? 0; log.Debug($"{aceObject.Name}'s weenie id is {Weenie.WeenieClassId} and its base burden is {Weenie.EncumbranceVal}, added to burden, Burden = {Burden}"); Value = 0; Value += Weenie.Value ?? 0; WieldedObjects = new Dictionary <ObjectGuid, WorldObject>(); foreach (var wieldedItem in WieldedItems) { ObjectGuid woGuid = new ObjectGuid(wieldedItem.Value.AceObjectId); WieldedObjects.Add(woGuid, WorldObjectFactory.CreateWorldObject(wieldedItem.Value)); Burden += wieldedItem.Value.EncumbranceVal; log.Debug($"{aceObject.Name} is wielding {wieldedItem.Value.Name}, adding {wieldedItem.Value.EncumbranceVal}, current Burden = {Burden}"); Value += wieldedItem.Value.Value; } InventoryObjects = new Dictionary <ObjectGuid, WorldObject>(); foreach (var inventoryItem in Inventory) { ObjectGuid woGuid = new ObjectGuid(inventoryItem.Value.AceObjectId); WorldObject wo = WorldObjectFactory.CreateWorldObject(inventoryItem.Value); InventoryObjects.Add(woGuid, wo); Burden += wo.Burden ?? 0; log.Debug($"{aceObject.Name} is has {wo.Name} in inventory, adding {wo.Burden}, current Burden = {Burden}"); Value += wo.Value ?? 0; if (wo.WeenieType == WeenieType.Coin) { CoinValue += wo.Value ?? 0; log.Debug($"{aceObject.Name} is has {wo.Name} in inventory, of WeenieType.Coin, adding {wo.Value}, current CoinValue = {CoinValue}"); } } }
public virtual void ActivateLinks(List <LandblockInstance> sourceObjects) { if (LinkedInstances.Count == 0) { return; } if (IsGenerator) { AddGeneratorLinks(); return; } foreach (var link in LinkedInstances) { var wo = WorldObjectFactory.CreateWorldObject(DatabaseManager.World.GetCachedWeenie(link.WeenieClassId), new ObjectGuid(link.Guid)); if (wo == null) { continue; } wo.Location = new Position(link.ObjCellId, link.OriginX, link.OriginY, link.OriginZ, link.AnglesX, link.AnglesY, link.AnglesZ, link.AnglesW); wo.ActivationTarget = Guid.Full; CurrentLandblock?.AddWorldObject(wo); // process nested links recursively foreach (var subLink in link.LandblockInstanceLink) { var linkInstance = sourceObjects.FirstOrDefault(x => x.Guid == subLink.ChildGuid); if (linkInstance != null) { wo.LinkedInstances.Add(linkInstance); } } if (wo.LinkedInstances.Count > 0) { wo.ActivateLinks(sourceObjects); } } }
/// <summary> /// On initial load, we will create all of the wielded items as world objects and add to dictionary for management. /// </summary> /// <param name="aceObject"></param> public Container(AceObject aceObject) : base(aceObject) { CoinValue = 0; WieldedObjects = new Dictionary <ObjectGuid, WorldObject>(); foreach (var wieldedItem in WieldedItems) { ObjectGuid woGuid = new ObjectGuid(wieldedItem.Value.AceObjectId); WieldedObjects.Add(woGuid, WorldObjectFactory.CreateWorldObject(wieldedItem.Value)); } InventoryObjects = new Dictionary <ObjectGuid, WorldObject>(); foreach (var inventoryItem in Inventory) { ObjectGuid woGuid = new ObjectGuid(inventoryItem.Value.AceObjectId); InventoryObjects.Add(woGuid, WorldObjectFactory.CreateWorldObject(inventoryItem.Value)); if (InventoryObjects[woGuid].WeenieType == WeenieType.Container) { InventoryObjects[woGuid].InventoryObjects = new Dictionary <ObjectGuid, WorldObject>(); foreach (var item in Inventory[woGuid].Inventory) { ObjectGuid cwoGuid = new ObjectGuid(item.Value.AceObjectId); InventoryObjects[woGuid].InventoryObjects.Add(cwoGuid, WorldObjectFactory.CreateWorldObject(item.Value)); if (InventoryObjects[woGuid].WeenieType == WeenieType.Coin) { CoinValue += item.Value.Value ?? 0; } } } if (InventoryObjects[woGuid].WeenieType == WeenieType.Coin) { CoinValue += inventoryItem.Value.Value ?? 0; } } }
public void HandleActionAbandonHouse() { Console.WriteLine($"\n{Name}.HandleActionAbandonHouse()"); var house = GetHouse(); if (house != null) { house.HouseOwner = null; house.SaveBiotaToDatabase(); // relink house.UpdateLinks(); // player slumlord 'off' animation var slumlord = house.SlumLord; slumlord.EnqueueBroadcastMotion(new Motion(MotionStance.Invalid, MotionCommand.Off)); // reset slumlord name var weenie = DatabaseManager.World.GetCachedWeenie(slumlord.WeenieClassId); var wo = WorldObjectFactory.CreateWorldObject(weenie, new ObjectGuid(0)); slumlord.Name = wo.Name; slumlord.EnqueueBroadcast(new GameMessagePublicUpdatePropertyString(slumlord, PropertyString.Name, wo.Name)); } HouseId = null; HouseInstance = null; HousePurchaseTimestamp = null; House = null; // send text message Session.Network.EnqueueSend(new GameMessageSystemChat("You abandon your house!", ChatMessageType.Broadcast)); HandleActionQueryHouse(); }
/// <summary> /// Handles the eviction process for a player house /// </summary> public static void HandleEviction(House house, uint playerGuid, bool multihouse = false) { // clear out slumlord inventory var slumlord = house.SlumLord; slumlord.ClearInventory(true); var player = PlayerManager.FindByGuid(playerGuid, out bool isOnline); if (!PropertyManager.GetBool("house_rent_enabled", true).Item&& !multihouse) { // rent disabled, push forward var purchaseTime = (uint)(player.HousePurchaseTimestamp ?? 0); var nextRentTime = house.GetRentDue(purchaseTime); player.HouseRentTimestamp = (int)nextRentTime; log.Info($"HouseManager.HandleRentPaid({player.Name}): house rent disabled via config"); // re-add item to queue AddRentQueue(player, house); return; } // handle eviction house.HouseOwner = null; house.MonarchId = null; house.HouseOwnerName = null; house.ClearPermissions(); house.SaveBiotaToDatabase(); // relink house.UpdateLinks(); // player slumlord 'off' animation var off = new Motion(MotionStance.Invalid, MotionCommand.Off); slumlord.CurrentMotionState = off; slumlord.EnqueueBroadcastMotion(off); // reset slumlord name var weenie = DatabaseManager.World.GetCachedWeenie(slumlord.WeenieClassId); var wo = WorldObjectFactory.CreateWorldObject(weenie, ObjectGuid.Invalid); slumlord.Name = wo.Name; slumlord.EnqueueBroadcast(new GameMessagePublicUpdatePropertyString(slumlord, PropertyString.Name, wo.Name)); slumlord.SaveBiotaToDatabase(); // if evicting a multihouse owner's previous house, // no update for player properties if (player.HouseInstance == house.Guid.Full) { player.HouseId = null; player.HouseInstance = null; //player.HousePurchaseTimestamp = null; player.HouseRentTimestamp = null; } else { log.Warn($"HouseManager.HandleRentEviction({house.Guid}, {player.Name}, {multihouse}): house guids don't match {player.HouseInstance}"); } house.ClearRestrictions(); log.Info($"HouseManager.HandleRentEviction({player.Name})"); if (multihouse) { RemoveRentQueue(house.Guid.Full); player.SaveBiotaToDatabase(); return; } if (!isOnline) { // inform player of eviction when they log in var offlinePlayer = PlayerManager.GetOfflinePlayer(playerGuid); if (offlinePlayer == null) { log.Warn($"{player.Name}.HandleEviction(): couldn't find offline player"); return; } offlinePlayer.SetProperty(PropertyBool.HouseEvicted, true); offlinePlayer.SaveBiotaToDatabase(); return; } var onlinePlayer = PlayerManager.GetOnlinePlayer(playerGuid); onlinePlayer.House = null; // send text message onlinePlayer.Session.Network.EnqueueSend(new GameMessageSystemChat("You abandon your house!", ChatMessageType.Broadcast)); onlinePlayer.RemoveDeed(); onlinePlayer.SaveBiotaToDatabase(); // clear house panel for online player var actionChain = new ActionChain(); actionChain.AddDelaySeconds(3.0f); // wait for slumlord inventory biotas above to save actionChain.AddAction(onlinePlayer, onlinePlayer.HandleActionQueryHouse); actionChain.EnqueueChain(); }
public static async void HandleEviction(PlayerHouse playerHouse) { // todo: copied from Player_House.HandleActionAbandonHouse, move to House.Abandon() // todo: get online copy of house var house = playerHouse.House; // clear out slumlord inventory // todo: get online copy of house var slumlord = house.SlumLord; slumlord.ClearInventory(true); var player = PlayerManager.FindByGuid(playerHouse.PlayerGuid, out bool isOnline); if (!PropertyManager.GetBool("house_rent_enabled", true).Item) { // rent disabled, push forward var purchaseTime = (uint)(player.HousePurchaseTimestamp ?? 0); var nextRentTime = house.GetRentDue(purchaseTime); player.HouseRentTimestamp = (int)nextRentTime; log.Info($"HouseManager.HandleRentPaid({playerHouse.PlayerName}): house rent disabled via config"); BuildRentQueue(); return; } house.HouseOwner = null; house.MonarchId = null; house.HouseOwnerName = null; house.ClearPermissions(); house.SaveBiotaToDatabase(); // relink house.UpdateLinks(); // player slumlord 'off' animation var off = new Motion(MotionStance.Invalid, MotionCommand.Off); slumlord.CurrentMotionState = off; slumlord.EnqueueBroadcastMotion(off); // reset slumlord name var weenie = DatabaseManager.World.GetCachedWeenie(slumlord.WeenieClassId); var wo = WorldObjectFactory.CreateWorldObject(weenie, ObjectGuid.Invalid); slumlord.Name = wo.Name; slumlord.EnqueueBroadcast(new GameMessagePublicUpdatePropertyString(slumlord, PropertyString.Name, wo.Name)); slumlord.SaveBiotaToDatabase(); player.HouseId = null; player.HouseInstance = null; //player.HousePurchaseTimestamp = null; player.HouseRentTimestamp = null; house.ClearRestrictions(); log.Info($"HouseManager.HandleRentEviction({playerHouse.PlayerName})"); BuildRentQueue(); if (!isOnline) { // inform player of eviction when they log in var offlinePlayer = PlayerManager.GetOfflinePlayer(playerHouse.PlayerGuid); if (offlinePlayer == null) { log.Warn($"{playerHouse.PlayerName}.HandleEviction(): couldn't find offline player"); return; } offlinePlayer.SetProperty(PropertyBool.HouseEvicted, true); offlinePlayer.SaveBiotaToDatabase(); return; } var onlinePlayer = PlayerManager.GetOnlinePlayer(playerHouse.PlayerGuid); onlinePlayer.House = null; // send text message onlinePlayer.Session.Network.EnqueueSend(new GameMessageSystemChat("You abandon your house!", ChatMessageType.Broadcast)); onlinePlayer.RemoveDeed(); await Task.Delay(3000); // wait for slumlord inventory biotas above to save onlinePlayer.HandleActionQueryHouse(); }
public static void HandleCreateInst(Session session, params string[] parameters) { var loc = new Position(session.Player.Location); var param = parameters[0]; Weenie weenie = null; if (uint.TryParse(param, out var wcid)) { weenie = DatabaseManager.World.GetCachedWeenie(wcid); // wcid } else { weenie = DatabaseManager.World.GetCachedWeenie(param); // classname } if (weenie == null) { session.Network.EnqueueSend(new GameMessageSystemChat($"Couldn't find weenie {param}", ChatMessageType.Broadcast)); return; } var landblock = session.Player.CurrentLandblock.Id.Landblock; var nextStaticGuid = GetNextStaticGuid(landblock); var wo = WorldObjectFactory.CreateWorldObject(weenie, new ObjectGuid(nextStaticGuid)); if (wo == null) { session.Network.EnqueueSend(new GameMessageSystemChat($"Failed to create new object for {weenie.ClassId} - {weenie.ClassName}", ChatMessageType.Broadcast)); return; } if (!wo.Stuck) { session.Network.EnqueueSend(new GameMessageSystemChat($"{weenie.ClassId} - {weenie.ClassName} is missing PropertyBool.Stuck, cannot spawn as landblock instance", ChatMessageType.Broadcast)); return; } // spawn as ethereal temporarily, to spawn directly on player position wo.Ethereal = true; wo.Location = new Position(loc); // even on flat ground, objects can sometimes fail to spawn at the player's current Z // Position.Z has some weird thresholds when moving around, but i guess the same logic doesn't apply when trying to spawn in... wo.Location.PositionZ += 0.05f; session.Network.EnqueueSend(new GameMessageSystemChat($"Creating new landblock instance @ {loc.ToLOCString()}\n{wo.WeenieClassId} - {wo.Name} ({nextStaticGuid:X8})", ChatMessageType.Broadcast)); wo.EnterWorld(); LastStaticGuid[landblock] = wo.Guid.Full; // serialize to .sql file var contentFolder = VerifyContentFolder(session, false); var sep = Path.DirectorySeparatorChar; var folder = new DirectoryInfo($"{contentFolder.FullName}{sep}sql{sep}6 LandblockExtendedData{sep}"); if (!folder.Exists) { folder.Create(); } var pos = wo.PhysicsObj.Position; var origin = pos.Frame.Origin; var rotation = pos.Frame.Orientation; var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); var insert = $"INSERT INTO `landblock_instance` (`guid`, `weenie_Class_Id`, `obj_Cell_Id`, `origin_X`, `origin_Y`, `origin_Z`, `angles_W`, `angles_X`, `angles_Y`, `angles_Z`, `is_Link_Child`, `last_Modified`)\n" + $"VALUES(0x{wo.Guid.Full:X8}, {wo.WeenieClassId}, 0x{wo.PhysicsObj.Position.ObjCellID:X8}, {origin.X}, {origin.Y}, {origin.Z}, {rotation.W}, {rotation.X}, {rotation.Y}, {rotation.Z}, False, '{timestamp}'); /* {wo.Name} */\n" + $"/* @teleloc {wo.Location.ToLOCString()} */"; var sql_filename = $"{landblock:X4}.sql"; using (var file = File.Open($"{folder.FullName}{sep}{sql_filename}", FileMode.OpenOrCreate)) { file.Seek(0, SeekOrigin.End); using (var stream = new StreamWriter(file)) { if (file.Position > 0) { stream.WriteLine(); } stream.WriteLine(insert); } } }
public static void HandleCreateInst(Session session, params string[] parameters) { var loc = new Position(session.Player.Location); var param = parameters[0]; Weenie weenie = null; uint?parentGuid = null; var landblock = session.Player.CurrentLandblock.Id.Landblock; var firstStaticGuid = 0x70000000 | (uint)landblock << 12; if (parameters.Length > 1) { var allParams = string.Join(" ", parameters); var match = Regex.Match(allParams, @"-p ([\S]+) -c ([\S]+)", RegexOptions.IgnoreCase); if (match.Success) { var parentGuidStr = match.Groups[1].Value; param = match.Groups[2].Value; if (parentGuidStr.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) { parentGuidStr = parentGuidStr.Substring(2); } if (!uint.TryParse(parentGuidStr, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var _parentGuid)) { session.Network.EnqueueSend(new GameMessageSystemChat($"Couldn't parse parent guid {match.Groups[1].Value}", ChatMessageType.Broadcast)); return; } parentGuid = _parentGuid; if (parentGuid <= 0xFFF) { parentGuid = firstStaticGuid | parentGuid; } } else if (parameters[1].StartsWith("-c", StringComparison.OrdinalIgnoreCase)) { // get parent from last appraised object var parent = CommandHandlerHelper.GetLastAppraisedObject(session); if (parent == null) { session.Network.EnqueueSend(new GameMessageSystemChat($"Couldn't find parent object", ChatMessageType.Broadcast)); return; } parentGuid = parent.Guid.Full; } } if (uint.TryParse(param, out var wcid)) { weenie = DatabaseManager.World.GetCachedWeenie(wcid); // wcid } else { weenie = DatabaseManager.World.GetCachedWeenie(param); // classname } if (weenie == null) { session.Network.EnqueueSend(new GameMessageSystemChat($"Couldn't find weenie {param}", ChatMessageType.Broadcast)); return; } // clear any cached instances for this landblock DatabaseManager.World.ClearCachedInstancesByLandblock(landblock); var instances = DatabaseManager.World.GetCachedInstancesByLandblock(landblock); // for link mode, ensure parent guid instance exists WorldObject parentObj = null; LandblockInstance parentInstance = null; if (parentGuid != null) { parentInstance = instances.FirstOrDefault(i => i.Guid == parentGuid); if (parentInstance == null) { session.Network.EnqueueSend(new GameMessageSystemChat($"Couldn't find landblock instance for parent guid 0x{parentGuid:X8}", ChatMessageType.Broadcast)); return; } parentObj = session.Player.CurrentLandblock.GetObject(parentGuid.Value); if (parentObj == null) { session.Network.EnqueueSend(new GameMessageSystemChat($"Couldn't find parent object 0x{parentGuid:X8}", ChatMessageType.Broadcast)); return; } } var nextStaticGuid = GetNextStaticGuid(landblock, instances); var maxStaticGuid = firstStaticGuid | 0xFFF; // manually specify a start guid? if (parameters.Length == 2) { if (uint.TryParse(parameters[1].Replace("0x", ""), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var startGuid)) { if (startGuid <= 0xFFF) { startGuid = firstStaticGuid | startGuid; } if (startGuid < firstStaticGuid || startGuid > maxStaticGuid) { session.Network.EnqueueSend(new GameMessageSystemChat($"Landblock instance guid {startGuid:X8} must be between {firstStaticGuid:X8} and {maxStaticGuid:X8}", ChatMessageType.Broadcast)); return; } var existing = instances.FirstOrDefault(i => i.Guid == startGuid); if (existing != null) { session.Network.EnqueueSend(new GameMessageSystemChat($"Landblock instance guid {startGuid:X8} already exists", ChatMessageType.Broadcast)); return; } nextStaticGuid = startGuid; } } if (nextStaticGuid >= maxStaticGuid) { session.Network.EnqueueSend(new GameMessageSystemChat($"Landblock {landblock:X4} has reached the maximum # of static guids", ChatMessageType.Broadcast)); return; } // create and spawn object var wo = WorldObjectFactory.CreateWorldObject(weenie, new ObjectGuid(nextStaticGuid)); if (wo == null) { session.Network.EnqueueSend(new GameMessageSystemChat($"Failed to create new object for {weenie.ClassId} - {weenie.ClassName}", ChatMessageType.Broadcast)); return; } if (!wo.Stuck) { session.Network.EnqueueSend(new GameMessageSystemChat($"{weenie.ClassId} - {weenie.ClassName} is missing PropertyBool.Stuck, cannot spawn as landblock instance", ChatMessageType.Broadcast)); return; } // spawn as ethereal temporarily, to spawn directly on player position wo.Ethereal = true; wo.Location = new Position(loc); // even on flat ground, objects can sometimes fail to spawn at the player's current Z // Position.Z has some weird thresholds when moving around, but i guess the same logic doesn't apply when trying to spawn in... wo.Location.PositionZ += 0.05f; var isLinkChild = parentInstance != null; session.Network.EnqueueSend(new GameMessageSystemChat($"Creating new landblock instance {(isLinkChild ? "child object " : "")}@ {loc.ToLOCString()}\n{wo.WeenieClassId} - {wo.Name} ({nextStaticGuid:X8})", ChatMessageType.Broadcast)); if (!wo.EnterWorld()) { session.Network.EnqueueSend(new GameMessageSystemChat("Failed to spawn new object at this location", ChatMessageType.Broadcast)); return; } // create new landblock instance var instance = CreateLandblockInstance(wo, isLinkChild); instances.Add(instance); if (isLinkChild) { var link = new LandblockInstanceLink(); link.ParentGuid = parentGuid.Value; link.ChildGuid = wo.Guid.Full; link.LastModified = DateTime.Now; parentInstance.LandblockInstanceLink.Add(link); parentObj.LinkedInstances.Add(instance); // ActivateLinks? parentObj.SetLinkProperties(wo); parentObj.ChildLinks.Add(wo); wo.ParentLink = parentObj; } SyncInstances(session, landblock, instances); }