diff --git a/Source/ACE.Server/Entity/Landblock.cs b/Source/ACE.Server/Entity/Landblock.cs index 43c0a438f1..7dbc5cef6a 100644 --- a/Source/ACE.Server/Entity/Landblock.cs +++ b/Source/ACE.Server/Entity/Landblock.cs @@ -884,10 +884,33 @@ private bool AddWorldObjectInternal(WorldObject wo) if (log.IsDebugEnabled) log.Debug($"AddWorldObjectInternal: couldn't spawn generator 0x{wo.Guid}:{wo.Name} [{wo.WeenieClassId} - {wo.WeenieType}] at {wo.Location.ToLOCString()}"); } - else if (wo.ProjectileTarget == null && !(wo is SpellProjectile)) - log.Warn($"AddWorldObjectInternal: couldn't spawn 0x{wo.Guid}:{wo.Name} [{wo.WeenieClassId} - {wo.WeenieType}] at {wo.Location.ToLOCString()}"); + else if (wo.ProjectileTarget is null && wo is not SpellProjectile) + { + if (wo.Guid.IsDynamic() && wo is not CombatPet && wo is not Pet && wo is not Portal) + { + if (PropertyManager.GetBool("dynamic_scatter_retry").Item) + { + var woFailedLocation = new Position(wo.Location); + wo.RetryEnterWorldWithScatter = true; + wo.InitPhysicsObj(); + success = wo.AddPhysicsObj(); + if (!success) + log.Warn($"Landblock.AddWorldObjectInternal: Could not spawn dynamic object!\n 0x{wo.Guid} - {wo.NameWithMaterial} (WCID: {wo.WeenieClassId} | WeenieType: {wo.WeenieType})\n at {wo.Location.ToLOCString()}"); + else + { + if (log.IsDebugEnabled) + log.Debug($"Landblock.AddWorldObjectInternal: Successfully moved and spawned dynamic object!\n 0x{wo.Guid} - {wo.NameWithMaterial} (WCID: {wo.WeenieClassId} | WeenieType: {wo.WeenieType})\n from: {wo.Location.ToLOCString()}\n to: {woFailedLocation.ToLOCString()}"); + } + } + else + log.Warn($"Landblock.AddWorldObjectInternal: Could not spawn dynamic object!\n 0x{wo.Guid} - {wo.NameWithMaterial} (WCID: {wo.WeenieClassId} | WeenieType: {wo.WeenieType})\n at {wo.Location.ToLOCString()}"); + } + else + log.Warn($"AddWorldObjectInternal: couldn't spawn 0x{wo.Guid}:{wo.Name} [{wo.WeenieClassId} - {wo.WeenieType}] at {wo.Location.ToLOCString()}"); + } - return false; + if (!success) + return false; } } diff --git a/Source/ACE.Server/Managers/PropertyManager.cs b/Source/ACE.Server/Managers/PropertyManager.cs index 4de87fd0e2..a2217b4c11 100644 --- a/Source/ACE.Server/Managers/PropertyManager.cs +++ b/Source/ACE.Server/Managers/PropertyManager.cs @@ -539,8 +539,7 @@ public static void LoadDefaultProperties() ("chat_log_trade", new Property(false, "log trade chat")), ("chat_log_townchans", new Property(false, "log advocate town chat")), ("chat_requires_account_15days", new Property(false, "global chat privileges requires accounts to be 15 days or older")), - ("chess_enabled", new Property(true, "if FALSE then chess will be disabled")), - ("use_cloak_proc_custom_scale", new Property(false, "If TRUE, the calculation for cloak procs will be based upon the values set by the server oeprator.")), + ("chess_enabled", new Property(true, "if FALSE then chess will be disabled")), ("client_movement_formula", new Property(false, "If enabled, server uses DoMotion/StopMotion self-client movement methods instead of apply_raw_movement")), ("container_opener_name", new Property(false, "If enabled, when a player tries to open a container that is already in use by someone else, replaces 'someone else' in the message with the actual name of the player")), ("corpse_decay_tick_logging", new Property(false, "If ENABLED then player corpse ticks will be logged")), @@ -548,6 +547,7 @@ public static void LoadDefaultProperties() ("craft_exact_msg", new Property(false, "If TRUE, and player has crafting chance of success dialog enabled, shows them an additional message in their chat window with exact %")), ("creature_name_check", new Property(true, "if enabled, creature names in world database restricts player names during character creation")), ("creatures_drop_createlist_wield", new Property(false, "If FALSE then Wielded items in CreateList will not drop. Retail defaulted to TRUE but there are currently data errors")), + ("dynamic_scatter_retry", new Property(false, "if TRUE, landblock will retry to spawn dynamic worldobjects that fail their first position per dynamic_scatter_num_retries with a radius of dynamic_scatter_retry_radius")), ("equipmentsetid_enabled", new Property(true, "enable this to allow adding EquipmentSetIDs to loot armor")), ("equipmentsetid_name_decoration", new Property(false, "enable this to add the EquipmentSet name to loot armor name")), ("fastbuff", new Property(true, "If TRUE, enables the fast buffing trick from retail.")), @@ -606,6 +606,7 @@ public static void LoadDefaultProperties() ("universal_masteries", new Property(true, "if TRUE, matches end of retail masteries - players wielding almost any weapon get +5 DR, except if the weapon \"seems tough to master\". " + "if FALSE, players start with mastery of 1 melee and 1 ranged weapon type based on heritage, and can later re-select these 2 masteries")), ("unlimited_sequence_gaps", new Property(false, "upon startup, allows server to find all unused guids in a range instead of a set hard limit")), + ("use_cloak_proc_custom_scale", new Property(false, "If TRUE, the calculation for cloak procs will be based upon the values set by the server oeprator.")), ("use_generator_rotation_offset", new Property(true, "enables or disables using the generator's current rotation when offseting relative positions")), ("use_portal_max_level_requirement", new Property(true, "disable this to ignore the max level restriction on portals")), ("use_turbine_chat", new Property(true, "enables or disables global chat channels (General, LFG, Roleplay, Trade, Olthoi, Society, Allegience)")), @@ -623,6 +624,7 @@ public static void LoadDefaultProperties() ("chat_requires_player_level", new Property(0, "the level a player is required to have for global chat privileges")), ("corpse_spam_limit", new Property(15, "the number of corpses a player is allowed to leave on a landblock at one time")), ("default_subscription_level", new Property(1, "retail defaults to 1, 1 = standard subscription (same as 2 and 3), 4 grants ToD pre-order bonus item Asheron's Benediction")), + ("dynamic_scatter_num_retries", new Property(20, "number of retry attempts for dynamic scatter position. Be cautious in adjusting this number")), ("fellowship_even_share_level", new Property(50, "level when fellowship XP sharing is no longer restricted")), ("mansion_min_rank", new Property(6, "overrides the default allegiance rank required to own a mansion")), ("max_chars_per_account", new Property(11, "retail defaults to 11, client supports up to 20")), @@ -636,28 +638,27 @@ public static void LoadDefaultProperties() public static readonly ReadOnlyDictionary> DefaultDoubleProperties = DictOf( - + ("advocate_fane_auto_bestow_level", new Property(1, "the level that advocates are automatically bestowed by Advocate Fane if advocate_fane_auto_bestow is true")), + ("aetheria_drop_rate", new Property(1.0, "Modifier for Aetheria drop rate, 1 being normal")), ("cantrip_drop_rate", new Property(1.0, "Scales the chance for cantrips to drop in each tier. Defaults to 1.0, as per end of retail")), + ("chess_ai_start_time", new Property(-1.0, "the number of seconds for the chess ai to start. defaults to -1 (disabled)")), ("cloak_cooldown_seconds", new Property(5.0, "The number of seconds between possible cloak procs.")), ("cloak_max_proc_base", new Property(0.25, "The max proc chance of a cloak.")), ("cloak_max_proc_damage_percentage", new Property(0.30, "The damage percentage at which cloak proc chance plateaus.")), ("cloak_min_proc", new Property(0, "The minimum proc chance of a cloak.")), - ("minor_cantrip_drop_rate", new Property(1.0, "Scales the chance for minor cantrips to drop, relative to other cantrip levels in the tier. Defaults to 1.0, as per end of retail")), - ("major_cantrip_drop_rate", new Property(1.0, "Scales the chance for major cantrips to drop, relative to other cantrip levels in the tier. Defaults to 1.0, as per end of retail")), - ("epic_cantrip_drop_rate", new Property(1.0, "Scales the chance for epic cantrips to drop, relative to other cantrip levels in the tier. Defaults to 1.0, as per end of retail")), - ("legendary_cantrip_drop_rate", new Property(1.0, "Scales the chance for legendary cantrips to drop, relative to other cantrip levels in the tier. Defaults to 1.0, as per end of retail")), - - ("advocate_fane_auto_bestow_level", new Property(1, "the level that advocates are automatically bestowed by Advocate Fane if advocate_fane_auto_bestow is true")), - ("aetheria_drop_rate", new Property(1.0, "Modifier for Aetheria drop rate, 1 being normal")), - ("chess_ai_start_time", new Property(-1.0, "the number of seconds for the chess ai to start. defaults to -1 (disabled)")), + ("dynamic_scatter_retry_radius", new Property(0.05, "The radius at which each dynamic scatter retry is adjusted by. Be cautious in adjusting this number")), ("encounter_delay", new Property(1800, "the number of seconds a generator profile for regions is delayed from returning to free slots")), ("encounter_regen_interval", new Property(600, "the number of seconds a generator for regions at which spawns its next set of objects")), + ("epic_cantrip_drop_rate", new Property(1.0, "Scales the chance for epic cantrips to drop, relative to other cantrip levels in the tier. Defaults to 1.0, as per end of retail")), ("equipmentsetid_drop_rate", new Property(1.0, "Modifier for EquipmentSetID drop rate, 1 being normal")), ("fast_missile_modifier", new Property(1.2, "The speed multiplier applied to fast missiles. Defaults to retail value of 1.2")), ("ignore_magic_armor_pvp_scalar", new Property(1.0, "Scales the effectiveness of IgnoreMagicArmor (ie. hollow weapons) in pvp battles. 1.0 = full effectiveness / ignore all enchantments on armor (default), 0.5 = half effectiveness / use half enchantments from armor, 0.0 = no effectiveness / use full enchantments from armor")), ("ignore_magic_resist_pvp_scalar", new Property(1.0, "Scales the effectiveness of IgnoreMagicResist (ie. hollow weapons) in pvp battles. 1.0 = full effectiveness / ignore all resistances from life enchantments (default), 0.5 = half effectiveness / use half resistances from life enchantments, 0.0 = no effectiveness / use full resistances from life enchantments")), + ("legendary_cantrip_drop_rate", new Property(1.0, "Scales the chance for legendary cantrips to drop, relative to other cantrip levels in the tier. Defaults to 1.0, as per end of retail")), ("luminance_modifier", new Property(1.0, "Scales the amount of luminance received by players")), + ("major_cantrip_drop_rate", new Property(1.0, "Scales the chance for major cantrips to drop, relative to other cantrip levels in the tier. Defaults to 1.0, as per end of retail")), ("melee_max_angle", new Property(0.0, "for melee players, the maximum angle before a TurnTo is required. retail appeared to have required a TurnTo even for the smallest of angle offsets.")), + ("minor_cantrip_drop_rate", new Property(1.0, "Scales the chance for minor cantrips to drop, relative to other cantrip levels in the tier. Defaults to 1.0, as per end of retail")), ("mob_awareness_range", new Property(1.0, "Scales the distance the monsters become alerted and aggro the players")), ("pk_new_character_grace_period", new Property(300, "the number of seconds, in addition to pk_respite_timer, that a player killer is set to non-player killer status after first exiting training academy")), ("pk_respite_timer", new Property(300, "the number of seconds that a player killer is set to non-player killer status after dying to another player killer")), diff --git a/Source/ACE.Server/Physics/PhysicsObj.cs b/Source/ACE.Server/Physics/PhysicsObj.cs index 930aea489c..53691eabda 100644 --- a/Source/ACE.Server/Physics/PhysicsObj.cs +++ b/Source/ACE.Server/Physics/PhysicsObj.cs @@ -2430,6 +2430,14 @@ public bool enter_world(bool slide) if (slide) setPos.Flags |= SetPositionFlags.Slide; + if (WeenieObj.WorldObject.RetryEnterWorldWithScatter) + { + setPos.Flags |= SetPositionFlags.Scatter; + setPos.NumTries = (int)PropertyManager.GetLong("dynamic_scatter_num_retries").Item; + setPos.RadX = (float)PropertyManager.GetDouble("dynamic_scatter_retry_radius").Item; + setPos.RadY = (float)PropertyManager.GetDouble("dynamic_scatter_retry_radius").Item; + } + var result = SetPosition(setPos); if (result != SetPositionError.OK) return false; diff --git a/Source/ACE.Server/WorldObjects/WorldObject.cs b/Source/ACE.Server/WorldObjects/WorldObject.cs index 0ac00a8e7e..74ae0b6973 100644 --- a/Source/ACE.Server/WorldObjects/WorldObject.cs +++ b/Source/ACE.Server/WorldObjects/WorldObject.cs @@ -1097,5 +1097,13 @@ public int StructureUnitValue return Math.Max(0, structureUnitValue); } } + + /// + /// Retry PhysicsObj.enter_world with added Scatter position flag and options + /// + /// + /// Used by Landblock, this lets the physics system attempt to adjust failed position slightly with scatter to allow object to successfully enter the world. + /// + public bool RetryEnterWorldWithScatter; } }