Example #1
0
 protected RewardSourceBase(IRewardSource copyFromRewardSource, Item item)
 {
     Address           = copyFromRewardSource.Address;
     Name              = copyFromRewardSource.Name;
     Item              = item;
     MapLocation       = copyFromRewardSource.MapLocation;
     AccessRequirement = copyFromRewardSource.AccessRequirement;
     IsUnused          = false;
 }
Example #2
0
        private void MoveShipToRewardSource(IRewardSource source)
        {
            Blob location = null;

            if (!ItemLocations.ShipLocations.TryGetValue(source.MapLocation, out location))
            {
                location = Dock.Coneria;
            }

            Put(0x3000 + UnsramIndex.ShipX, location);
        }
Example #3
0
 public static IRewardSource NewItemPlacement(IRewardSource copyFromSource, Item newItem)
 {
     if (copyFromSource is MapObject)
     {
         return(new MapObject(copyFromSource as MapObject, newItem));
     }
     else
     {
         return(new TreasureChest(copyFromSource, newItem));
     }
 }
Example #4
0
        public static List <IRewardSource> PlaceSaneItems(MT19337 rng,
                                                          ITreasureShuffleFlags flags,
                                                          IncentiveData incentivesData,
                                                          List <Item> allTreasures,
                                                          ItemShopSlot caravanItemLocation,
                                                          Dictionary <MapLocation, List <MapChange> > mapLocationRequirements)
        {
            long sanityCounter = 0;
            List <IRewardSource> placedItems;

            var incentiveLocationPool = incentivesData.IncentiveLocations.ToList();
            var incentivePool         = incentivesData.IncentiveItems.ToList();
            var forcedItems           = incentivesData.ForcedItemPlacements.ToList();
            var bridgeLocations       = incentivesData.BridgeLocations.ToList();
            var shipLocations         = incentivesData.ShipLocations.ToList();
            var itemLocationPool      = incentivesData.AllValidItemLocations.ToList();

            var unincentivizedQuestItems =
                ItemLists.AllQuestItems
                .Where(x => !incentivePool.Contains(x) &&
                       x != Item.Ship && x != Item.Bridge && x != Item.Bottle &&
                       !forcedItems.Any(y => y.Item == x)).ToList();

            var treasurePool = allTreasures.ToList();

            treasurePool.Remove(Item.Bridge);
            treasurePool.Remove(Item.Ship);

            foreach (var incentive in incentivePool)
            {
                treasurePool.Remove(incentive);
            }
            foreach (var placement in forcedItems)
            {
                treasurePool.Remove(placement.Item);
            }
            foreach (var questItem in unincentivizedQuestItems)
            {
                treasurePool.Remove(questItem);
            }
            while (treasurePool.Remove(Item.Shard))
            {
                unincentivizedQuestItems.Add(Item.Shard);
            }
            do
            {
                sanityCounter++;
                if (sanityCounter > 10000)
                {
                    throw new InvalidOperationException("Invalid flag set");
                }
                // 1. (Re)Initialize lists inside of loop
                placedItems = forcedItems.ToList();
                var incentives = incentivePool.ToList();
                incentives.Shuffle(rng);

                if (flags.NPCItems)
                {
                    // 2. Place caravan item first because among incentive locations it has the smallest set of possible items
                    if (!placedItems.Any(x => x.Address == ItemLocations.CaravanItemShop1.Address))
                    {
                        var itemPick            = Item.Bottle;
                        var validShopIncentives = incentives
                                                  .Where(x => x > Item.None && x <= Item.Soft)
                                                  .ToList();
                        if (validShopIncentives.Any())
                        {
                            itemPick = validShopIncentives.PickRandom(rng);
                            incentives.Remove(itemPick);
                        }
                        else if (placedItems.Any(x => x.Item == Item.Bottle))
                        {
                            itemPick = ItemLists.AllConsumables.ToList().PickRandom(rng);
                        }
                        else
                        {
                            treasurePool.Remove(Item.Bottle);
                        }

                        placedItems.Add(new ItemShopSlot(caravanItemLocation, itemPick));
                    }

                    // 3. Place Bridge and Ship next since the valid location lists are so small
                    IRewardSource bridgePlacement = bridgeLocations.PickRandom(rng);
                    placedItems.Add(NewItemPlacement(bridgePlacement, Item.Bridge));

                    var shipPlacement =
                        shipLocations
                        .Where(x => x.Address != bridgePlacement.Address)
                        .ToList().PickRandom(rng);
                    placedItems.Add(NewItemPlacement(shipPlacement, Item.Ship));
                }

                // 4. Then place all incentive locations that don't have special logic
                foreach (var incentiveLocation in incentiveLocationPool.Where(x => !placedItems.Any(y => y.Address == x.Address)))
                {
                    if (!incentives.Any())
                    {
                        break;
                    }
                    placedItems.Add(NewItemPlacement(incentiveLocation, incentives.SpliceRandom(rng)));
                }

                // 5. Then place remanining incentive items and unincentivized quest items in any other chest before ToFR
                var leftoverItems = incentives.Concat(unincentivizedQuestItems).ToList();
                leftoverItems.Shuffle(rng);
                var leftoverItemLocations =
                    itemLocationPool
                    .Where(x => !ItemLocations.ToFR.Any(y => y.Address == x.Address) &&
                           !x.IsUnused && !placedItems.Any(y => y.Address == x.Address))
                    .ToList();
                foreach (var leftoverItem in leftoverItems)
                {
                    placedItems.Add(NewItemPlacement(leftoverItemLocations.SpliceRandom(rng), leftoverItem));
                }

                // 6. Check sanity and loop if needed
            } while (!CheckSanity(placedItems, flags, mapLocationRequirements));

            // 7. Place all remaining unincentivized treasures or incentivized non-quest items that weren't placed
            var i = 0;

            itemLocationPool =
                itemLocationPool
                .Where(x => !x.IsUnused && !placedItems.Any(y => y.Address == x.Address))
                .ToList();
            foreach (var placedItem in placedItems)
            {
                incentivePool.Remove(placedItem.Item);
            }
            foreach (var unplacedIncentive in incentivePool)
            {
                treasurePool.Add(unplacedIncentive);
            }
            treasurePool.Shuffle(rng);
            foreach (var remainingTreasure in itemLocationPool)
            {
                placedItems.Add(NewItemPlacement(remainingTreasure, treasurePool[i]));
                i++;
            }

            //Debug.WriteLine($"Sanity Check Fails: {sanityCounter}");
            return(placedItems);
        }
