示例#1
0
        public void BuildProfile(WorldObject wo, Player examiner, bool success = true)
        {
            //Console.WriteLine("Appraise: " + wo.Guid);
            Success = success;

            // get wielder, if applicable
            var wielder = GetWielder(wo, examiner);

            BuildProperties(wo, wielder);
            BuildSpells(wo);

            // Help us make sure the item identify properly
            NPCLooksLikeObject = wo.GetProperty(PropertyBool.NpcLooksLikeObject) ?? false;

            if (PropertiesIID.ContainsKey(PropertyInstanceId.AllowedWielder))
            {
                if (!PropertiesBool.ContainsKey(PropertyBool.AppraisalHasAllowedWielder))
                {
                    PropertiesBool.Add(PropertyBool.AppraisalHasAllowedWielder, true);
                }
            }

            if (PropertiesIID.ContainsKey(PropertyInstanceId.AllowedActivator))
            {
                if (!PropertiesBool.ContainsKey(PropertyBool.AppraisalHasAllowedActivator))
                {
                    PropertiesBool.Add(PropertyBool.AppraisalHasAllowedActivator, true);
                }
            }

            if (PropertiesString.ContainsKey(PropertyString.ScribeAccount) && !examiner.IsAdmin && !examiner.IsSentinel && !examiner.IsEnvoy && !examiner.IsArch && !examiner.IsPsr)
            {
                PropertiesString.Remove(PropertyString.ScribeAccount);
            }

            if (PropertiesString.ContainsKey(PropertyString.HouseOwnerAccount) && !examiner.IsAdmin && !examiner.IsSentinel && !examiner.IsEnvoy && !examiner.IsArch && !examiner.IsPsr)
            {
                PropertiesString.Remove(PropertyString.HouseOwnerAccount);
            }

            if (PropertiesInt.ContainsKey(PropertyInt.Lifespan))
            {
                PropertiesInt[PropertyInt.RemainingLifespan] = wo.GetRemainingLifespan();
            }

            // armor / clothing / shield
            if (wo is Clothing || wo.IsShield)
            {
                BuildArmor(wo);
            }

            if (wo is Creature creature)
            {
                BuildCreature(creature);
            }

            if (wo is MeleeWeapon || wo is Missile || wo is MissileLauncher || wo is Ammunition || wo is Caster)
            {
                BuildWeapon(wo, wielder);
            }

            if (wo is Door || wo is Chest)
            {
                // If wo is not locked, do not send ResistLockpick value. If ResistLockpick is sent for unlocked objects, id panel shows bonus to Lockpick skill
                if (!wo.IsLocked && PropertiesInt.ContainsKey(PropertyInt.ResistLockpick))
                {
                    PropertiesInt.Remove(PropertyInt.ResistLockpick);
                }

                // If wo is locked, append skill check percent, as int, to properties for id panel display on chances of success
                if (wo.IsLocked)
                {
                    var resistLockpick = LockHelper.GetResistLockpick(wo);

                    if (resistLockpick != null)
                    {
                        PropertiesInt[PropertyInt.ResistLockpick] = (int)resistLockpick;

                        var pickSkill = examiner.Skills[Skill.Lockpick].Current;

                        var successChance = SkillCheck.GetSkillChance((int)pickSkill, (int)resistLockpick) * 100;

                        if (!PropertiesInt.ContainsKey(PropertyInt.AppraisalLockpickSuccessPercent))
                        {
                            PropertiesInt.Add(PropertyInt.AppraisalLockpickSuccessPercent, (int)successChance);
                        }
                    }
                }
            }

            if (wo is Corpse)
            {
                PropertiesBool.Clear();
                PropertiesDID.Clear();
                PropertiesFloat.Clear();
                PropertiesInt64.Clear();

                var discardInts = PropertiesInt.Where(x => x.Key != PropertyInt.EncumbranceVal && x.Key != PropertyInt.Value).Select(x => x.Key).ToList();
                foreach (var key in discardInts)
                {
                    PropertiesInt.Remove(key);
                }
                var discardString = PropertiesString.Where(x => x.Key != PropertyString.LongDesc).Select(x => x.Key).ToList();
                foreach (var key in discardString)
                {
                    PropertiesString.Remove(key);
                }
            }

            if (wo is Portal)
            {
                if (PropertiesInt.ContainsKey(PropertyInt.EncumbranceVal))
                {
                    PropertiesInt.Remove(PropertyInt.EncumbranceVal);
                }
            }

            if (wo is SlumLord slumLord)
            {
                PropertiesBool.Clear();
                PropertiesDID.Clear();
                PropertiesFloat.Clear();
                PropertiesIID.Clear();
                //PropertiesInt.Clear();
                PropertiesInt64.Clear();
                PropertiesString.Clear();

                var longDesc = "";

                if (slumLord.HouseOwner.HasValue && slumLord.HouseOwner.Value > 0)
                {
                    longDesc = $"The current maintenance has {(slumLord.IsRentPaid() || !PropertyManager.GetBool("house_rent_enabled").Item ? "" : "not ")}been paid.\n";

                    PropertiesInt.Clear();
                }
                else
                {
                    //longDesc = $"This house is {(slumLord.HouseStatus == HouseStatus.Disabled ? "not " : "")}available for purchase.\n"; // this was the retail msg.
                    longDesc = $"This {(slumLord.House.HouseType == HouseType.Undef ? "house" : slumLord.House.HouseType.ToString().ToLower())} is {(slumLord.House.HouseStatus == HouseStatus.Disabled ? "not " : "")}available for purchase.\n";

                    var discardInts = PropertiesInt.Where(x => x.Key != PropertyInt.HouseStatus && x.Key != PropertyInt.HouseType && x.Key != PropertyInt.MinLevel && x.Key != PropertyInt.MaxLevel && x.Key != PropertyInt.AllegianceMinLevel && x.Key != PropertyInt.AllegianceMaxLevel).Select(x => x.Key).ToList();
                    foreach (var key in discardInts)
                    {
                        PropertiesInt.Remove(key);
                    }
                }

                if (slumLord.HouseRequiresMonarch)
                {
                    longDesc += "You must be a monarch to purchase and maintain this dwelling.\n";
                }

                if (slumLord.AllegianceMinLevel.HasValue)
                {
                    var allegianceMinLevel = PropertyManager.GetLong("mansion_min_rank", -1).Item;
                    if (allegianceMinLevel == -1)
                    {
                        allegianceMinLevel = slumLord.AllegianceMinLevel.Value;
                    }

                    longDesc += $"Restricted to characters of allegiance rank {allegianceMinLevel} or greater.\n";
                }

                PropertiesString.Add(PropertyString.LongDesc, longDesc);
            }

            if (wo is Storage)
            {
                var longDesc = "";

                if (wo.HouseOwner.HasValue && wo.HouseOwner.Value > 0)
                {
                    longDesc = $"Owned by {wo.ParentLink.HouseOwnerName}\n";
                }

                var discardString = PropertiesString.Where(x => x.Key != PropertyString.Use).Select(x => x.Key).ToList();
                foreach (var key in discardString)
                {
                    PropertiesString.Remove(key);
                }

                PropertiesString.Add(PropertyString.LongDesc, longDesc);

                if (PropertiesInt.ContainsKey(PropertyInt.Value))
                {
                    PropertiesInt[PropertyInt.Value] = wo.Biota.GetProperty(PropertyInt.Value, wo.BiotaDatabaseLock) ?? 200; // Value is masked to base value of Storage
                }
            }

            if (wo is Hook)
            {
                // If the hook has any inventory, we need to send THOSE properties instead.
                var hook = wo as Container;

                string baseDescString = "";
                if (wo.ParentLink.HouseOwner != null)
                {
                    // This is for backwards compatibility. This value was not set/saved in earlier versions.
                    // It will get the player's name and save that to the HouseOwnerName property of the house. This is now done when a player purchases a house.
                    if (wo.ParentLink.HouseOwnerName == null)
                    {
                        var houseOwnerPlayer = PlayerManager.FindByGuid((uint)wo.ParentLink.HouseOwner);
                        if (houseOwnerPlayer != null)
                        {
                            wo.ParentLink.HouseOwnerName = houseOwnerPlayer.Name;
                            wo.ParentLink.SaveBiotaToDatabase();
                        }
                    }
                    baseDescString = "This hook is owned by " + wo.ParentLink.HouseOwnerName + ". "; //if house is owned, display this text
                }

                var containsString = "";
                if (hook.Inventory.Count == 1)
                {
                    WorldObject hookedItem = hook.Inventory.First().Value;

                    // Hooked items have a custom "description", containing the desc of the sub item and who the owner of the house is (if any)
                    BuildProfile(hookedItem, examiner, success);

                    containsString = "It contains: \n";

                    if (PropertiesString.ContainsKey(PropertyString.LongDesc) && PropertiesString[PropertyString.LongDesc] != null)
                    {
                        containsString += PropertiesString[PropertyString.LongDesc];
                    }
                    else if (PropertiesString.ContainsKey(PropertyString.ShortDesc) && PropertiesString[PropertyString.ShortDesc] != null)
                    {
                        containsString += PropertiesString[PropertyString.ShortDesc];
                    }
                    else
                    {
                        containsString += PropertiesString[PropertyString.Name];
                    }

                    BuildHookProfile(hookedItem);
                }

                if (PropertiesString.ContainsKey(PropertyString.LongDesc) && PropertiesString[PropertyString.LongDesc] != null)
                {
                    PropertiesString[PropertyString.LongDesc] = baseDescString + containsString;
                }
                else if (PropertiesString.ContainsKey(PropertyString.ShortDesc) && PropertiesString[PropertyString.ShortDesc] != null)
                {
                    PropertiesString[PropertyString.LongDesc] = baseDescString + containsString;
                }
                else
                {
                    PropertiesString[PropertyString.LongDesc] = baseDescString + containsString;
                }
            }

            if (wo is ManaStone)
            {
                var useMessage = "";

                if (wo.ItemCurMana.HasValue)
                {
                    useMessage = "Use on a magic item to give the stone's stored Mana to that item.";
                }
                else
                {
                    useMessage = "Use on a magic item to destroy that item and drain its Mana.";
                }

                PropertiesString[PropertyString.Use] = useMessage;
            }

            if (wo is CraftTool && (wo.ItemType == ItemType.TinkeringMaterial || wo.WeenieClassId >= 36619 && wo.WeenieClassId <= 36628 || wo.WeenieClassId >= 36634 && wo.WeenieClassId <= 36636))
            {
                if (PropertiesInt.ContainsKey(PropertyInt.Structure))
                {
                    PropertiesInt.Remove(PropertyInt.Structure);
                }
            }

            BuildFlags();
        }
