private void ReplaceTreasures(IEnumerable <Item> items) { var treasures = new TreasureData(rom); treasures.LoadTable(); var pool1 = GetLegendaryPool(); var pool2 = ItemLists.RareWeaponTier.Concat(ItemLists.RareArmorTier).Where(i => !pool1.Contains(i)).ToList(); var pool3 = ItemLists.CommonArmorTier.Concat(ItemLists.CommonWeaponTier).ToList(); var hqPool = new HashSet <Item>(pool1.Concat(pool2)); items = items.Where(i => hqPool.Contains(i)).ToArray(); foreach (var i in items) { var indices = treasures.Data.Select((item, idx) => (item, idx)).Where(e => e.item == i).ToList(); if (indices.Count > 0) { var idx = indices.PickRandom(rng).idx; treasures[idx] = ExtConsumables.ExtConsumableStartingEquipmentFix(pool3.PickRandom(rng), flags); } } treasures.StoreTable(); }
public void SetStartingEquipment() { var items = GetEquipment(); ExtConsumables.ExtConsumableStartingEquipmentFix(items.Weapons, flags); if (flags.StartingEquipmentRemoveFromPool) { ReplaceTreasures(items.Weapons.Concat(items.Armor)); } for (int i = 0; i < 16; i++) { if (items.Weapons[i] > 0) { items.Weapons[i] -= Item.Soft; } if (items.Armor[i] > 0) { items.Armor[i] -= Item.Masamune; } } for (int i = 0; i < 4; i++) { byte[] buffer1 = new byte[4]; Buffer.BlockCopy(items.Weapons, 4 * i, buffer1, 0, 4); byte[] buffer2 = new byte[4]; Buffer.BlockCopy(items.Armor, 4 * i, buffer2, 0, 4); rom.PutInBank(0x1B, 0x8520 + 0x08 * i, buffer1); rom.PutInBank(0x1B, 0x8524 + 0x08 * i, buffer2); } rom.PutInBank(0x1B, 0x8540, Blob.FromHex("A200A000205785A040205785A080205785A0C020578560BD2085991861E8C898290FC908D0F160")); }
// Scale is the geometric scale factor used with RNG. Multiplier is where we make everything cheaper // instead of enemies giving more gold, so we don't overflow. public void ScalePrices(IScaleFlags flags, MT19337 rng, bool increaseOnly, ItemShopSlot shopItemLocation, bool FreeClinic = false) { IEnumerable <Item> tmpExcludedItems = Array.Empty <Item>(); if (flags.ExcludeGoldFromScaling ?? false) { tmpExcludedItems = tmpExcludedItems.Concat(ItemLists.AllGoldTreasure); } if ((flags.ExcludeGoldFromScaling ?? false) && flags.CheapVendorItem) { tmpExcludedItems = tmpExcludedItems.Concat(ItemLists.AllQuestItems); } HashSet <Item> excludedItems = new HashSet <Item>(tmpExcludedItems); HashSet <Item> questItems = new HashSet <Item>(ItemLists.AllQuestItems); int rawScaleLow = increaseOnly ? 100 : flags.PriceScaleFactorLow; int rawScaleHigh = increaseOnly ? Math.Max(100, flags.PriceScaleFactorHigh) : flags.PriceScaleFactorHigh; double scaleLow = (double)rawScaleLow / 100.0; double scaleHigh = (double)rawScaleHigh / 100.0; var multiplier = flags.ExcludeGoldFromScaling ?? false ? 1.0 : flags.ExpMultiplier; var prices = Get(PriceOffset, PriceSize * PriceCount).ToUShorts(); for (int i = 0; i < prices.Length; i++) { if (excludedItems.Contains((Item)i)) { var price = (int)prices[i]; if (flags.CheapVendorItem && questItems.Contains((Item)i)) { price = 20000; } var newPrice = price + rng.Between(-price / 10, price / 10); prices[i] = (ushort)Math.Min(Math.Max(newPrice, 1), 65535); } else { var price = ExtConsumables.ExtConsumablePriceFix((Item)i, prices[i], flags); var newPrice = RangeScaleWithZero(price / multiplier, scaleLow, scaleHigh, 1e-5 * multiplier, 1, rng); prices[i] = (ushort)Min(newPrice, 0xFFFF); } } var questItemPrice = prices[(int)Item.Bottle]; // If we don't do this before checking for the item shop location factor, Ribbons and Shirts will end up being really cheap // This realistically doesn't matter without Shop Wares shuffle on because nobody wants to sell Ribbons/Shirts, but if it is... prices[(int)Item.WhiteShirt] = (ushort)(questItemPrice / 2); prices[(int)Item.BlackShirt] = (ushort)(questItemPrice / 2); prices[(int)Item.Ribbon] = questItemPrice; var itemShopFactor = new Dictionary <MapLocation, int>() { { MapLocation.Coneria, 8 }, { MapLocation.Pravoka, 2 } }; if (itemShopFactor.TryGetValue(shopItemLocation.MapLocation, out int divisor)) { questItemPrice = (ushort)(prices[(int)Item.Bottle] / divisor); } for (var i = 0; i < (int)Item.Tent; i++) { prices[i] = questItemPrice; } Put(PriceOffset, Blob.FromUShorts(prices)); for (int i = GoldItemOffset; i < GoldItemOffset + GoldItemCount; i++) { ItemsText[i] = prices[i].ToString() + " G"; } var pointers = Get(ShopPointerOffset, ShopPointerCount * ShopPointerSize).ToUShorts(); RepackShops(pointers); for (int i = (int)ShopType.Clinic; i < (int)ShopType.Inn + ShopSectionSize; i++) { if (pointers[i] != ShopNullPointer) { var priceBytes = Get(ShopPointerBase + pointers[i], 2); var priceValue = BitConverter.ToUInt16(priceBytes, 0); priceValue = (ushort)RangeScale(priceValue / multiplier, scaleLow, scaleHigh, 1, rng); if (FreeClinic && i < (int)ShopType.Clinic + ShopSectionSize) { priceValue = 0; } priceBytes = BitConverter.GetBytes(priceValue); Put(ShopPointerBase + pointers[i], priceBytes); } } List <(StartingGold, ushort)> startingGold = new() { (StartingGold.None, 0), (StartingGold.Gp100, 100), (StartingGold.Gp200, 200), (StartingGold.Gp400, 400), (StartingGold.Gp800, 800), (StartingGold.Gp2500, 2500), (StartingGold.Gp9999, 9999), (StartingGold.Gp65535, 65535), (StartingGold.RandomLow, (ushort)rng.Between(0, 800)), (StartingGold.RandomHigh, (ushort)rng.Between(0, 65535)), }; Put(StartingGoldOffset, BitConverter.GetBytes(startingGold[(int)flags.StartingGold].Item2)); }