diff --git a/TsRandomizer/Extensions/Helper.cs b/TsRandomizer/Extensions/Helper.cs index 3b320a23..9c7ea62f 100644 --- a/TsRandomizer/Extensions/Helper.cs +++ b/TsRandomizer/Extensions/Helper.cs @@ -14,12 +14,111 @@ public static EInventoryOrbType[] GetAllOrbs() .Where(o => o != EInventoryOrbType.None && o != EInventoryOrbType.Monske) .ToArray(); } + public static ELootTier LookupUseItemRarity(EInventoryUseItemType itemType) + { + switch (itemType) + { + case EInventoryUseItemType.CheveuxFeather: + case EInventoryUseItemType.CheveuxBreast: + case EInventoryUseItemType.Drumstick: + case EInventoryUseItemType.EelMeat: + case EInventoryUseItemType.Herb: + case EInventoryUseItemType.WyvernTail: + case EInventoryUseItemType.PlumpMaggot: + case EInventoryUseItemType.RottenTail: + case EInventoryUseItemType.SilverOre: + case EInventoryUseItemType.SirenInk: + case EInventoryUseItemType.PlasmaCore: + case EInventoryUseItemType.PlaceHolderItem1: + return ELootTier.Trash; + case EInventoryUseItemType.Potion: + case EInventoryUseItemType.Ether: + case EInventoryUseItemType.SandBottle: + case EInventoryUseItemType.FuturePotion: + case EInventoryUseItemType.FutureEther: + case EInventoryUseItemType.Antidote: + case EInventoryUseItemType.ChaosHeal: + case EInventoryUseItemType.Jerky: + return ELootTier.Common; + case EInventoryUseItemType.HiPotion: + case EInventoryUseItemType.HiSandBottle: + case EInventoryUseItemType.HiEther: + case EInventoryUseItemType.FutureHiPotion: + case EInventoryUseItemType.FutureHiEther: + case EInventoryUseItemType.FamiliarTreat: + case EInventoryUseItemType.OrangeJuice: + return ELootTier.Uncommon; + case EInventoryUseItemType.WarpCard: + case EInventoryUseItemType.CheveuxAuVin: + case EInventoryUseItemType.FriedCheveux: + case EInventoryUseItemType.Biscuit: + case EInventoryUseItemType.FiligreeTea: + case EInventoryUseItemType.MagicMarbles: + case EInventoryUseItemType.Spaghetti: + case EInventoryUseItemType.UnagiRoll: + return ELootTier.Rare; + case EInventoryUseItemType.LachiemiSun: + case EInventoryUseItemType.EmpressCake: + case EInventoryUseItemType.Casserole: + return ELootTier.UltraRare; + default: + return ELootTier.Uncommon; + } + } + public static ELootTier LookupEquipmentRarity(EInventoryEquipmentType equipmentType) + { + switch (equipmentType) + { + case EInventoryEquipmentType.OldCoat: + case EInventoryEquipmentType.Sunglasses: + case EInventoryEquipmentType.BuckleHat: + case EInventoryEquipmentType.MetalWristband: + case EInventoryEquipmentType.MidnightCloak: + case EInventoryEquipmentType.MotherOfPearl: + return ELootTier.Trash; + case EInventoryEquipmentType.PointyHat: + case EInventoryEquipmentType.AdvisorHat: + case EInventoryEquipmentType.AdvisorRobe: + case EInventoryEquipmentType.CalvaryArmor: + case EInventoryEquipmentType.CalvaryHelmet: + case EInventoryEquipmentType.CaptainsCap: + case EInventoryEquipmentType.CaptainsJacket: + case EInventoryEquipmentType.LeatherArmor: + case EInventoryEquipmentType.LeatherHelmet: + return ELootTier.Common; + case EInventoryEquipmentType.SecurityVisor: + case EInventoryEquipmentType.SecurityVest: + case EInventoryEquipmentType.LibrarianHat: + case EInventoryEquipmentType.LibrarianRobe: + case EInventoryEquipmentType.EngineerGoggles: + case EInventoryEquipmentType.SirenHairband: + return ELootTier.Uncommon; + case EInventoryEquipmentType.LabGlasses: + case EInventoryEquipmentType.LabCoat: + case EInventoryEquipmentType.LachiemCrown: + case EInventoryEquipmentType.LuckyCoin: + case EInventoryEquipmentType.DemonHorn: + case EInventoryEquipmentType.FiligreeClasp: + case EInventoryEquipmentType.VileteCrown: + case EInventoryEquipmentType.VileteDress: + return ELootTier.Rare; + case EInventoryEquipmentType.Pendulum: + case EInventoryEquipmentType.BirdStatue: + case EInventoryEquipmentType.DemonStole: + case EInventoryEquipmentType.AzureStole: + case EInventoryEquipmentType.EmpressCoat: + case EInventoryEquipmentType.NelisteEarring: + case EInventoryEquipmentType.ShinyRock: + return ELootTier.UltraRare; + default: + return ELootTier.Uncommon; + } + } public static List GetAllLoot() { // Exclude unique and placeholder objects var useItems = ((EInventoryUseItemType[])Enum.GetValues(typeof(EInventoryUseItemType))) .Where(o => o != EInventoryUseItemType.None - && o != EInventoryUseItemType.PlaceHolderItem1 && o != EInventoryUseItemType.AlchemistTools && o != EInventoryUseItemType.MagicMarbles && o != EInventoryUseItemType.EssenceCrystal @@ -41,6 +140,8 @@ public static List GetAllLoot() && o != EInventoryEquipmentType.SelenBangle && o != EInventoryEquipmentType.ShinyRock && o != EInventoryEquipmentType.GlassPumpkin + && o != EInventoryEquipmentType.EternalCoat + && o != EInventoryEquipmentType.EternalTiara ) .ToArray(); List loot = new List(); diff --git a/TsRandomizer/IntermediateObjects/ItemIdentifier.cs b/TsRandomizer/IntermediateObjects/ItemIdentifier.cs index 1a73c2a5..cb6f3c6e 100644 --- a/TsRandomizer/IntermediateObjects/ItemIdentifier.cs +++ b/TsRandomizer/IntermediateObjects/ItemIdentifier.cs @@ -7,6 +7,14 @@ namespace TsRandomizer.IntermediateObjects { + public enum ELootTier + { + Trash = 16, + Common = 10, + Uncommon = 6, + Rare = 3, + UltraRare = 2, + } public class ItemIdentifier : IEquatable { public LootType LootType { get; } diff --git a/TsRandomizer/Randomisation/BestiaryManager.cs b/TsRandomizer/Randomisation/BestiaryManager.cs index 790588d7..5d3493a6 100644 --- a/TsRandomizer/Randomisation/BestiaryManager.cs +++ b/TsRandomizer/Randomisation/BestiaryManager.cs @@ -71,6 +71,8 @@ enum EMinionID ShieldKnight } + + static class BestiaryManager { public static int[] GetValidBosses(Level level) @@ -654,17 +656,67 @@ public static void UpdateBestiary(Level level, SettingCollection gameSettings) } } } + bestiaryEntry.VisibleDescription = $"ATK - {bestiaryEntry.TouchDamage}"; int dropSlot = 0; foreach (var loot in bestiaryEntry.LootTable) { + var item = Helper.GetAllLoot().SelectRandom(random); + switch (gameSettings.DropRateCategory.Value) + { + case "Fixed": + loot.DropRate = (int)gameSettings.DropRate.Value; + break; + case "Tiered": + if (item.LootType == LootType.Equipment) + loot.DropRate = (int)Helper.LookupEquipmentRarity((EInventoryEquipmentType)item.ItemId); + else + loot.DropRate = (int)Helper.LookupUseItemRarity((EInventoryUseItemType)item.ItemId); + break; + case "Random": + Array lootTiers = Enum.GetValues(typeof(ELootTier)); + if (gameSettings.LootTierDistro.Value == "Full Random") + loot.DropRate = (int)lootTiers.GetValue(random.Next(lootTiers.Length)); + else + { + int offset = 0; + if (gameSettings.LootTierDistro.Value == "Inverted Weight") + offset = 4; + int rarityRoll = random.Next(21); + switch (rarityRoll) + { + case int roll when (roll >= 20): + loot.DropRate = (int)lootTiers.GetValue(Math.Abs(4 - offset)); + break; + case int roll when (roll >= 16): + loot.DropRate = (int)lootTiers.GetValue(Math.Abs(3 - offset)); + break; + case int roll when (roll >= 13): + loot.DropRate = (int)lootTiers.GetValue(Math.Abs(2 - offset)); + break; + case int roll when (roll >= 8): + loot.DropRate = (int)lootTiers.GetValue(Math.Abs(1 - offset)); + break; + case int roll when (roll >= 0): + loot.DropRate = (int)lootTiers.GetValue(Math.Abs(0 - offset)); + break; + default: + loot.DropRate = (int)ELootTier.Uncommon; + break; + } + } + break; + case "Vanilla": + default: + break; + } + bestiaryEntry.VisibleDescription += $"\nItem {dropSlot + 1} base drop rate: {loot.DropRate}%"; if (gameSettings.ShowDrops.Value) { level.GameSave.SetValue(string.Format(bestiaryEntry.Key.Replace("Enemy_", "DROP_" + dropSlot + "_")), true); } if (gameSettings.LootPool.Value == "Random") { - var item = Helper.GetAllLoot().SelectRandom(random); loot.Item = item.ItemId; if (item.LootType == LootType.Equipment) loot.Category = (int)EInventoryCategoryType.Equipment; diff --git a/TsRandomizer/Randomisation/ItemPlacers/ItemLocationRandomizer.cs b/TsRandomizer/Randomisation/ItemPlacers/ItemLocationRandomizer.cs index 8c10580f..052a8a20 100644 --- a/TsRandomizer/Randomisation/ItemPlacers/ItemLocationRandomizer.cs +++ b/TsRandomizer/Randomisation/ItemPlacers/ItemLocationRandomizer.cs @@ -74,7 +74,8 @@ protected ItemLocationRandomizer(Seed seed, ItemInfoProvider itemInfoProvider, I ItemInfoProvider.Get(EInventoryUseItemType.ChaosHeal), ItemInfoProvider.Get(EInventoryUseItemType.Antidote), ItemInfoProvider.Get(EInventoryUseItemType.SandBottle), - ItemInfoProvider.Get(EInventoryUseItemType.HiSandBottle) + ItemInfoProvider.Get(EInventoryUseItemType.HiSandBottle), + ItemInfoProvider.Get(EInventoryUseItemType.PlaceHolderItem1) // "Nothing"/empty chest }; } diff --git a/TsRandomizer/Screens/SaveSelectScreen.cs b/TsRandomizer/Screens/SaveSelectScreen.cs index 094f853a..2ecbc397 100644 --- a/TsRandomizer/Screens/SaveSelectScreen.cs +++ b/TsRandomizer/Screens/SaveSelectScreen.cs @@ -36,6 +36,7 @@ class SaveSelectScreen : Screen public SaveSelectScreen(ScreenManager screenManager, GameScreen screen) : base(screenManager, screen) { var saveFileEntries = (IList)((object)Dynamic._saveFileCollection).AsDynamic().Entries; + TimeSpinnerGame.Localizer.OverrideKey("inv_use_PlaceHolderItem1", "Nothing"); foreach (var entry in saveFileEntries) { diff --git a/TsRandomizer/Settings/SettingCollection.cs b/TsRandomizer/Settings/SettingCollection.cs index f1fcb013..2960e4a3 100644 --- a/TsRandomizer/Settings/SettingCollection.cs +++ b/TsRandomizer/Settings/SettingCollection.cs @@ -13,7 +13,7 @@ public class SettingCollection }}, new GameSettingCategoryInfo { Name = "Loot", Description = "Settings related to shop inventory and loot.", SettingsPerCategory = new List> { - s => s.ShopFill, s => s.ShopMultiplier, s => s.ShopWarpShards, s => s.LootPool + s => s.ShopFill, s => s.ShopMultiplier, s => s.ShopWarpShards, s => s.LootPool, s => s.DropRateCategory, s => s.DropRate, s => s.LootTierDistro }}, new GameSettingCategoryInfo { Name = "Minimap", Description = "Settings related to minimap colors.", SettingsPerCategory = new List> { @@ -61,6 +61,16 @@ public class SettingCollection "Sets which items enemies will drop: [Vanilla, Random, Empty]", new List { "Vanilla", "Random", "Empty" }, "Vanilla", true); + public SpecificValuesGameSetting DropRateCategory = new SpecificValuesGameSetting("Drop Rate Category", + "Sets the drop rate of enemy items [Tiered (based on item), Vanilla (based on enemy), Random (2-12%), Fixed", + new List { "Tiered", "Vanilla", "Random", "Fixed" }, "Tiered", true); + + public NumberGameSetting DropRate = new NumberGameSetting("Fixed Drop Rate", + "Drop rate to be used when Drop Rate Category set to 'Fixed'", 0, 100, 2.5, 5); + + public SpecificValuesGameSetting LootTierDistro = new SpecificValuesGameSetting("Loot Tier Distribution", + "Sets how frequently items of each loot rarity tier will be assigned to a drop slot.", + new List { "Default Weight", "Full Random", "Inverted Weight" }, "Default Weight", true); public SpecificValuesGameSetting ShopFill = new SpecificValuesGameSetting("Shop Inventory", "Sets the items for sale in Merchant Crow's shops. Options: [Default,Random,Vanilla,Empty]",