示例#2
0
        /// <summary>
        /// Construct all of the info required for appraising any WorldObject
        /// </summary>
        public AppraiseInfo(WorldObject wo, Player examiner, bool success = true)
        {
            //Console.WriteLine("Appraise: " + wo.Guid);

            Success = success;
            if (!Success)
            {
                return;
            }

            // get wielder, if applicable
            var wielder = GetWielder(wo);

            BuildProperties(wo, wielder);
            BuildSpells(wo);

            // Help us make sure the item identify properly
            NPCLooksLikeObject = wo.GetProperty(PropertyBool.NpcLooksLikeObject) ?? false;

            // armor / clothing / shield
            if (wo is Clothing || wo.IsShield)
            {
                BuildArmor(wo);
            }

            if (wo is Creature creature)
            {
                BuildCreature(creature);
            }

            if (wo is MeleeWeapon || wo is Missile || wo is MissileLauncher || wo is Ammunition || wo is Caster)
            {
                BuildWeapon(wo, wielder);
            }

            if (wo is Door || wo is Chest)
            {
                // If wo is not locked, do not send ResistLockpick value. If ResistLockpick is sent for unlocked objects, id panel shows bonus to Lockpick skill
                if (!wo.IsLocked && PropertiesInt.ContainsKey(PropertyInt.ResistLockpick))
                {
                    PropertiesInt.Remove(PropertyInt.ResistLockpick);
                }

                // If wo is locked, append skill check percent, as int, to properties for id panel display on chances of success
                if (wo.IsLocked)
                {
                    var playerLockPickSkill = examiner.Skills[Skill.Lockpick].Current;

                    var doorLockPickResistance = wo.ResistLockpick;

                    var lockpickSuccessPercent = SkillCheck.GetSkillChance((int)playerLockPickSkill, (int)doorLockPickResistance) * 100;

                    if (!PropertiesInt.ContainsKey(PropertyInt.AppraisalLockpickSuccessPercent))
                    {
                        PropertiesInt.Add(PropertyInt.AppraisalLockpickSuccessPercent, (int)lockpickSuccessPercent);
                    }
                }
            }

            BuildFlags();
        }