Example #5
0
        public IEnumerable <IRewardSource> GetNearRewardSources(IEnumerable <IRewardSource> sources, IRewardSource current)
        {
            if (current is TreasureChest c)
            {
                var chests = sources.Select(r => r as TreasureChest).Where(r => r != null).ToDictionary(r => (byte)(r.Address - 0x3100));
                var chest  = (byte)(c.Address - 0x3100);

                foreach (var dungeon in Main.Dungeons)
                {
                    var poi = dungeon.PointsOfInterest.Where(p => p.Type == SCPointOfInterestType.Treasure).FirstOrDefault(p => p.TreasureId == chest);

                    if (poi != null)
                    {
                        return(dungeon.PointsOfInterest.Where(p => p.Type == SCPointOfInterestType.Treasure)
                               .Where(p => p.BitFlagSet.ToString() == poi.BitFlagSet.ToString())
                               .Where(p => chests.ContainsKey(p.TreasureId))
                               .Select(p => chests[p.TreasureId]).ToList());
                    }
                }
            }

            return(Array.Empty <IRewardSource>());
        }
Example #6
0
        public bool IsRewardSourceAccessible(IRewardSource source, AccessRequirement currentAccess, List <MapLocation> locations)
        {
            //if (currentAccess != requirements) throw new InvalidOperationException("no can do");

            return(rewardSources.Contains(source));
        }
Example #7
0
 public IEnumerable <IRewardSource> GetNearRewardSources(IEnumerable <IRewardSource> sources, IRewardSource source)
 {
     return(Array.Empty <IRewardSource>());
 }