示例#3
0
        public static void HandleTinkering(Player player, WorldObject tool, WorldObject target, bool confirmed = false)
        {
            Console.WriteLine($"{player.Name}.HandleTinkering({tool.Name}, {target.Name})");

            // calculate % success chance

            var toolWorkmanship = tool.Workmanship ?? 0;
            var itemWorkmanship = target.Workmanship ?? 0;

            var tinkeredCount = target.NumTimesTinkered;
            var attemptMod    = TinkeringDifficulty[tinkeredCount];

            var materialType = tool.MaterialType.Value;
            var salvageMod   = GetMaterialMod(materialType);

            var workmanshipMod = 1.0f;

            if (toolWorkmanship >= itemWorkmanship)
            {
                workmanshipMod = 2.0f;
            }

            var recipe      = DatabaseManager.World.GetCachedCookbook(tool.WeenieClassId, target.WeenieClassId);
            var recipeSkill = (Skill)recipe.Recipe.Skill;
            var skill       = player.GetCreatureSkill(recipeSkill);

            // thanks to Endy's Tinkering Calculator for this formula!
            var difficulty = (int)Math.Floor(((salvageMod * 5.0f) + (itemWorkmanship * salvageMod * 2.0f) - (toolWorkmanship * workmanshipMod * salvageMod / 5.0f)) * attemptMod);

            var successChance = SkillCheck.GetSkillChance((int)skill.Current, difficulty);

            // imbue: divide success by 3

            // handle rare foolproof material
            //if (tool.WeenieClassId >= 30094 && tool.WeenieClassId <= 30106)
            //successChance = 1.0f;

            // check for player option: 'Use Crafting Chance of Success Dialog'
            if (player.GetCharacterOption(CharacterOption.UseCraftingChanceOfSuccessDialog) && !confirmed)
            {
                var percent       = (float)successChance * 100;
                var decimalPlaces = 2;
                var truncated     = percent.Truncate(decimalPlaces);

                var templateMsg = $"You have a % chance of using {tool.Name} on {target.Name}.";
                var floorMsg    = templateMsg.Replace("%", (int)percent + "%");
                var truncateMsg = templateMsg.Replace("%", Math.Round(truncated, decimalPlaces) + "%");
                var exactMsg    = templateMsg.Replace("%", percent + "%");

                var confirm = new Confirmation(ConfirmationType.CraftInteraction, floorMsg, tool, target, player);
                ConfirmationManager.AddConfirmation(confirm);

                player.Session.Network.EnqueueSend(new GameEventConfirmationRequest(player.Session, ConfirmationType.CraftInteraction, confirm.ConfirmationID, floorMsg));
                player.Session.Network.EnqueueSend(new GameMessageSystemChat(exactMsg, ChatMessageType.Craft));

                player.SendUseDoneEvent();
                return;
            }

            var animLength = DoCraftMotion(player);

            var actionChain = new ActionChain();

            actionChain.AddDelaySeconds(animLength);
            actionChain.AddAction(player, () => DoTinkering(player, tool, target, (float)successChance));
            actionChain.EnqueueChain();
        }
示例#4
0
        public void FiftyFiftyIsAccurate()
        {
            var result = SkillCheck.GetSkillChance(100, 100);

            Assert.AreEqual(0.5d, result);
        }
示例#5
0
        private void generateAppraisalInfo(WorldObject wo, Player examiner, bool success = true)
        {
            //Console.WriteLine("Appraise: " + wo.Guid);
            Success = success;

            // get wielder, if applicable
            var wielder = GetWielder(wo, examiner);

            BuildProperties(wo, wielder);
            BuildSpells(wo);

            // Help us make sure the item identify properly
            NPCLooksLikeObject = wo.GetProperty(PropertyBool.NpcLooksLikeObject) ?? false;

            // armor / clothing / shield
            if (wo is Clothing || wo.IsShield)
            {
                BuildArmor(wo);
            }

            if (wo is Creature creature)
            {
                BuildCreature(creature);
            }

            if (wo is MeleeWeapon || wo is Missile || wo is MissileLauncher || wo is Ammunition || wo is Caster)
            {
                BuildWeapon(wo, wielder);
            }

            if (wo is Door || wo is Chest)
            {
                // If wo is not locked, do not send ResistLockpick value. If ResistLockpick is sent for unlocked objects, id panel shows bonus to Lockpick skill
                if (!wo.IsLocked && PropertiesInt.ContainsKey(PropertyInt.ResistLockpick))
                {
                    PropertiesInt.Remove(PropertyInt.ResistLockpick);
                }

                // If wo is locked, append skill check percent, as int, to properties for id panel display on chances of success
                if (wo.IsLocked)
                {
                    var playerLockPickSkill = examiner.Skills[Skill.Lockpick].Current;

                    var doorLockPickResistance = wo.ResistLockpick;

                    var lockpickSuccessPercent = SkillCheck.GetSkillChance((int)playerLockPickSkill, (int)doorLockPickResistance) * 100;

                    if (!PropertiesInt.ContainsKey(PropertyInt.AppraisalLockpickSuccessPercent))
                    {
                        PropertiesInt.Add(PropertyInt.AppraisalLockpickSuccessPercent, (int)lockpickSuccessPercent);
                    }
                }
            }

            if (wo is Portal)
            {
                if (PropertiesInt.ContainsKey(PropertyInt.EncumbranceVal))
                {
                    PropertiesInt.Remove(PropertyInt.EncumbranceVal);
                }
            }

            if (wo is Hook)
            {
                // If the hook has any inventory, we need to send THOSE properties instead.
                var hook = wo as Container;
                if (hook.Inventory.Count == 1)
                {
                    WorldObject hookedItem = hook.Inventory.First().Value;

                    // Hooked items have a custom "description", containing the desc of the sub item and who the owner of the house is (if any)
                    generateAppraisalInfo(hookedItem, examiner, success);
                    string baseDescString = "";
                    if (wo.ParentLink.HouseOwner != null)
                    {
                        // This is for backwards compatibility. This value was not set/saved in earlier versions.
                        // It will get the player's name and save that to the HouseOwnerName property of the house. This is now done when a player purchases a house.
                        if (wo.ParentLink.HouseOwnerName == null)
                        {
                            var houseOwnerPlayer = PlayerManager.FindByGuid((uint)wo.ParentLink.HouseOwner);
                            if (houseOwnerPlayer != null)
                            {
                                wo.ParentLink.HouseOwnerName = houseOwnerPlayer.Name;
                                wo.ParentLink.SaveBiotaToDatabase();
                            }
                        }
                        baseDescString = "This hook is owned by " + wo.ParentLink.HouseOwnerName + ". "; //if house is owned, display this text
                    }
                    if (PropertiesString.ContainsKey(PropertyString.LongDesc) && PropertiesString[PropertyString.LongDesc] != null)
                    {
                        PropertiesString[PropertyString.LongDesc] = baseDescString + "It contains: \n" + PropertiesString[PropertyString.LongDesc];
                    }
                    else if (PropertiesString.ContainsKey(PropertyString.ShortDesc) && PropertiesString[PropertyString.ShortDesc] != null)
                    {
                        PropertiesString[PropertyString.LongDesc] = baseDescString + "It contains: \n" + PropertiesString[PropertyString.ShortDesc];
                    }

                    BuildHookProfile(hookedItem);
                }
            }

            BuildFlags();
        }