Example #8
0
 public bool IsRewardSourceAccessible(IRewardSource source, AccessRequirement currentAccess, List <MapLocation> locations)
 {
     return(locations.Contains(source.MapLocation) && currentAccess.HasFlag(source.AccessRequirement) &&
            locations.Contains((source as MapObject)?.SecondLocation ?? MapLocation.StartingLocation));
 }
Example #9
0
 public ItemShopSlot(IRewardSource copyFromRewardSource, Item item)
     : base(copyFromRewardSource, item)
 {
 }
Example #10
0
 public TreasureChest(IRewardSource copyFromRewardSource, Item item, AccessRequirement access)
     : base(copyFromRewardSource, item)
 {
     AccessRequirement = access;
 }
Example #11
0
 public TreasureChest(IRewardSource copyFromRewardSource, Item item)
     : base(copyFromRewardSource, item)
 {
 }
Example #12
0
        public IRewardSource Pick(List <IRewardSource> sources, bool forward, bool spread, bool incentive, MT19337 rng)
        {
            if (!sources.Any())
            {
                return(null);
            }

            IRewardSource result = null;

            //This loop sums up all weights of the reward source.
            double sum = 0.0;

            foreach (var s in sources)
            {
                //If weight already exists for rewardsource use it
                if (weights.TryGetValue(s.Address, out var v))
                {
                    sum += v;
                }
                //If it doesn't have a weight and it's a chest, give it the weight 1
                else if (s is TreasureChest)
                {
                    sum += 1.0;
                    weights.Add(s.Address, 1.0);
                }
                //If it's not a chest, give it a significantly higher weight. There are way more chests than nonchet rewards.
                //That way there is a reasonable chance, that some loose key items land on npcs.
                //changed nonchest weight is tied to the spread placement flag.
                else if (!incentive)
                {
                    sum += nonchest;
                    weights.Add(s.Address, nonchest);
                }
                else
                {
                    sum += 1.0;
                    weights.Add(s.Address, 1.0);
                }
            }

            //We pick a value between 0 and sum. We can't work with the full double range, but with reasonable weight reduction, it's not going to be a problem.
            var r = rng.Next() / (double)uint.MaxValue * sum;

            //This loop finds the reward source associated with the rng number.
            //If Forward placement is active, reduce the weight of all reward sources that were elligible for an item this step.
            //Normally chest like ToF or Matoya have a chance to roll a loose item at every step, because they are accessible from the start.
            //TFC is accessible way later so is used in way less rolls.
            //By reducing the weight on chests, that already had a chance, it's more likely to select a chest or npc from a newly opened up area(hence forward placement).
            //The forward placement is deactivated for incentive items, so it doesn't skew the placement.
            //Forward placement alone produces ..... results.
            sum = 0.0;
            foreach (var s in sources)
            {
                var v = weights[s.Address];
                if (spread && forward)
                {
                    weights[s.Address] = v * reduction;
                }

                sum += v;
                if (result == null && r <= sum)
                {
                    result = s;
                }
            }

            //If we fail to find a reward source because of numerical problems, just pick a random one. It hasn't happend so far, but just to be sure.
            if (result == null)
            {
                result = sources.PickRandom(rng);
            }

            //The V2 builds a list of chests for each overworld entrance. GetNearRewardSources selects chests accessible from the same entrance with the same requirements.
            //So something like all accessible chests in marsh, or all keylocked chests in marsh. The function works for F/E. Otherwise V2 wouldn't work for F/E.
            //All chests found are demoted heavily. So the placement algorithm will only place another item there if it "has" to.
            //That spreads the key items out into different places(hence spread placement).
            //The spread placement remains active for incentive items. There is only one incentive location per dungeon and access requirement.
            //This has the effect of reducing the chance of a loose item in incentivized dungeons.
            //There is a small chance of two incentive locations in one dungeon with F/E. But the effect will not be noticable by any unaware observer.
            if (spread)
            {
                foreach (var s in checker.GetNearRewardSources(sources, result))
                {
                    if (weights.TryGetValue(s.Address, out var v))
                    {
                        weights[s.Address] = v * reduction * reduction;
                    }
                }
            }

            return(result);
        }