示例#6
0
        public void BuildProfile(WorldObject wo, Player examiner, bool success = true)
        {
            //Console.WriteLine("Appraise: " + wo.Guid);
            Success = success;

            BuildProperties(wo);
            BuildSpells(wo);

            // Help us make sure the item identify properly
            NPCLooksLikeObject = wo.GetProperty(PropertyBool.NpcLooksLikeObject) ?? false;

            if (PropertiesIID.ContainsKey(PropertyInstanceId.AllowedWielder) && !PropertiesBool.ContainsKey(PropertyBool.AppraisalHasAllowedWielder))
            {
                PropertiesBool.Add(PropertyBool.AppraisalHasAllowedWielder, true);
            }

            if (PropertiesIID.ContainsKey(PropertyInstanceId.AllowedActivator) && !PropertiesBool.ContainsKey(PropertyBool.AppraisalHasAllowedActivator))
            {
                PropertiesBool.Add(PropertyBool.AppraisalHasAllowedActivator, true);
            }

            if (PropertiesString.ContainsKey(PropertyString.ScribeAccount) && !examiner.IsAdmin && !examiner.IsSentinel && !examiner.IsEnvoy && !examiner.IsArch && !examiner.IsPsr)
            {
                PropertiesString.Remove(PropertyString.ScribeAccount);
            }

            if (PropertiesString.ContainsKey(PropertyString.HouseOwnerAccount) && !examiner.IsAdmin && !examiner.IsSentinel && !examiner.IsEnvoy && !examiner.IsArch && !examiner.IsPsr)
            {
                PropertiesString.Remove(PropertyString.HouseOwnerAccount);
            }

            if (PropertiesInt.ContainsKey(PropertyInt.Lifespan))
            {
                PropertiesInt[PropertyInt.RemainingLifespan] = wo.GetRemainingLifespan();
            }

            if (PropertiesInt.TryGetValue(PropertyInt.Faction1Bits, out var faction1Bits))
            {
                // hide any non-default factions, prevent client from displaying ???
                // this is only needed for non-standard faction creatures that use templates, to hide the ??? in the client
                var sendBits = faction1Bits & (int)FactionBits.ValidFactions;
                if (sendBits != faction1Bits)
                {
                    if (sendBits != 0)
                    {
                        PropertiesInt[PropertyInt.Faction1Bits] = sendBits;
                    }
                    else
                    {
                        PropertiesInt.Remove(PropertyInt.Faction1Bits);
                    }
                }
            }

            // armor / clothing / shield
            if (wo is Clothing || wo.IsShield)
            {
                BuildArmor(wo);
            }

            if (wo is Creature creature)
            {
                BuildCreature(creature);
            }

            if (wo.Damage != null && !(wo is Clothing) || wo is MeleeWeapon || wo is Missile || wo is MissileLauncher || wo is Ammunition || wo is Caster)
            {
                BuildWeapon(wo);
            }

            // TODO: Resolve this issue a better way?
            // Because of the way ACE handles default base values in recipe system (or rather the lack thereof)
            // we need to check the following weapon properties to see if they're below expected minimum and adjust accordingly
            // The issue is that the recipe system likely added 0.005 to 0 instead of 1, which is what *should* have happened.
            if (wo.WeaponMagicDefense.HasValue && wo.WeaponMagicDefense.Value > 0 && wo.WeaponMagicDefense.Value < 1 && ((wo.GetProperty(PropertyInt.ImbueStackingBits) ?? 0) & 1) != 0)
            {
                PropertiesFloat[PropertyFloat.WeaponMagicDefense] += 1;
            }
            if (wo.WeaponMissileDefense.HasValue && wo.WeaponMissileDefense.Value > 0 && wo.WeaponMissileDefense.Value < 1 && ((wo.GetProperty(PropertyInt.ImbueStackingBits) ?? 0) & 1) != 0)
            {
                PropertiesFloat[PropertyFloat.WeaponMissileDefense] += 1;
            }

            if (wo is Door || wo is Chest)
            {
                // If wo is not locked, do not send ResistLockpick value. If ResistLockpick is sent for unlocked objects, id panel shows bonus to Lockpick skill
                if (!wo.IsLocked && PropertiesInt.ContainsKey(PropertyInt.ResistLockpick))
                {
                    PropertiesInt.Remove(PropertyInt.ResistLockpick);
                }

                // If wo is locked, append skill check percent, as int, to properties for id panel display on chances of success
                if (wo.IsLocked)
                {
                    var resistLockpick = LockHelper.GetResistLockpick(wo);

                    if (resistLockpick != null)
                    {
                        PropertiesInt[PropertyInt.ResistLockpick] = (int)resistLockpick;

                        var pickSkill = examiner.Skills[Skill.Lockpick].Current;

                        var successChance = SkillCheck.GetSkillChance((int)pickSkill, (int)resistLockpick) * 100;

                        if (!PropertiesInt.ContainsKey(PropertyInt.AppraisalLockpickSuccessPercent))
                        {
                            PropertiesInt.Add(PropertyInt.AppraisalLockpickSuccessPercent, (int)successChance);
                        }
                    }
                }
                // if wo has DefaultLocked property and is unlocked, add that state to the property buckets
                else if (PropertiesBool.ContainsKey(PropertyBool.DefaultLocked))
                {
                    PropertiesBool[PropertyBool.Locked] = false;
                }
            }

            if (wo is Corpse)
            {
                PropertiesBool.Clear();
                PropertiesDID.Clear();
                PropertiesFloat.Clear();
                PropertiesInt64.Clear();

                var discardInts = PropertiesInt.Where(x => x.Key != PropertyInt.EncumbranceVal && x.Key != PropertyInt.Value).Select(x => x.Key).ToList();
                foreach (var key in discardInts)
                {
                    PropertiesInt.Remove(key);
                }
                var discardString = PropertiesString.Where(x => x.Key != PropertyString.LongDesc).Select(x => x.Key).ToList();
                foreach (var key in discardString)
                {
                    PropertiesString.Remove(key);
                }

                PropertiesInt[PropertyInt.Value] = 0;
            }

            if (wo is Portal)
            {
                if (PropertiesInt.ContainsKey(PropertyInt.EncumbranceVal))
                {
                    PropertiesInt.Remove(PropertyInt.EncumbranceVal);
                }
            }

            if (wo is SlumLord slumLord)
            {
                PropertiesBool.Clear();
                PropertiesDID.Clear();
                PropertiesFloat.Clear();
                PropertiesIID.Clear();
                //PropertiesInt.Clear();
                PropertiesInt64.Clear();
                PropertiesString.Clear();

                var longDesc = "";

                if (slumLord.HouseOwner.HasValue && slumLord.HouseOwner.Value > 0)
                {
                    longDesc = $"The current maintenance has {(slumLord.IsRentPaid() || !PropertyManager.GetBool("house_rent_enabled").Item ? "" : "not ")}been paid.\n";

                    PropertiesInt.Clear();
                }
                else
                {
                    //longDesc = $"This house is {(slumLord.HouseStatus == HouseStatus.Disabled ? "not " : "")}available for purchase.\n"; // this was the retail msg.
                    longDesc = $"This {(slumLord.House.HouseType == HouseType.Undef ? "house" : slumLord.House.HouseType.ToString().ToLower())} is {(slumLord.House.HouseStatus == HouseStatus.Disabled ? "not " : "")}available for purchase.\n";

                    var discardInts = PropertiesInt.Where(x => x.Key != PropertyInt.HouseStatus && x.Key != PropertyInt.HouseType && x.Key != PropertyInt.MinLevel && x.Key != PropertyInt.MaxLevel && x.Key != PropertyInt.AllegianceMinLevel && x.Key != PropertyInt.AllegianceMaxLevel).Select(x => x.Key).ToList();
                    foreach (var key in discardInts)
                    {
                        PropertiesInt.Remove(key);
                    }
                }

                if (slumLord.HouseRequiresMonarch)
                {
                    longDesc += "You must be a monarch to purchase and maintain this dwelling.\n";
                }

                if (slumLord.AllegianceMinLevel.HasValue)
                {
                    var allegianceMinLevel = PropertyManager.GetLong("mansion_min_rank", -1).Item;
                    if (allegianceMinLevel == -1)
                    {
                        allegianceMinLevel = slumLord.AllegianceMinLevel.Value;
                    }

                    longDesc += $"Restricted to characters of allegiance rank {allegianceMinLevel} or greater.\n";
                }

                PropertiesString.Add(PropertyString.LongDesc, longDesc);
            }

            if (wo is Storage)
            {
                var longDesc = "";

                if (wo.HouseOwner.HasValue && wo.HouseOwner.Value > 0)
                {
                    longDesc = $"Owned by {wo.ParentLink.HouseOwnerName}\n";
                }

                var discardString = PropertiesString.Where(x => x.Key != PropertyString.Use).Select(x => x.Key).ToList();
                foreach (var key in discardString)
                {
                    PropertiesString.Remove(key);
                }

                PropertiesString.Add(PropertyString.LongDesc, longDesc);

                if (PropertiesInt.ContainsKey(PropertyInt.Value))
                {
                    PropertiesInt[PropertyInt.Value] = wo.Biota.GetProperty(PropertyInt.Value, wo.BiotaDatabaseLock) ?? 200; // Value is masked to base value of Storage
                }
            }

            if (wo is Hook)
            {
                // If the hook has any inventory, we need to send THOSE properties instead.
                var hook = wo as Container;

                string baseDescString = "";
                if (wo.ParentLink.HouseOwner != null)
                {
                    // This is for backwards compatibility. This value was not set/saved in earlier versions.
                    // It will get the player's name and save that to the HouseOwnerName property of the house. This is now done when a player purchases a house.
                    if (wo.ParentLink.HouseOwnerName == null)
                    {
                        var houseOwnerPlayer = PlayerManager.FindByGuid((uint)wo.ParentLink.HouseOwner);
                        if (houseOwnerPlayer != null)
                        {
                            wo.ParentLink.HouseOwnerName = houseOwnerPlayer.Name;
                            wo.ParentLink.SaveBiotaToDatabase();
                        }
                    }
                    baseDescString = "This hook is owned by " + wo.ParentLink.HouseOwnerName + ". "; //if house is owned, display this text
                }

                var containsString = "";
                if (hook.Inventory.Count == 1)
                {
                    WorldObject hookedItem = hook.Inventory.First().Value;

                    // Hooked items have a custom "description", containing the desc of the sub item and who the owner of the house is (if any)
                    BuildProfile(hookedItem, examiner, success);

                    containsString = "It contains: \n";

                    if (!string.IsNullOrWhiteSpace(hookedItem.LongDesc))
                    {
                        containsString += hookedItem.LongDesc;
                    }
                    //else if (PropertiesString.ContainsKey(PropertyString.ShortDesc) && PropertiesString[PropertyString.ShortDesc] != null)
                    //{
                    //    containsString += PropertiesString[PropertyString.ShortDesc];
                    //}
                    else
                    {
                        containsString += hookedItem.Name;
                    }

                    BuildHookProfile(hookedItem);
                }

                //if (PropertiesString.ContainsKey(PropertyString.LongDesc) && PropertiesString[PropertyString.LongDesc] != null)
                //    PropertiesString[PropertyString.LongDesc] = baseDescString + containsString;
                ////else if (PropertiesString.ContainsKey(PropertyString.ShortDesc) && PropertiesString[PropertyString.ShortDesc] != null)
                ////    PropertiesString[PropertyString.LongDesc] = baseDescString + containsString;
                //else
                //    PropertiesString[PropertyString.LongDesc] = baseDescString + containsString;

                PropertiesString[PropertyString.LongDesc] = baseDescString + containsString;

                PropertiesInt.Remove(PropertyInt.Structure);

                // retail should have removed this property and then server side built the same result for the hook longdesc replacement but didn't and ends up with some odd looking appraisals as seen on video/pcaps
                //PropertiesInt.Remove(PropertyInt.AppraisalLongDescDecoration);
            }

            if (wo is ManaStone)
            {
                var useMessage = "";

                if (wo.ItemCurMana.HasValue)
                {
                    useMessage = "Use on a magic item to give the stone's stored Mana to that item.";
                }
                else
                {
                    useMessage = "Use on a magic item to destroy that item and drain its Mana.";
                }

                PropertiesString[PropertyString.Use] = useMessage;
            }

            if (wo is CraftTool && (wo.ItemType == ItemType.TinkeringMaterial || wo.WeenieClassId >= 36619 && wo.WeenieClassId <= 36628 || wo.WeenieClassId >= 36634 && wo.WeenieClassId <= 36636))
            {
                if (PropertiesInt.ContainsKey(PropertyInt.Structure))
                {
                    PropertiesInt.Remove(PropertyInt.Structure);
                }
            }

            if (!Success)
            {
                // todo: what specifically to keep/what to clear

                //PropertiesBool.Clear();
                //PropertiesDID.Clear();
                //PropertiesFloat.Clear();
                //PropertiesIID.Clear();
                //PropertiesInt.Clear();
                //PropertiesInt64.Clear();
                //PropertiesString.Clear();
            }

            BuildFlags();
        }