Ejemplo n.º 1
0
        public void ShuffleEnemyScripts(MT19337 rng, bool AllowUnsafePirates, bool doNormals)
        {
            var oldEnemies = Get(EnemyOffset, EnemySize * EnemyCount).Chunk(EnemySize);
            var newEnemies = Get(EnemyOffset, EnemySize * EnemyCount).Chunk(EnemySize);

            if (doNormals)
            {
                var normalOldEnemies = oldEnemies.Take(EnemyCount - 10).ToList();                 // all but WarMECH, fiends, fiends revisited, and CHAOS
                normalOldEnemies.Shuffle(rng);
                for (int i = 0; i < EnemyCount - 10; i++)
                {
                    newEnemies[i][7] = normalOldEnemies[i][7];
                }
            }

            var oldBosses = new List <Blob>
            {
                oldEnemies[Enemy.Lich],
                oldEnemies[Enemy.Kary],
                oldEnemies[Enemy.Kraken],
                oldEnemies[Enemy.Tiamat]
            };

            oldBosses.Shuffle(rng);

            newEnemies[Enemy.Lich][7]   = oldBosses[0][7];
            newEnemies[Enemy.Kary][7]   = oldBosses[1][7];
            newEnemies[Enemy.Kraken][7] = oldBosses[2][7];
            newEnemies[Enemy.Tiamat][7] = oldBosses[3][7];

            var oldBigBosses = new List <Blob>
            {
                oldEnemies[Enemy.WarMech],
                oldEnemies[Enemy.Lich2],
                oldEnemies[Enemy.Kary2],
                oldEnemies[Enemy.Kraken2],
                oldEnemies[Enemy.Tiamat2],
                oldEnemies[Enemy.Chaos]
            };

            oldBigBosses.Shuffle(rng);

            newEnemies[Enemy.WarMech][7] = oldBigBosses[0][7];
            newEnemies[Enemy.Lich2][7]   = oldBigBosses[1][7];
            newEnemies[Enemy.Kary2][7]   = oldBigBosses[2][7];
            newEnemies[Enemy.Kraken2][7] = oldBigBosses[3][7];
            newEnemies[Enemy.Tiamat2][7] = oldBigBosses[4][7];
            newEnemies[Enemy.Chaos][7]   = oldBigBosses[5][7];

            if (!AllowUnsafePirates)
            {
                if (newEnemies[Enemy.Pirate][7] < 0xFF)
                {
                    int swapEnemy = newEnemies.IndexOf(newEnemies.First((enemy) => enemy[7] == 0xFF));
                    newEnemies[swapEnemy][7]    = newEnemies[Enemy.Pirate][7];
                    newEnemies[Enemy.Pirate][7] = 0xFF;
                }
            }

            Put(EnemyOffset, newEnemies.SelectMany(enemy => enemy.ToBytes()).ToArray());
        }
Ejemplo n.º 2
0
        void updateCharacterFromOptions(int slotNumber, bool forced, IList <FF1Class> options, MT19337 rng)
        {
            const int lut_PtyGenBuf      = 0x784AA;              // offset for party generation buffer LUT
            const int lut_AllowedClasses = 0x78110;              // offset for allowed classes per slot LUT

            var i = slotNumber - 1;

            if (forced)             // if forced
            {
                FF1Class forcedclass;
                if (options.Any())
                {
                    forcedclass = options.PickRandom(rng);
                }
                else
                {
                    forcedclass = (FF1Class)(Enum.GetValues(typeof(FF1Class))).
                                  GetValue(rng.Between(0, slotNumber == 1 ? 5 : 6));
                }
                options.Clear();
                options.Add(forcedclass);
            }

            // don't make any changes if there's nothing to do
            if (!options.Any())
            {
                return;
            }

            byte allowedFlags = 0b0000_0000;

            foreach (FF1Class option in options)
            {
                allowedFlags |= AllowedClassBitmasks[(int)option];
            }

            // set default member
            var defaultclass = (forced || !DefaultChoices.SequenceEqual(options)) ? (int)options.PickRandom(rng) : slotNumber - 1;

            Data[lut_PtyGenBuf + i * 0x10] = defaultclass == 6 ? (byte)0xFF : (byte)defaultclass;

            // set allowed classes
            Data[lut_AllowedClasses + i] = allowedFlags;

            options.Clear();
        }
Ejemplo n.º 3
0
        public void ShuffleWeaponPermissions(MT19337 rng)
        {
            const int WeaponPermissionsOffset = 0x3BF50;

            ShuffleGearPermissions(rng, WeaponPermissionsOffset);
        }
Ejemplo n.º 4
0
		public void ShuffleShops(MT19337 rng)
		{
			var pointers = Get(ShopPointerOffset, ShopPointerCount*ShopPointerSize).ToUShorts();

			RepackShops(pointers);

			ShuffleShopType(ShopType.Weapon, pointers, rng);
			ShuffleShopType(ShopType.Armor, pointers, rng);
			ShuffleShopType(ShopType.Item, pointers, rng);

			Put(ShopPointerOffset, Blob.FromUShorts(pointers));
		}
Ejemplo n.º 5
0
        public void PubReplaceClinic(MT19337 rng, Flags flags)
        {
            // Copy some CHR data to make the Tavern look more like one.
            const int ShopTileDataOffet = 0x24000;
            const int TileSize          = 16;
            const int ArmorTileOffset   = 14 * 1 * TileSize;
            const int ClinicTileOffset  = 14 * 4 * TileSize;
            const int ItemTileOffset    = 14 * 6 * TileSize;
            const int CaravanTileOffset = 14 * 7 * TileSize;
            const int DecorationOffset  = TileSize * 4;
            const int VendorOffset      = TileSize * 8;

            Put(ShopTileDataOffet + ClinicTileOffset, Get(ShopTileDataOffet + CaravanTileOffset, TileSize * 4));                                    // Tablecloth
            Put(ShopTileDataOffet + ClinicTileOffset + DecorationOffset, Get(ShopTileDataOffet + ItemTileOffset + DecorationOffset, TileSize * 4)); // Barrels of fine ale
            Put(ShopTileDataOffet + ClinicTileOffset + VendorOffset, Get(ShopTileDataOffet + ArmorTileOffset + VendorOffset, TileSize * 6));        // Armorer tending bar
            Put(0x03250, Get(0x03258, 4));                                                                                                          // Caravan palette

            List <byte> options = new List <byte> {
            };

            if ((flags.TAVERN1 ?? false))
            {
                options.Add(0x0);
            }
            if ((flags.TAVERN2 ?? false))
            {
                options.Add(0x1);
            }
            if ((flags.TAVERN3 ?? false))
            {
                options.Add(0x2);
            }
            if ((flags.TAVERN4 ?? false))
            {
                options.Add(0x3);
            }
            if ((flags.TAVERN5 ?? false))
            {
                options.Add(0x4);
            }
            if ((flags.TAVERN6 ?? false))
            {
                options.Add(0x5);
            }

            if (options.Count == 0)
            {
                options = new List <byte> {
                    0x0, 0x1, 0x2, 0x3, 0x4, 0x5
                }
            }
            ;
            List <byte> pub_lut = new List <byte> {
            };

            while (pub_lut.Count < 7)
            {
                options.Shuffle(rng);
                pub_lut.AddRange(options);
            }
            pub_lut.Insert(3, (byte)0xFF);              // Will break if Melmond ever gets a clinic, Nones will need to be hired dead, this results in them being alive.
            Put(0x38066, Blob.FromHex("9D8A9F8E9B97")); // Replaces "CLINIC" with "TAVERN"
            // EnterClinic
            PutInBank(0x0E, 0xA5A1, Blob.FromHex("60A90085248525205BAAA0FFC8B91DA7991003D0F7A902991003C8A648BD0A9D69F0991003C8A905991003C8A9C5991003C8A900991003A910853EA903853F2032AA20D7A6A5628D0C03B0B4209BAA20C2A8B0ADA562D0A92089A6AD0C03186D0C036D0C03AABD10036A6A6A29C04818201C9D68B08BAAA562D0458A690A8510A96185118A488512A9638513A000A90091109112C8C00A30F7C040D0F59D266120E99CA448B90A9D9D00612071C2A00E20799068AA900918BD006169069D0061A9019D0A61A9009D016120349DEAEAEAEA200CE92078A7A921205BAA2043A7A5240525F0F74CA2A5"));
            PutInBank(0x0E, 0x9D0A, pub_lut.Take(8).ToArray());
            // ClinicBuildNameString
            PutInBank(0x0E, 0xA6DD, Blob.FromHex("EDA6A910853EA903853F2032AA4C07A9A000A20086638A2A2A2A29036910991003A900991103A90199120398186903A8E6638A186940AAD0DDA900991003386091ACB5A8FFA40500"));
            // Moved routine
            PutInBank(0x0E, 0xA3F8, Blob.FromHex("7D9C"));
            PutInBank(0x0E, 0x9C7D, Blob.FromHex("A5626A6A6A29C08D0A03A666D01EA018AE0A03BD1861F032C8BD1961F02CC8BD1A61F026C8BD1B61F0203860A01CAE0A03BD1C61F025C8BD1D61F01FC8BD1E61F019C8BD1F61F013386098186D0A03AAAD0C0338E91B9D0061186098186D0A03AAAD0C0338E9439D00611860"));
            // New routine that unequips all gear and sets all substats to zero
            PutInBank(0x0E, 0x9CE9, Blob.FromHex("188A69188510A9618511A007B110297F91108810F7A900A0089110C8C00ED0F960"));

            PutInBank(0x1F, 0xC271, CreateLongJumpTableEntry(0x00, 0xC783));

            //ReviveorBuy routine
            PutInBank(0x0E, 0x9D1C, Blob.FromHex("A903203BAAA912853EA99D853F2032AAA9028D62004C07A9"));

            // Buy Revive text options
            PutInBank(0x0E, 0x9D12, Blob.FromHex("8BB8BC019BA8B9AC3200"));

            // New routine to level up replaced character and zero some stuff, needs new level up stuff in bank 1B
            PutInBank(0x0E, 0x9D34, Blob.FromHex("A99D48A94B48A98748A9A9488A182A2A2A8510A91B4C03FEA9008D24008D25008D012060"));

            Data[0x101A] = 0x13;
            Data[0x109A] = 0x13;
            Data[0x111A] = 0x76;
            Data[0x119A] = 0x77;

            if (flags.RecruitmentModeHireOnly ?? false)
            {
                PutInBank(0x0E, 0xA60F, Blob.FromHex("EAEAEAEAEAEAEAEA"));
            }
        }
Ejemplo n.º 6
0
        private void ShuffleIndexedSkillsSpells(List <Blob> scriptBytes, List <int> indices, MT19337 rng)
        {
            var scripts = indices.Select(i => scriptBytes[i]).ToList();

            var spellBytes = scripts.SelectMany(script => script.SubBlob(2, 8).ToBytes()).Where(b => b != 0xFF).ToList();
            var skillBytes = scripts.SelectMany(script => script.SubBlob(11, 4).ToBytes()).Where(b => b != 0xFF).ToList();

            spellBytes.Shuffle(rng);
            skillBytes.Shuffle(rng);

            var spellBuckets = Bucketize(spellBytes, scripts.Count(script => script[0] != 0), 8, rng);
            var skillBuckets = Bucketize(skillBytes, scripts.Count(script => script[1] != 0), 4, rng);

            var spellChances = scripts.Select(script => script[0]).ToList();
            var skillChances = scripts.Select(script => script[1]).ToList();

            spellChances.Shuffle(rng);
            skillChances.Shuffle(rng);

            int spellBucketIndex = 0, skillBucketIndex = 0;

            for (int i = 0; i < scripts.Count; i++)
            {
                var script = scriptBytes[indices[i]];
                script[0] = spellChances[i];
                script[1] = skillChances[i];

                for (int j = 2; j < 16; j++)
                {
                    script[j] = 0xFF;
                }
                if (spellChances[i] != 0)
                {
                    for (int j = 0; j < spellBuckets[spellBucketIndex].Count; j++)
                    {
                        script[j + 2] = spellBuckets[spellBucketIndex][j];
                    }
                    spellBucketIndex++;
                }
                if (skillChances[i] != 0)
                {
                    for (int j = 0; j < skillBuckets[skillBucketIndex].Count; j++)
                    {
                        script[j + 11] = skillBuckets[skillBucketIndex][j];
                    }
                    skillBucketIndex++;
                }
            }
        }
Ejemplo n.º 7
0
        protected override ItemPlacementResult DoSanePlacement(MT19337 rng, ItemPlacementContext ctx)
        {
            var itemLocationPool      = _incentivesData.AllValidItemLocations.ToList();
            var incentiveLocationPool = _incentivesData.IncentiveLocations.ToList();
            var keyLocations          = _incentivesData.KeyLocations.ToList();
            var canoeLocations        = _incentivesData.CanoeLocations.ToList();
            var bridgeLocations       = _incentivesData.BridgeLocations.ToList();
            var shipLocations         = _incentivesData.ShipLocations.ToList();

            var canoeObsoletesBridge = (bool)_flags.MapCanalBridge && (bool)_flags.MapConeriaDwarves;
            var canoeObsoletesShip   = (bool)_flags.MapCanalBridge ? ((bool)_flags.MapConeriaDwarves || (bool)_flags.MapVolcanoIceRiver) : ((bool)_flags.MapConeriaDwarves && (bool)_flags.MapVolcanoIceRiver);

            Dictionary <MapLocation, Tuple <List <MapChange>, AccessRequirement> > fullLocationRequirements = _overworldMap.FullLocationRequirements;
            Dictionary <MapLocation, OverworldTeleportIndex> overridenOverworld = _overworldMap.OverriddenOverworldLocations;

            var startingPotentialAccess = _overworldMap.StartingPotentialAccess;
            var startingMapLocations    = AccessibleMapLocations(startingPotentialAccess, MapChange.None, fullLocationRequirements);
            var earlyMapLocations       = AccessibleMapLocations(startingPotentialAccess | AccessRequirement.Crystal, MapChange.Bridge, fullLocationRequirements);

            List <IRewardSource> placedItems  = null;
            List <Item>          treasurePool = null;
            var itemShopItem = Item.Bottle;

            _sanityCounter = 0;

            do
            {
                _sanityCounter++;
                if (_sanityCounter > 20)
                {
                    throw new InsaneException("RandomItemPlacement Failure!");
                }
                // 1. (Re)Initialize lists inside of loop
                placedItems = ctx.Forced.ToList();
                var incentives    = ctx.Incentivized.ToList();
                var nonincentives = ctx.Unincentivized.ToList();
                treasurePool = ctx.AllTreasures.ToList();
                incentives.Shuffle(rng);
                nonincentives.Shuffle(rng);

                if ((bool)_flags.NPCItems)
                {
                    // 2. Place caravan item first because among incentive locations it has the smallest set of possible items
                    itemShopItem = SelectVendorItem(incentives, nonincentives, treasurePool, incentiveLocationPool, rng);
                    placedItems.Add(new ItemShopSlot(_caravanItemLocation, itemShopItem));

                    // 3. Place key and canoe next because among incentive locations these have the greatest initial impact
                    if (incentives.Remove(Item.Key) || nonincentives.Remove(Item.Key))
                    {
                        placedItems.Add(NewItemPlacement(keyLocations.PickRandom(rng), Item.Key));
                    }
                    if (incentives.Remove(Item.Canoe) || nonincentives.Remove(Item.Canoe))
                    {
                        placedItems.Add(NewItemPlacement(canoeLocations.Where(x => !placedItems.Any(y => y.Address == x.Address)).ToList().PickRandom(rng), Item.Canoe));
                    }

                    var startingCanoeAvailable =
                        placedItems.Any(x => x.Item == Item.Canoe && startingMapLocations.Contains(x.MapLocation) &&
                                        startingMapLocations.Contains((x as MapObject)?.SecondLocation ?? MapLocation.StartingLocation));
                    var earlyCanoeAvailable =
                        placedItems.Any(x => x.Item == Item.Canoe && earlyMapLocations.Contains(x.MapLocation) &&
                                        earlyMapLocations.Contains((x as MapObject)?.SecondLocation ?? MapLocation.StartingLocation));
                    var earlyKeyAvailable =
                        placedItems.Any(x => x.Item == Item.Key && earlyMapLocations.Contains(x.MapLocation) &&
                                        earlyMapLocations.Contains((x as MapObject)?.SecondLocation ?? MapLocation.StartingLocation));

                    // 4. Place Bridge and Ship next since the valid location lists are so small, unless canoe is available and map edits are applied
                    if (!earlyCanoeAvailable || !canoeObsoletesShip)
                    {
                        var remainingShipLocations =
                            shipLocations
                            .Where(x => !placedItems.Any(y => y.Address == x.Address) &&
                                   (earlyKeyAvailable || !x.AccessRequirement.HasFlag(AccessRequirement.Key)))
                            .ToList();
                        if (!remainingShipLocations.Any())
                        {
                            continue;
                        }
                        if (incentives.Remove(Item.Ship) && remainingShipLocations.Any(x => incentiveLocationPool.Any(y => y.Address == x.Address)))
                        {
                            remainingShipLocations =
                                remainingShipLocations
                                .Where(x => incentiveLocationPool.Any(y => y.Address == x.Address))
                                .ToList();
                        }
                        nonincentives.Remove(Item.Ship);
                        placedItems.Add(NewItemPlacement(remainingShipLocations.PickRandom(rng), Item.Ship));
                    }

                    var startingShipAvailable =
                        placedItems.Any(x => x.Item == Item.Ship && startingMapLocations.Contains(x.MapLocation) &&
                                        startingMapLocations.Contains((x as MapObject)?.SecondLocation ?? MapLocation.StartingLocation));

                    if (!(startingCanoeAvailable && canoeObsoletesBridge) && !startingShipAvailable && !((bool)_flags.FreeBridge))
                    {
                        var startingKeyAvailable =
                            earlyKeyAvailable && placedItems.Any(x => x.Item == Item.Key && startingMapLocations.Contains(x.MapLocation) &&
                                                                 startingMapLocations.Contains((x as MapObject)?.SecondLocation ?? MapLocation.StartingLocation));

                        var remainingBridgeLocations =
                            bridgeLocations
                            .Where(x => !placedItems.Any(y => y.Address == x.Address) &&
                                   (startingKeyAvailable || !x.AccessRequirement.HasFlag(AccessRequirement.Key)))
                            .ToList();
                        if (!remainingBridgeLocations.Any())
                        {
                            continue;
                        }
                        if (incentives.Remove(Item.Bridge) && remainingBridgeLocations.Any(x => incentiveLocationPool.Any(y => y.Address == x.Address)))
                        {
                            remainingBridgeLocations =
                                remainingBridgeLocations
                                .Where(x => incentiveLocationPool.Any(y => y.Address == x.Address))
                                .ToList();
                        }
                        nonincentives.Remove(Item.Bridge);
                        placedItems.Add(NewItemPlacement(remainingBridgeLocations.PickRandom(rng), Item.Bridge));
                    }
                }

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

                // 6. Then place remanining incentive items and unincentivized quest items in any other chest before ToFR
                var leftoverItems = incentives.Concat(nonincentives).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));
                }

                // 7. Check sanity and loop if needed
            } while (!CheckSanity(placedItems, fullLocationRequirements, _flags).Complete);

            return(new ItemPlacementResult {
                PlacedItems = placedItems, RemainingTreasures = treasurePool
            });
        }
Ejemplo n.º 8
0
		private void ShuffleIndexedSkillsSpells(List<Blob> scriptBytes, List<int> indices, MT19337 rng)
		{
			var scripts = indices.Select(i => scriptBytes[i]).ToList();

			var spellBytes = scripts.SelectMany(script => script.SubBlob(2, 8).ToBytes()).Where(b => b != 0xFF).ToList();
			var skillBytes = scripts.SelectMany(script => script.SubBlob(11, 4).ToBytes()).Where(b => b != 0xFF).ToList();
			spellBytes.Shuffle(rng);
			skillBytes.Shuffle(rng);

			var spellBuckets = Bucketize(spellBytes, scripts.Count(script => script[0] != 0), 8, rng);
			var skillBuckets = Bucketize(skillBytes, scripts.Count(script => script[1] != 0), 4, rng);

			var spellChances = scripts.Select(script => script[0]).ToList();
			var skillChances = scripts.Select(script => script[1]).ToList();
			spellChances.Shuffle(rng);
			skillChances.Shuffle(rng);

			int spellBucketIndex = 0, skillBucketIndex = 0;
			for (int i = 0; i < scripts.Count; i++)
			{
				var script = scriptBytes[indices[i]];
				script[0] = spellChances[i];
				script[1] = skillChances[i];

				for (int j = 2; j < 16; j++)
				{
					script[j] = 0xFF;
				}
				if (spellChances[i] != 0)
				{
					for (int j = 0; j < spellBuckets[spellBucketIndex].Count; j++)
					{
						script[j + 2] = spellBuckets[spellBucketIndex][j];
					}
					spellBucketIndex++;
				}
				if (skillChances[i] != 0)
				{
					for (int j = 0; j < skillBuckets[skillBucketIndex].Count; j++)
					{
						script[j + 11] = skillBuckets[skillBucketIndex][j];
					}
					skillBucketIndex++;
				}
			}
		}
Ejemplo n.º 9
0
		private List<List<byte>> Bucketize(List<byte> bytes, int bucketCount, int bucketSize, MT19337 rng)
		{
			var buckets = new List<List<byte>>();
			for (int i = 0; i < bucketCount; i++)
			{
				buckets.Add(new List<byte>());
			}

			int index = 0;
			while (index < bucketCount)
			{
				buckets[index].Add(bytes[index++]);
			}
			while (index < bytes.Count)
			{
				var bucket = rng.Between(0, buckets.Count - 1);
				if (buckets[bucket].Count < bucketSize)
				{
					buckets[bucket].Add(bytes[index++]);
				}
			}

			return buckets;
		}
Ejemplo n.º 10
0
		public void ShuffleEnemyScripts(MT19337 rng)
		{
			var oldEnemies = Get(EnemyOffset, EnemySize * EnemyCount).Chunk(EnemySize);
			var newEnemies = Get(EnemyOffset, EnemySize * EnemyCount).Chunk(EnemySize);

			var normalOldEnemies = oldEnemies.Take(EnemyCount - 10).ToList(); // all but WarMECH, fiends, fiends revisited, and CHAOS
			normalOldEnemies.Shuffle(rng);
			for (int i = 0; i < EnemyCount - 10; i++)
			{
				newEnemies[i][7] = normalOldEnemies[i][7];
			}

			const int WarMech = 118;
			const int Lich = 119;
			const int Lich2 = 120;
			const int Kary = 121;
			const int Kary2 = 122;
			const int Kraken = 123;
			const int Kraken2 = 124;
			const int Tiamat = 125;
			const int Tiamat2 = 126;
			const int Chaos = 127;
			var oldBosses = new List<Blob>
			{
				oldEnemies[Lich],
				oldEnemies[Kary],
				oldEnemies[Kraken],
				oldEnemies[Tiamat]
			};
			oldBosses.Shuffle(rng);

			newEnemies[Lich][7] = oldBosses[0][7];
			newEnemies[Kary][7] = oldBosses[1][7];
			newEnemies[Kraken][7] = oldBosses[2][7];
			newEnemies[Tiamat][7] = oldBosses[3][7];

			var oldBigBosses = new List<Blob>
			{
				oldEnemies[WarMech],
				oldEnemies[Lich2],
				oldEnemies[Kary2],
				oldEnemies[Kraken2],
				oldEnemies[Tiamat2],
				oldEnemies[Chaos]
			};
			oldBigBosses.Shuffle(rng);

			newEnemies[WarMech][7] = oldBigBosses[0][7];
			newEnemies[Lich2][7] = oldBigBosses[1][7];
			newEnemies[Kary2][7] = oldBigBosses[2][7];
			newEnemies[Kraken2][7] = oldBigBosses[3][7];
			newEnemies[Tiamat2][7] = oldBigBosses[4][7];
			newEnemies[Chaos][7] = oldBigBosses[5][7];

			Put(EnemyOffset, newEnemies.SelectMany(enemy => enemy.ToBytes()).ToArray());
		}
Ejemplo n.º 11
0
		public void ShuffleEnemySkillsSpells(MT19337 rng)
		{
			var scriptBytes = Get(ScriptOffset, ScriptSize * ScriptCount).Chunk(ScriptSize);

			var normalIndices = Enumerable.Range(0, 32).Concat(new[] { 33, 43 }).ToList();
			var bossIndices = new List<int> { 34, 36, 38, 40 };
			var bigBossIndices = new List<int> { 32, 35, 37, 39, 41, 42 };

			ShuffleIndexedSkillsSpells(scriptBytes, normalIndices, rng);
			ShuffleIndexedSkillsSpells(scriptBytes, bossIndices, rng);
			ShuffleIndexedSkillsSpells(scriptBytes, bigBossIndices, rng);

			Put(ScriptOffset, scriptBytes.SelectMany(script => script.ToBytes()).ToArray());
		}
Ejemplo n.º 12
0
		public void ShuffleMagicLevels(MT19337 rng, bool keepPermissions)
		{
			var spells = Get(MagicOffset, MagicSize*MagicCount).Chunk(MagicSize);
			var names = Get(MagicNamesOffset, MagicNameSize*MagicCount).Chunk(MagicNameSize);
			var pointers = Get(MagicTextPointersOffset, MagicCount);

			var magicSpells = spells.Select((spell, i) => new MagicSpell
			{
				Index = (byte)i,
				Data = spell,
				Name = names[i],
				TextPointer = pointers[i]
			}).ToList();

			// First we have to un-interleave white and black spells.
			var whiteSpells = magicSpells.Where((spell, i) => (i/4)%2 == 0).ToList();
			var blackSpells = magicSpells.Where((spell, i) => (i/4)%2 == 1).ToList();

			whiteSpells.Shuffle(rng);

			const int warpIndex = 38;
			bool warpBug;
			do
			{
				blackSpells.Shuffle(rng);
				var newWarpIndex = blackSpells.FindIndex(spell => spell.Index == warpIndex);

				// If WARP is the last spell in its level, it won't work due to a bug in the original game.
				warpBug = newWarpIndex%4 == 3;
			} while (warpBug);

			// Now we re-interleave the spells.
			var shuffledSpells = new List<MagicSpell>();
			for (int i = 0; i < MagicCount; i++)
			{
				var sourceIndex = 4*(i/8) + i%4;
				if ((i/4)%2 == 0)
				{
					shuffledSpells.Add(whiteSpells[sourceIndex]);
				}
				else
				{
					shuffledSpells.Add(blackSpells[sourceIndex]);
				}
			}

			Put(MagicOffset, shuffledSpells.Select(spell => spell.Data).Aggregate((seed, next) => seed + next));
			Put(MagicNamesOffset, shuffledSpells.Select(spell => spell.Name).Aggregate((seed, next) => seed + next));
			Put(MagicTextPointersOffset, shuffledSpells.Select(spell => spell.TextPointer).ToArray());

			if (keepPermissions)
			{
				// Shuffle the permissions the same way the spells were shuffled.
				for (int c = 0; c < MagicPermissionsCount; c++)
				{
					var oldPermissions = Get(MagicPermissionsOffset + c*MagicPermissionsSize, MagicPermissionsSize);

					var newPermissions = new byte[MagicPermissionsSize];
					for (int i = 0; i < 8; i++)
					{
						for (int j = 0; j < 8; j++)
						{
							var oldIndex = shuffledSpells[8*i + j].Index;
							var oldPermission = (oldPermissions[oldIndex/8] & (0x80 >> oldIndex%8)) >> (7 - oldIndex%8);
							newPermissions[i] |= (byte)(oldPermission << (7 - j));
						}
					}

					Put(MagicPermissionsOffset + c*MagicPermissionsSize, newPermissions);
				}
			}

			// Map old indices to new indices.
			var newIndices = new byte[MagicCount];
			for (byte i = 0; i < MagicCount; i++)
			{
				newIndices[shuffledSpells[i].Index] = i;
			}

			// Fix enemy spell pointers to point to where the spells are now.
			var scripts = Get(ScriptOffset, ScriptSize*ScriptCount).Chunk(ScriptSize);
			foreach (var script in scripts)
			{
				// Bytes 2-9 are magic spells.
				for (int i = 2; i < 10; i++)
				{
					if (script[i] != 0xFF)
					{
						script[i] = newIndices[script[i]];
					}
				}
			}
			Put(ScriptOffset, scripts.SelectMany(script => script.ToBytes()).ToArray());

			// Fix weapon and armor spell pointers to point to where the spells are now.
			var weapons = Get(WeaponOffset, WeaponSize*WeaponCount).Chunk(WeaponSize);
			foreach (var weapon in weapons)
			{
				if (weapon[3] != 0x00)
				{
					weapon[3] = (byte)(newIndices[weapon[3] - 1] + 1);
				}
			}
			Put(WeaponOffset, weapons.SelectMany(weapon => weapon.ToBytes()).ToArray());

			var armors = Get(ArmorOffset, ArmorSize*ArmorCount).Chunk(ArmorSize);
			foreach (var armor in armors)
			{
				if (armor[3] != 0x00)
				{
					armor[3] = (byte)(newIndices[armor[3] - 1] + 1);
				}
			}
			Put(ArmorOffset, armors.SelectMany(armor => armor.ToBytes()).ToArray());

			// Fix the crazy out of battle spell system.
			var outOfBattleSpellOffset = MagicOutOfBattleOffset;
			for (int i = 0; i < MagicOutOfBattleCount; i++)
			{
				var oldSpellIndex = _outOfBattleSpells[i];
				var newSpellIndex = newIndices[oldSpellIndex];

				Put(outOfBattleSpellOffset, new [] { (byte)(newSpellIndex + 0xB0) });

				outOfBattleSpellOffset += MagicOutOfBattleSize;
			}
		}
Ejemplo n.º 13
0
		private void ShuffleShopType(ShopType shopType, ushort[] pointers, MT19337 rng)
		{
			var shops = GetShops(shopType, pointers);

			var allEntries = shops.SelectMany(list => list).ToList();
			allEntries.Shuffle(rng);

			var newShops = new List<byte>[ShopSectionSize];
			var entry = 0;
			for (int i = 0; i < ShopSectionSize; i++)
			{
				newShops[i] = new List<byte>();
				if (pointers[(int)shopType + i] != ShopNullPointer)
				{
					newShops[i].Add(allEntries[entry++]);
				}
			}

			while (entry < allEntries.Count)
			{
				var tryShop = newShops[rng.Between(0, ShopSectionSize - 1)];
				if (tryShop.Count > 0 && tryShop.Count < 5 && !tryShop.Contains(allEntries[entry]))
				{
					tryShop.Add(allEntries[entry++]);
				}
			}

			foreach (var newShop in newShops)
			{
				newShop.Add(0);
			}

			var pointer = pointers[(int)shopType];
			for (int i = 0; i < ShopSectionSize; i++)
			{
				if (newShops[i].Count > 1)
				{
					Put(ShopPointerBase + pointer, newShops[i].ToArray());

					pointers[(int)shopType + i] = pointer;
					pointer += (ushort)newShops[i].Count;
				}
			}
		}
Ejemplo n.º 14
0
		public void ShuffleMagicShops(MT19337 rng)
		{
			var pointers = Get(ShopPointerOffset, ShopPointerCount * ShopPointerSize).ToUShorts();

			RepackShops(pointers);

			ShuffleShopType(ShopType.White, pointers, rng);
			ShuffleShopType(ShopType.Black, pointers, rng);

			Put(ShopPointerOffset, Blob.FromUShorts(pointers));
		}
Ejemplo n.º 15
0
        public void TransformFinalFormation(FinalFormation formation, EvadeCapValues evadeClampFlag, MT19337 rng)
        {
            if (formation == FinalFormation.None)             // This shouldnt be possible, but we are still checking anyways to be safe.
            {
                return;
            }

            Blob finalBattle = Get(FormationsOffset + ChaosFormationIndex * FormationSize, FormationSize);

            if (formation == FinalFormation.Random)
            {
                formation = (FinalFormation)rng.Between(2, Enum.GetValues(typeof(FinalFormation)).Length - 1);                 // First two are None and Random
                System.Diagnostics.Debug.Assert(formation != FinalFormation.None);
                System.Diagnostics.Debug.Assert(formation != FinalFormation.Random);
            }

            switch (formation)
            {
            case FinalFormation.WarMECHsAndFriends:
                finalBattle[TypeOffset]             = 0x2C;                     // Big/Small Enemy mix, and the Astos/Madpony/Badman/WarMECH patterns
                finalBattle[GFXOffset]              = 0x03;                     // WarMECH Badman N/A N/A
                finalBattle[IDsOffset + 0]          = Enemy.WarMech;
                finalBattle[IDsOffset + 1]          = Enemy.Evilman;
                finalBattle[QuantityOffset + 0]     = 0x22;
                finalBattle[QuantityOffset + 1]     = 0x66;
                finalBattle[PalettesOffset + 0]     = 0x2F;
                finalBattle[PalettesOffset + 1]     = 0x17;
                finalBattle[PaletteAsignmentOffset] = 0x41;                         // Palette Assignment in top nibble, 1 in bottom for unrunnable.
                break;

            case FinalFormation.KaryAndTiamat:
                finalBattle[TypeOffset]             = 0x2B;                     // Big/Small Enemy mix, and the Dragon2 pattern
                finalBattle[GFXOffset]              = 0x05;                     // Dragon Dragon N/A N/A
                finalBattle[IDsOffset + 0]          = Enemy.Kary2;
                finalBattle[IDsOffset + 1]          = Enemy.Tiamat2;
                finalBattle[QuantityOffset + 0]     = 0x11;
                finalBattle[QuantityOffset + 1]     = 0x11;
                finalBattle[PalettesOffset + 0]     = 0x08;
                finalBattle[PalettesOffset + 1]     = 0x0A;
                finalBattle[PaletteAsignmentOffset] = 0x41;                         // Palette Assignment in top nibble, 1 in bottom for unrunnable.
                break;

            case FinalFormation.TheFundead:
                finalBattle[TypeOffset]             = 0x24;                     // Eye pattern
                finalBattle[GFXOffset]              = 0x0B;                     // Eye / Geist
                finalBattle[IDsOffset + 0]          = Enemy.Lich2;
                finalBattle[IDsOffset + 1]          = Enemy.Phantom;
                finalBattle[QuantityOffset + 0]     = 0x22;
                finalBattle[QuantityOffset + 1]     = 0x44;
                finalBattle[PalettesOffset + 0]     = 0x03;
                finalBattle[PalettesOffset + 1]     = 0x17;
                finalBattle[PaletteAsignmentOffset] = 0x41;                         // Palette Assignment in top nibble, 1 in bottom for unrunnable.

                // Scale up the Fundead enemies if we end up with them. They're too weak otherwise.
                ScaleSingleEnemyStats(Enemy.Lich2, 140, 140, false, null, false, 100, 100, GetEvadeIntFromFlag(evadeClampFlag));
                ScaleSingleEnemyStats(Enemy.Phantom, 120, 120, false, null, false, 100, 100, GetEvadeIntFromFlag(evadeClampFlag));
                break;

            case FinalFormation.TimeLoop:
                finalBattle[TypeOffset]             = 0x0B;                     // 9Small + Garland pattern
                finalBattle[GFXOffset]              = 0x2A;                     // Garland Garland Garland N/A
                finalBattle[IDsOffset + 0]          = Enemy.Garland;
                finalBattle[IDsOffset + 1]          = Enemy.Chaos;
                finalBattle[IDsOffset + 2]          = Enemy.Garland;
                finalBattle[QuantityOffset + 0]     = 0x08;
                finalBattle[QuantityOffset + 1]     = 0x11;
                finalBattle[QuantityOffset + 2]     = 0x88;
                finalBattle[PalettesOffset + 0]     = 0x00;
                finalBattle[PalettesOffset + 1]     = 0x00;
                finalBattle[PaletteAsignmentOffset] = 0x01;                         // Palette Assignment in top nibble, 1 in bottom for unrunnable.
                break;

            case FinalFormation.KrakensAndSahags:
                finalBattle[TypeOffset]             = 0x21;                     // 2/4 + Sahag Pattern
                finalBattle[GFXOffset]              = 0x0F;                     // BigEye BigEye Sahag Sahag
                finalBattle[IDsOffset + 0]          = Enemy.Kraken;
                finalBattle[IDsOffset + 1]          = Enemy.Kraken2;
                finalBattle[IDsOffset + 2]          = Enemy.RSahag;
                finalBattle[IDsOffset + 3]          = Enemy.WzSahag;
                finalBattle[QuantityOffset + 0]     = 0x11;
                finalBattle[QuantityOffset + 1]     = 0x11;
                finalBattle[QuantityOffset + 2]     = 0x24;
                finalBattle[QuantityOffset + 3]     = 0x44;
                finalBattle[PalettesOffset + 0]     = 0x08;
                finalBattle[PalettesOffset + 1]     = 0x0B;
                finalBattle[PaletteAsignmentOffset] = 0x51;                         // Palette Assignment in top nibble, 1 in bottom for unrunnable.
                BoostEnemyMorale(Enemy.RSahag);
                BoostEnemyMorale(Enemy.WzSahag);
                break;

            case FinalFormation.SnakePit:
                finalBattle[TypeOffset]             = 0x03;                     // 9 Small + Asp pattern
                finalBattle[GFXOffset]              = 0x00;                     // Asp Asp Asp Asp
                finalBattle[IDsOffset + 0]          = Enemy.Asp;
                finalBattle[IDsOffset + 1]          = Enemy.Tiamat;
                finalBattle[IDsOffset + 2]          = Enemy.Tiamat2;
                finalBattle[IDsOffset + 3]          = Enemy.Cobra;
                finalBattle[QuantityOffset + 0]     = 0x16;
                finalBattle[QuantityOffset + 1]     = 0x11;
                finalBattle[QuantityOffset + 2]     = 0x11;
                finalBattle[QuantityOffset + 3]     = 0x66;
                finalBattle[PalettesOffset + 0]     = 0x3C;
                finalBattle[PaletteAsignmentOffset] = 0x01;                         // Palette Assignment in top nibble, 1 in bottom for unrunnable.
                BoostEnemyMorale(Enemy.Asp);
                BoostEnemyMorale(Enemy.Cobra);
                break;
            }

            Put(FormationsOffset + ChaosFormationIndex * FormationSize, finalBattle);
        }
Ejemplo n.º 16
0
		public void ShuffleEnemyStatusAttacks(MT19337 rng)
		{
			var oldEnemies = Get(EnemyOffset, EnemySize * EnemyCount).Chunk(EnemySize);
			var newEnemies = Get(EnemyOffset, EnemySize * EnemyCount).Chunk(EnemySize);

			oldEnemies.Shuffle(rng);

			for (int i = 0; i < EnemyCount; i++)
			{
				newEnemies[i][14] = oldEnemies[i][14];
				newEnemies[i][15] = oldEnemies[i][15];
			}

			Put(EnemyOffset, newEnemies.SelectMany(enemy => enemy.ToBytes()).ToArray());
		}
Ejemplo n.º 17
0
        public void ScaleSingleEnemyStats(int index, double scale, bool wrapOverflow, bool includeMorale, MT19337 rng, bool increaseOnly)
        {
            var enemy = Get(EnemyOffset + index * EnemySize, EnemySize);

            var hp = BitConverter.ToUInt16(enemy, 4);

            hp = (ushort)Min(Scale(hp, scale, 1.0, rng, increaseOnly), 0x7FFF);
            var hpBytes = BitConverter.GetBytes(hp);

            Array.Copy(hpBytes, 0, enemy, 4, 2);

            var newMorale     = includeMorale ? Scale(enemy[6], scale, 0.25, rng, increaseOnly) : enemy[6];
            var newEvade      = Scale(enemy[8], scale, 1.0, rng, increaseOnly);
            var newDefense    = Scale(enemy[9], scale, 0.5, rng, increaseOnly);
            var newHits       = Scale(enemy[10], scale, 0.5, rng, increaseOnly);
            var newHitPercent = Scale(enemy[11], scale, 1.0, rng, increaseOnly);
            var newStrength   = Scale(enemy[12], scale, 0.25, rng, increaseOnly);
            var newCrit       = Scale(enemy[13], scale, 0.5, rng, increaseOnly);

            if (wrapOverflow)
            {
                newEvade      = ((newEvade - 1) % 0xFF) + 1;
                newDefense    = ((newDefense - 1) % 0xFF) + 1;
                newHits       = ((newHits - 1) % 0xFF) + 1;
                newHitPercent = ((newHitPercent - 1) % 0xFF) + 1;
                newStrength   = ((newStrength - 1) % 0xFF) + 1;
                newCrit       = ((newCrit - 1) % 0xFF) + 1;
            }
            enemy[6]  = (byte)Min(newMorale, 0xFF);           // morale
            enemy[8]  = (byte)Min(newEvade, 0xF0);            // evade clamped to 240
            enemy[9]  = (byte)Min(newDefense, 0xFF);          // defense
            enemy[10] = (byte)Max(Min(newHits, 0xFF), 1);     // hits
            enemy[11] = (byte)Min(newHitPercent, 0xFF);       // hit%
            enemy[12] = (byte)Min(newStrength, 0xFF);         // strength
            enemy[13] = (byte)Min(newCrit, 0xFF);             // critical%

            Put(EnemyOffset + index * EnemySize, enemy);
        }
Ejemplo n.º 18
0
		public void ScalePrices(double scale, MT19337 rng)
		{
			var prices = Get(PriceOffset, PriceSize*PriceCount).Chunk(PriceSize);
			foreach (var price in prices)
			{
				var priceValue = BitConverter.ToUInt16(price, 0);
				priceValue = (ushort)Min(Scale(priceValue, scale, 1, rng), 0xFFFF);

				var priceBytes = BitConverter.GetBytes(priceValue);
				Array.Copy(priceBytes, 0, price, 0, 2);
			}

			Put(PriceOffset, prices.SelectMany(price => price.ToBytes()).ToArray());

			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 price = BitConverter.ToUInt16(priceBytes, 0);

					price = (ushort)Scale(price, scale, 1, rng);
					priceBytes = BitConverter.GetBytes(price);
					Put(ShopPointerBase + pointers[i], priceBytes);
				}
			}
		}
Ejemplo n.º 19
0
        public IncentiveData(MT19337 rng, IIncentiveFlags flags, OverworldMap map, ItemShopSlot shopSlot)
        {
            Dictionary <MapLocation, Tuple <List <MapChange>, AccessRequirement> > fullLocationRequirements = map.FullLocationRequirements;
            var forcedItemPlacements = ItemLocations.AllOtherItemLocations.ToList();

            if (!(flags.NPCItems ?? false))
            {
                forcedItemPlacements.AddRange(ItemLocations.AllNPCFreeItemLocationsExcludingVendor);
                forcedItemPlacements.Add(shopSlot);
            }
            if (!(flags.NPCFetchItems ?? false))
            {
                forcedItemPlacements.AddRange(ItemLocations.AllNPCFetchItemLocations);
            }
            if ((!flags.Treasures ?? false))
            {
                forcedItemPlacements.AddRange(ItemLocations.AllTreasures);
            }
            var incentivePool = new List <Item>();

            if (flags.IncentivizeBridge)
            {
                incentivePool.Add(Item.Bridge);
            }
            if (flags.IncentivizeShip ?? false)
            {
                incentivePool.Add(Item.Ship);
            }
            if (flags.IncentivizeCanal ?? false)
            {
                incentivePool.Add(Item.Canal);
            }
            if (flags.IncentivizeLute ?? false)
            {
                incentivePool.Add(Item.Lute);
            }
            if (flags.IncentivizeCrown ?? false)
            {
                incentivePool.Add(Item.Crown);
            }
            if (flags.IncentivizeCrystal ?? false)
            {
                incentivePool.Add(Item.Crystal);
            }
            if (flags.IncentivizeHerb ?? false)
            {
                incentivePool.Add(Item.Herb);
            }
            if (flags.IncentivizeKey ?? false)
            {
                incentivePool.Add(Item.Key);
            }
            if (flags.IncentivizeTnt ?? false)
            {
                incentivePool.Add(Item.Tnt);
            }
            if (flags.IncentivizeAdamant ?? false)
            {
                incentivePool.Add(Item.Adamant);
            }
            if (flags.IncentivizeSlab ?? false)
            {
                incentivePool.Add(Item.Slab);
            }
            if (flags.IncentivizeRuby ?? false)
            {
                incentivePool.Add(Item.Ruby);
            }
            if (flags.IncentivizeRod ?? false)
            {
                incentivePool.Add(Item.Rod);
            }
            if (flags.IncentivizeFloater ?? false)
            {
                incentivePool.Add(Item.Floater);
            }
            if (flags.IncentivizeChime ?? false)
            {
                incentivePool.Add(Item.Chime);
            }
            if (flags.IncentivizePromotion ?? false)
            {
                incentivePool.Add(Item.Tail);
            }
            if (flags.IncentivizeCube ?? false)
            {
                incentivePool.Add(Item.Cube);
            }
            if (flags.IncentivizeBottle ?? false)
            {
                incentivePool.Add(Item.Bottle);
            }
            if (flags.IncentivizeOxyale ?? false)
            {
                incentivePool.Add(Item.Oxyale);
            }
            if (flags.IncentivizeCanoe ?? false)
            {
                incentivePool.Add(Item.Canoe);
            }

            if (flags.IncentivizeXcalber ?? false)
            {
                incentivePool.Add(Item.Xcalber);
            }
            if (flags.IncentivizeMasamune ?? false)
            {
                incentivePool.Add(Item.Masamune);
            }
            if (flags.IncentivizeKatana ?? false)
            {
                incentivePool.Add(Item.Katana);
            }
            if (flags.IncentivizeVorpal ?? false)
            {
                incentivePool.Add(Item.Vorpal);
            }
            if (flags.IncentivizeRibbon ?? false)
            {
                incentivePool.Add(Item.Ribbon);
            }
            if (flags.IncentivizeRibbon2)
            {
                incentivePool.Add(Item.Ribbon);
            }
            if (flags.IncentivizeOpal ?? false)
            {
                incentivePool.Add(Item.Opal);
            }
            if (flags.Incentivize65K)
            {
                incentivePool.Add(Item.Gold65000);
            }
            if (flags.IncentivizeBad)
            {
                incentivePool.Add(Item.Cloth);
            }
            if (flags.IncentivizeDefCastArmor ?? false)
            {
                incentivePool.Add(Item.WhiteShirt);
            }
            if (flags.IncentivizeOffCastArmor ?? false)
            {
                incentivePool.Add(Item.BlackShirt);
            }
            if (flags.IncentivizeOtherCastArmor ?? false)
            {
                incentivePool.Add(Item.PowerGauntlets);
            }
            if (flags.IncentivizeDefCastWeapon ?? false)
            {
                incentivePool.Add(Item.Defense);
            }
            if (flags.IncentivizeOffCastWeapon ?? false)
            {
                incentivePool.Add(Item.ThorHammer);
            }
            if (flags.IncentivizeOtherCastWeapon)
            {
                incentivePool.Add(Item.BaneSword);
            }

            var incentiveLocationPool = new List <IRewardSource>();

            if (flags.IncentivizeKingConeria)
            {
                incentiveLocationPool.Add(ItemLocations.KingConeria);
            }
            if (flags.IncentivizePrincess)
            {
                incentiveLocationPool.Add(ItemLocations.Princess);
            }
            if (flags.IncentivizeMatoya)
            {
                incentiveLocationPool.Add(ItemLocations.Matoya);
            }
            if (flags.IncentivizeBikke)
            {
                incentiveLocationPool.Add(ItemLocations.Bikke);
            }
            if (flags.IncentivizeElfPrince)
            {
                incentiveLocationPool.Add(ItemLocations.ElfPrince);
            }
            if (flags.IncentivizeAstos)
            {
                incentiveLocationPool.Add(ItemLocations.Astos);
            }
            if (flags.IncentivizeNerrick)
            {
                incentiveLocationPool.Add(ItemLocations.Nerrick);
            }
            if (flags.IncentivizeSmith)
            {
                incentiveLocationPool.Add(ItemLocations.Smith);
            }
            if (flags.IncentivizeSarda)
            {
                incentiveLocationPool.Add(ItemLocations.Sarda);
            }
            if (flags.IncentivizeCanoeSage)
            {
                incentiveLocationPool.Add(ItemLocations.CanoeSage);
            }
            if (flags.IncentivizeCubeBot)
            {
                incentiveLocationPool.Add(ItemLocations.CubeBot);
            }
            if (flags.IncentivizeFairy)
            {
                incentiveLocationPool.Add(ItemLocations.Fairy);
            }
            if (flags.IncentivizeLefein)
            {
                incentiveLocationPool.Add(ItemLocations.Lefein);
            }
            if (flags.IncentivizeVolcano ?? false)
            {
                incentiveLocationPool.Add(ItemLocations.VolcanoMajor);
            }
            if (flags.IncentivizeEarth ?? false)
            {
                incentiveLocationPool.Add(ItemLocations.EarthCaveMajor);
            }
            if (flags.IncentivizeMarsh ?? false)
            {
                incentiveLocationPool.Add(ItemLocations.MarshCaveMajor);
            }
            if (flags.IncentivizeMarshKeyLocked ?? false)
            {
                incentiveLocationPool.Add(ItemLocations.MarshCave13);
            }
            if (flags.IncentivizeSkyPalace ?? false)
            {
                incentiveLocationPool.Add(ItemLocations.SkyPalaceMajor);
            }
            if (flags.IncentivizeSeaShrine ?? false)
            {
                incentiveLocationPool.Add(ItemLocations.SeaShrineMajor);
            }
            if (flags.IncentivizeConeria ?? false)
            {
                incentiveLocationPool.Add(ItemLocations.ConeriaMajor);
            }
            if (flags.IncentivizeIceCave ?? false)
            {
                incentiveLocationPool.Add(ItemLocations.IceCaveMajor);
            }
            if (flags.IncentivizeOrdeals ?? false)
            {
                incentiveLocationPool.Add(ItemLocations.OrdealsMajor);
            }
            if (flags.IncentivizeCaravan)
            {
                incentiveLocationPool.Add(ItemLocations.CaravanItemShop1);
            }
            if (flags.IncentivizeTitansTrove ?? false)
            {
                incentiveLocationPool.Add(ItemLocations.TitansTunnel1);
            }
            var itemLocationPool =
                ItemLocations.AllTreasures.Concat(ItemLocations.AllNPCItemLocations)
                .Where(x => !x.IsUnused && !forcedItemPlacements.Any(y => y.Address == x.Address))
                .ToList();

            if ((bool)flags.EarlyOrdeals)
            {
                forcedItemPlacements =
                    forcedItemPlacements
                    .Select(x => ((x as TreasureChest)?.AccessRequirement.HasFlag(AccessRequirement.Crown) ?? false)
                                                                ? new TreasureChest(x, x.Item, x.AccessRequirement & ~AccessRequirement.Crown)
                                                                : x).ToList();
                itemLocationPool =
                    itemLocationPool
                    .Select(x => ((x as TreasureChest)?.AccessRequirement.HasFlag(AccessRequirement.Crown) ?? false)
                                                                ? new TreasureChest(x, x.Item, x.AccessRequirement & ~AccessRequirement.Crown)
                                                                : x).ToList();
                incentiveLocationPool =
                    incentiveLocationPool
                    .Select(x => ((x as TreasureChest)?.AccessRequirement.HasFlag(AccessRequirement.Crown) ?? false)
                                                        ? new TreasureChest(x, x.Item, x.AccessRequirement & ~AccessRequirement.Crown)
                                                        : x).ToList();
            }
            if ((bool)flags.EarlyKing)
            {
                forcedItemPlacements =
                    forcedItemPlacements
                    .Select(x => x.Address == ItemLocations.KingConeria.Address
                                                                        ? new MapObject(ObjectId.King, MapLocation.ConeriaCastle2, x.Item)
                                                                        : x).ToList();
                itemLocationPool =
                    itemLocationPool
                    .Select(x => x.Address == ItemLocations.KingConeria.Address
                                                                        ? new MapObject(ObjectId.King, MapLocation.ConeriaCastle2, x.Item)
                                                                        : x).ToList();
                incentiveLocationPool =
                    incentiveLocationPool
                    .Select(x => x.Address == ItemLocations.KingConeria.Address
                                                                        ? new MapObject(ObjectId.King, MapLocation.ConeriaCastle2, x.Item)
                                                                        : x).ToList();
            }
            if ((bool)flags.EarlySage)
            {
                forcedItemPlacements =
                    forcedItemPlacements
                    .Select(x => x.Address == ItemLocations.CanoeSage.Address
                                                                        ? new MapObject(ObjectId.CanoeSage, MapLocation.CrescentLake, x.Item)
                                                                        : x).ToList();
                itemLocationPool =
                    itemLocationPool
                    .Select(x => x.Address == ItemLocations.CanoeSage.Address
                                                                        ? new MapObject(ObjectId.CanoeSage, MapLocation.CrescentLake, x.Item)
                                                                        : x).ToList();
                incentiveLocationPool =
                    incentiveLocationPool
                    .Select(x => x.Address == ItemLocations.CanoeSage.Address
                                                                        ? new MapObject(ObjectId.CanoeSage, MapLocation.CrescentLake, x.Item)
                                                                        : x).ToList();
            }
            if ((bool)flags.EarlySarda)
            {
                forcedItemPlacements =
                    forcedItemPlacements
                    .Select(x => x.Address == ItemLocations.Sarda.Address
                                                                ? new MapObject(ObjectId.Sarda, MapLocation.SardasCave, x.Item)
                                                                : x).ToList();
                itemLocationPool =
                    itemLocationPool
                    .Select(x => x.Address == ItemLocations.Sarda.Address
                                                                ? new MapObject(ObjectId.Sarda, MapLocation.SardasCave, x.Item)
                                                                : x).ToList();
                incentiveLocationPool =
                    incentiveLocationPool
                    .Select(x => x.Address == ItemLocations.Sarda.Address
                                                                ? new MapObject(ObjectId.Sarda, MapLocation.SardasCave, x.Item)
                                                                : x).ToList();
            }

            MapLocation elfDoctorLocation = map.ObjectiveNPCs[ObjectId.ElfDoc];

            if (elfDoctorLocation != MapLocation.ElflandCastle)
            {
                forcedItemPlacements =
                    forcedItemPlacements
                    .Select(x => x.Address == ItemLocations.ElfPrince.Address
                                                                ? new MapObject(ObjectId.ElfPrince, MapLocation.ElflandCastle, x.Item, AccessRequirement.Herb, ObjectId.ElfDoc, requiredSecondLocation: elfDoctorLocation)
                                                                : x).ToList();
                itemLocationPool =
                    itemLocationPool
                    .Select(x => x.Address == ItemLocations.ElfPrince.Address
                                                                ? new MapObject(ObjectId.ElfPrince, MapLocation.ElflandCastle, x.Item, AccessRequirement.Herb, ObjectId.ElfDoc, requiredSecondLocation: elfDoctorLocation)
                                                                : x).ToList();
                incentiveLocationPool =
                    incentiveLocationPool
                    .Select(x => x.Address == ItemLocations.ElfPrince.Address
                                                                ? new MapObject(ObjectId.ElfPrince, MapLocation.ElflandCastle, x.Item, AccessRequirement.Herb, ObjectId.ElfDoc, requiredSecondLocation: elfDoctorLocation)
                                                                : x).ToList();
            }

            MapLocation unneLocation = map.ObjectiveNPCs[ObjectId.Unne];

            if (unneLocation != MapLocation.Melmond)
            {
                forcedItemPlacements =
                    forcedItemPlacements
                    .Select(x => x.Address == ItemLocations.Lefein.Address
                                                                ? new MapObject(ObjectId.Lefein, MapLocation.Lefein, x.Item, AccessRequirement.Slab, ObjectId.Unne, requiredSecondLocation: unneLocation)
                                                                : x).ToList();
                itemLocationPool =
                    itemLocationPool
                    .Select(x => x.Address == ItemLocations.Lefein.Address
                                                                ? new MapObject(ObjectId.Lefein, MapLocation.Lefein, x.Item, AccessRequirement.Slab, ObjectId.Unne, requiredSecondLocation: unneLocation)
                                                                : x).ToList();
                incentiveLocationPool =
                    incentiveLocationPool
                    .Select(x => x.Address == ItemLocations.Lefein.Address
                                                                ? new MapObject(ObjectId.Lefein, MapLocation.Lefein, x.Item, AccessRequirement.Slab, ObjectId.Unne, requiredSecondLocation: unneLocation)
                                                                : x).ToList();
            }

            foreach (var item in forcedItemPlacements.Select(x => x.Item))
            {
                incentivePool.Remove(item);
            }

            var validKeyLocations = new List <IRewardSource> {
                ItemLocations.ElfPrince
            };
            var validBridgeLocations = new List <IRewardSource> {
                ItemLocations.KingConeria
            };
            var validShipLocations = new List <IRewardSource> {
                ItemLocations.Bikke
            };
            var validCanoeLocations = new List <IRewardSource> {
                ItemLocations.CanoeSage
            };
            var everythingButOrbs = ~AccessRequirement.BlackOrb;

            if (flags.NPCFetchItems ?? false)
            {
                var validKeyMapLocations = ItemPlacement.AccessibleMapLocations(~(AccessRequirement.BlackOrb | AccessRequirement.Key), MapChange.All, fullLocationRequirements);
                validKeyLocations = itemLocationPool.Where(x => validKeyMapLocations.Contains(x.MapLocation) &&
                                                           validKeyMapLocations.Contains((x as MapObject)?.SecondLocation ?? MapLocation.StartingLocation)).ToList();
                var keyPlacementRank = rng.Between(1, incentivePool.Count);
                if (incentivePool.Contains(Item.Key) && incentiveLocationPool.Any(x => validKeyLocations.Any(y => y.Address == x.Address)) && keyPlacementRank <= incentiveLocationPool.Count)
                {
                    validKeyLocations = validKeyLocations.Where(x => incentiveLocationPool.Any(y => y.Address == x.Address)).ToList();
                }
                else if (!(flags.IncentivizeKey ?? false) && incentivePool.Count >= incentiveLocationPool.Count)
                {
                    validKeyLocations = validKeyLocations.Where(x => !incentiveLocationPool.Any(y => y.Address == x.Address)).ToList();
                }
            }

            if (flags.NPCItems ?? false)
            {
                var everythingButCanoe      = ~MapChange.Canoe;
                var startingPotentialAccess = map.StartingPotentialAccess;
                var startingMapLocations    = ItemPlacement.AccessibleMapLocations(startingPotentialAccess, MapChange.None, fullLocationRequirements);
                var validShipMapLocations   = ItemPlacement.AccessibleMapLocations(startingPotentialAccess | AccessRequirement.Crystal, MapChange.Bridge, fullLocationRequirements);
                var validCanoeMapLocations  = ItemPlacement.AccessibleMapLocations(everythingButOrbs, everythingButCanoe, fullLocationRequirements);

                validBridgeLocations =
                    itemLocationPool.Where(x => startingMapLocations.Contains(x.MapLocation) &&
                                           startingMapLocations.Contains((x as MapObject)?.SecondLocation ?? MapLocation.StartingLocation)).ToList();
                validShipLocations =
                    itemLocationPool.Where(x => validShipMapLocations.Contains(x.MapLocation) &&
                                           validShipMapLocations.Contains((x as MapObject)?.SecondLocation ?? MapLocation.StartingLocation)).ToList();
                validCanoeLocations =
                    itemLocationPool.Where(x => validCanoeMapLocations.Contains(x.MapLocation) &&
                                           validCanoeMapLocations.Contains((x as MapObject)?.SecondLocation ?? MapLocation.StartingLocation)).ToList();

                var canoePlacementRank   = rng.Between(1, incentivePool.Count);
                var validCanoeIncentives = validCanoeLocations.Where(x => incentiveLocationPool.Any(y => y.Address == x.Address)).ToList();
                if (incentivePool.Contains(Item.Canoe) && canoePlacementRank <= incentiveLocationPool.Count &&
                    validKeyLocations.Union(validCanoeIncentives).Count() > 1)                     // The Key can be placed in at least one place more than than the Canoe
                {
                    validCanoeLocations = validCanoeIncentives;
                }
                else if (!flags.IncentivizeBridge && incentivePool.Count >= incentiveLocationPool.Count)
                {
                    validCanoeLocations = validCanoeLocations.Where(x => !incentiveLocationPool.Any(y => y.Address == x.Address)).ToList();
                }
            }

            var nonEndgameMapLocations = ItemPlacement.AccessibleMapLocations(~AccessRequirement.BlackOrb, MapChange.All, fullLocationRequirements);

            ForcedItemPlacements = forcedItemPlacements.ToList();
            IncentiveItems       = incentivePool.ToList();

            BridgeLocations = validBridgeLocations
                              .Where(x => !forcedItemPlacements.Any(y => y.Address == x.Address))
                              .ToList();
            ShipLocations = validShipLocations
                            .Where(x => !forcedItemPlacements.Any(y => y.Address == x.Address))
                            .ToList();
            KeyLocations = validKeyLocations
                           .Where(x => !forcedItemPlacements.Any(y => y.Address == x.Address))
                           .ToList();
            CanoeLocations = validCanoeLocations
                             .Where(x => !forcedItemPlacements.Any(y => y.Address == x.Address))
                             .ToList();
            IncentiveLocations = incentiveLocationPool
                                 .Where(x => !forcedItemPlacements.Any(y => y.Address == x.Address))
                                 .ToList();

            AllValidItemLocations            = itemLocationPool.ToList();
            AllValidPreBlackOrbItemLocations = AllValidItemLocations
                                               .Where(x => nonEndgameMapLocations.Contains(x.MapLocation) && nonEndgameMapLocations.Contains((x as MapObject)?.SecondLocation ?? MapLocation.StartingLocation))
                                               .ToList();
        }
Ejemplo n.º 20
0
		public void ScaleEnemyStats(double scale, MT19337 rng)
		{
			var enemies = Get(EnemyOffset, EnemySize*EnemyCount).Chunk(EnemySize);
			foreach (var enemy in enemies)
			{
				var hp = BitConverter.ToUInt16(enemy, 4);
				hp = (ushort)Min(Scale(hp, scale, 1.0, rng), 0x7FFF);
				var hpBytes = BitConverter.GetBytes(hp);
				Array.Copy(hpBytes, 0, enemy, 4, 2);

				enemy[6] = (byte)Min(Scale(enemy[6], scale, 0.25, rng), 0xFF); // morale
				enemy[8] = (byte)Min(Scale(enemy[8], scale, 1.0, rng), 0xFA); // evade clamped to 250
				enemy[9] = (byte)Min(Scale(enemy[9], scale, 0.5, rng), 0xFF); // defense
				enemy[10] = (byte)Max(Min(Scale(enemy[10], scale, 0.5, rng), 0xFF), 1); // hits
				enemy[11] = (byte)Min(Scale(enemy[11], scale, 1.0, rng), 0xFF); // hit%
				enemy[12] = (byte)Min(Scale(enemy[12], scale, 0.25, rng), 0xFF); // strength
				enemy[13] = (byte)Min(Scale(enemy[13], scale, 0.5, rng), 0xFF); // critical%
			}

			Put(EnemyOffset, enemies.SelectMany(enemy => enemy.ToBytes()).ToArray());
		}
Ejemplo n.º 21
0
 protected abstract ItemPlacementResult DoSanePlacement(MT19337 rng, ItemPlacementContext ctx);
Ejemplo n.º 22
0
		private int Scale(int value, double scale, double adjustment, MT19337 rng)
		{
			var exponent = (double)rng.Next()/uint.MaxValue*2.0 - 1.0;
			var adjustedScale = 1.0 + adjustment*(scale - 1.0);

			return (int)Round(Pow(adjustedScale, exponent)*value, MidpointRounding.AwayFromZero);
		}
Ejemplo n.º 23
0
        public CompleteMap Generate(MT19337 rng, MapGeneratorStrategy strategy, MapRequirements reqs)
        {
            CompleteMap map = null;

            if (reqs.MapId == MapId.Waterfall)
            {
                reqs.Floor       = Tile.WaterfallRandomEncounters;
                reqs.InRoomFloor = Tile.WaterfallInside;
                reqs.FreeNPCs    = Enumerable.Range(1, 10);
                reqs.Rooms       = new List <RoomSpec>
                {
                    new RoomSpec
                    {
                        Tiledata = new byte[, ] {
                            { 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02 },
                            { 0x03, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x05 },
                            { 0x03, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x05 },
                            { 0x06, 0x07, 0x48, 0x07, 0x07, 0x07, 0x07, 0x08 },
                            { 0x30, 0x30, 0x36, 0x30, 0x30, 0x30, 0x30, 0x30 },
                            { 0x49, 0x49, 0x3A, 0x49, 0x49, 0x49, 0x49, 0x49 }
                        },
                        NPCs = new List <NPC> {
                            new NPC {
                                Index = 0, Coord = (5, 2), InRoom = true, Stationary = false
                            }
                        },
                    }
                };
                reqs.Portals = new byte[] { (byte)Tile.WarpUp };

                IMapGeneratorEngine engine = GetEngine(strategy);
                int iterations             = 0;
                while (iterations < MAX_MAP_ITERATIONS && map == null)
                {
                    Console.WriteLine($"Generating {reqs.MapId} - iteration #{iterations}");
                    map = engine.Generate(rng, reqs);
                }

                if (map == null)
                {
                    throw new InsaneException($"Couldn't generate map using {strategy} after maximum {iterations} iterations.");
                }

                // add the reqs we used
                map.Requirements = reqs;
            }
            else if (reqs.MapId == MapId.EarthCaveB1)
            {
                reqs.Floor       = Tile.EarthCaveRandomEncounters;
                reqs.InRoomFloor = Tile.EarthCaveInside;
                reqs.OutOfBounds = Tile.EarthCaveOOB;
                reqs.Barrier     = Tile.EarthCaveRockA;
                reqs.FreeNPCs    = new int[] { };
                reqs.Rooms       = new List <RoomSpec> {
                };
                reqs.Portals     = new byte[] { (byte)Tile.WarpUp, 0x24 };
                reqs.Objects     = Enumerable.Range(0x42, 5).Select(x => (byte)x);
                reqs.Traps       = Enumerable.Repeat(0x1D, 3).Select(x => (byte)x);

                IMapGeneratorEngine engine = GetEngine(strategy);
                map = engine.Generate(rng, reqs);

                // add the reqs we used
                map.Requirements = reqs;
            }
            else if (reqs.MapId == MapId.EarthCaveB2)
            {
                reqs.Floor       = Tile.EarthCaveRandomEncounters;
                reqs.InRoomFloor = Tile.EarthCaveInside;
                reqs.OutOfBounds = Tile.EarthCaveOOB;
                reqs.Barrier     = Tile.EarthCaveRockA;
                reqs.FreeNPCs    = Enumerable.Range(0, 13);
                reqs.Rooms       = new List <RoomSpec> {
                };
                reqs.Portals     = new byte[] { (byte)Tile.WarpUp, 0x25 };
                reqs.Objects     = Enumerable.Range(0x47, 6).Select(x => (byte)x);
                reqs.Traps       = Enumerable.Range(0x1D, 1).Select(x => (byte)x);

                IMapGeneratorEngine engine = GetEngine(strategy);
                map = engine.Generate(rng, reqs);

                // add the reqs we used
                map.Requirements = reqs;
            }
            else
            {
                throw new ArgumentOutOfRangeException();
            }

            // Free NPC placement doesn't require the engine
            var locations = map.Map.Where(element => element.Tile == reqs.Floor).ToList();

            reqs.FreeNPCs.ToList().ForEach(npc =>
            {
                var location = locations.SpliceRandom(rng);
                reqs.Rom.MoveNpc(reqs.MapId, npc, location.X, location.Y, false, false);
            });

            if (Debugger.IsAttached)
            {
                Console.Write(map.AsText());
            }

            return(map);
        }
Ejemplo n.º 24
0
		public void ShuffleTreasures(MT19337 rng)
		{
			DirectedGraph<byte> graph = new DirectedGraph<byte>();
			var treasureBlob = Get(TreasureOffset, TreasureSize * TreasureCount);
			var usedIndices = Enumerable.Range(0, TreasureCount).Except(TreasureConditions.NotUsed).ToList();
			var usedTreasures = usedIndices.Select(i => treasureBlob[i]).ToList();
			bool tofrQuestItem;
			do
			{
				usedTreasures.Shuffle(rng);

				for (int i = 0; i < usedIndices.Count; i++)
				{
					treasureBlob[usedIndices[i]] = usedTreasures[i];
				}

				// ToFR is only exitable using WARP or EXIT, so we don't want these items showing up there.
				// Especially not the TAIL, as that would make class change impossible.  And the CROWN being
				// here could block a LOT of valuable loot if you don't have a WW or BW.
				tofrQuestItem =
					TreasureConditions.ToFR.Contains(treasureBlob.IndexOf((byte)QuestItems.Crown)) ||
					TreasureConditions.ToFR.Contains(treasureBlob.IndexOf((byte)QuestItems.Tail)) ||
					TreasureConditions.ToFR.Contains(treasureBlob.IndexOf((byte)QuestItems.Adamant));
				if (tofrQuestItem)
				{
					continue;
				}

				var blockages = new List<Tuple<byte, int, List<int>>>
				{
					Tuple.Create((byte)QuestItems.Crown,   treasureBlob.IndexOf((byte)QuestItems.Crown),   TreasureConditions.CrownBlocked),
					Tuple.Create((byte)QuestItems.Tnt,     treasureBlob.IndexOf((byte)QuestItems.Tnt),     TreasureConditions.TntBlocked),
					Tuple.Create((byte)QuestItems.Ruby,    treasureBlob.IndexOf((byte)QuestItems.Ruby),    TreasureConditions.RubyBlocked),
					Tuple.Create((byte)QuestItems.Floater, treasureBlob.IndexOf((byte)QuestItems.Floater), TreasureConditions.FloaterBlocked),
					Tuple.Create((byte)QuestItems.Slab,    treasureBlob.IndexOf((byte)QuestItems.Slab),    TreasureConditions.SlabBlocked)
				};

				graph = new DirectedGraph<byte>();
				foreach (var blockage in blockages)
				{
					graph.AddNode(blockage.Item1);
				}

				foreach (var blocker in blockages)
				{
					foreach (var blockee in blockages)
					{
						if (blocker.Item3.Contains(blockee.Item2))
						{
							graph.AddEdge(blockee.Item1, blocker.Item1);
						}
					}
				}
			} while (tofrQuestItem || graph.HasCycles());

			Put(TreasureOffset, treasureBlob);
		}
Ejemplo n.º 25
0
        public void ShuffleEnemyScripts(MT19337 rng, bool AllowUnsafePirates)
        {
            var oldEnemies = Get(EnemyOffset, EnemySize * EnemyCount).Chunk(EnemySize);
            var newEnemies = Get(EnemyOffset, EnemySize * EnemyCount).Chunk(EnemySize);

            var normalOldEnemies = oldEnemies.Take(EnemyCount - 10).ToList();             // all but WarMECH, fiends, fiends revisited, and CHAOS

            normalOldEnemies.Shuffle(rng);
            for (int i = 0; i < EnemyCount - 10; i++)
            {
                newEnemies[i][7] = normalOldEnemies[i][7];
            }

            const int Pirate    = 15;
            const int WarMech   = 118;
            const int Lich      = 119;
            const int Lich2     = 120;
            const int Kary      = 121;
            const int Kary2     = 122;
            const int Kraken    = 123;
            const int Kraken2   = 124;
            const int Tiamat    = 125;
            const int Tiamat2   = 126;
            const int Chaos     = 127;
            var       oldBosses = new List <Blob>
            {
                oldEnemies[Lich],
                oldEnemies[Kary],
                oldEnemies[Kraken],
                oldEnemies[Tiamat]
            };

            oldBosses.Shuffle(rng);

            newEnemies[Lich][7]   = oldBosses[0][7];
            newEnemies[Kary][7]   = oldBosses[1][7];
            newEnemies[Kraken][7] = oldBosses[2][7];
            newEnemies[Tiamat][7] = oldBosses[3][7];

            var oldBigBosses = new List <Blob>
            {
                oldEnemies[WarMech],
                oldEnemies[Lich2],
                oldEnemies[Kary2],
                oldEnemies[Kraken2],
                oldEnemies[Tiamat2],
                oldEnemies[Chaos]
            };

            oldBigBosses.Shuffle(rng);

            newEnemies[WarMech][7] = oldBigBosses[0][7];
            newEnemies[Lich2][7]   = oldBigBosses[1][7];
            newEnemies[Kary2][7]   = oldBigBosses[2][7];
            newEnemies[Kraken2][7] = oldBigBosses[3][7];
            newEnemies[Tiamat2][7] = oldBigBosses[4][7];
            newEnemies[Chaos][7]   = oldBigBosses[5][7];

            if (!AllowUnsafePirates)
            {
                if (newEnemies[Pirate][7] < 0xFF)
                {
                    int swapEnemy = newEnemies.IndexOf(newEnemies.First((enemy) => enemy[7] == 0xFF));
                    newEnemies[swapEnemy][7] = newEnemies[Pirate][7];
                    newEnemies[Pirate][7]    = 0xFF;
                }
            }

            Put(EnemyOffset, newEnemies.SelectMany(enemy => enemy.ToBytes()).ToArray());
        }
Ejemplo n.º 26
0
        public void ScaleSingleEnemyStats(int index, int lowPercentStats, int highPercentStats, bool wrapOverflow, bool includeMorale, MT19337 rng,
                                          bool separateHPScale, int lowPercentHp, int highPercentHp, int evadeClamp)
        {
            double lowDecimalStats  = (double)lowPercentStats / 100.0;
            double highDecimalStats = (double)highPercentStats / 100.0;
            double lowDecimalHp     = (double)lowPercentHp / 100.0;
            double highDecimalHp    = (double)highPercentHp / 100.0;

            var enemy = Get(EnemyOffset + index * EnemySize, EnemySize);

            var hp = BitConverter.ToUInt16(enemy, 4);

            if (separateHPScale)
            {
                hp = (ushort)Min(RangeScale(hp, lowDecimalHp, highDecimalHp, 1.0, rng), 0x7FFF);
            }
            else
            {
                hp = (ushort)Min(RangeScale(hp, lowDecimalStats, highDecimalStats, 1.0, rng), 0x7FFF);
            }
            var hpBytes = BitConverter.GetBytes(hp);

            Array.Copy(hpBytes, 0, enemy, 4, 2);

            var newMorale     = includeMorale ? RangeScale(enemy[6], lowDecimalStats, highDecimalStats, 0.25, rng) : enemy[6];
            var newEvade      = RangeScale(enemy[8], lowDecimalStats, highDecimalStats, 1.0, rng);
            var newDefense    = RangeScale(enemy[9], lowDecimalStats, highDecimalStats, 0.5, rng);
            var newHits       = RangeScale(enemy[10], lowDecimalStats, highDecimalStats, 0.5, rng);
            var newHitPercent = RangeScale(enemy[11], lowDecimalStats, highDecimalStats, 1.0, rng);
            var newStrength   = RangeScale(enemy[12], lowDecimalStats, highDecimalStats, 0.25, rng);
            var newCrit       = RangeScale(enemy[13], lowDecimalStats, highDecimalStats, 0.5, rng);

            if (wrapOverflow)
            {
                newEvade      = ((newEvade - 1) % 0xFF) + 1;
                newDefense    = ((newDefense - 1) % 0xFF) + 1;
                newHits       = ((newHits - 1) % 0xFF) + 1;
                newHitPercent = ((newHitPercent - 1) % 0xFF) + 1;
                newStrength   = ((newStrength - 1) % 0xFF) + 1;
                newCrit       = ((newCrit - 1) % 0xFF) + 1;
            }
            enemy[6]  = (byte)Min(newMorale, 0xFF);           // morale
            enemy[8]  = (byte)Min(newEvade, evadeClamp);      // evade
            enemy[9]  = (byte)Min(newDefense, 0xFF);          // defense
            enemy[10] = (byte)Max(Min(newHits, 0xFF), 1);     // hits
            enemy[11] = (byte)Min(newHitPercent, 0xFF);       // hit%
            enemy[12] = (byte)Min(newStrength, 0xFF);         // strength
            enemy[13] = (byte)Min(newCrit, 0xFF);             // critical%

            Put(EnemyOffset + index * EnemySize, enemy);
        }
Ejemplo n.º 27
0
        public void PartyComposition(MT19337 rng, Flags flags)
        {
            var options = new List <FF1Class>();

            // Do each slot - so ugly!
            if (flags.FIGHTER1)
            {
                options.Add(FF1Class.Fighter);
            }
            if (flags.THIEF1)
            {
                options.Add(FF1Class.Thief);
            }
            if (flags.BLACK_BELT1)
            {
                options.Add(FF1Class.BlackBelt);
            }
            if (flags.RED_MAGE1)
            {
                options.Add(FF1Class.RedMage);
            }
            if (flags.WHITE_MAGE1)
            {
                options.Add(FF1Class.WhiteMage);
            }
            if (flags.BLACK_MAGE1)
            {
                options.Add(FF1Class.BlackMage);
            }
            updateCharacterFromOptions(1, flags.FORCED1, options, rng);

            if (flags.FIGHTER2)
            {
                options.Add(FF1Class.Fighter);
            }
            if (flags.THIEF2)
            {
                options.Add(FF1Class.Thief);
            }
            if (flags.BLACK_BELT2)
            {
                options.Add(FF1Class.BlackBelt);
            }
            if (flags.RED_MAGE2)
            {
                options.Add(FF1Class.RedMage);
            }
            if (flags.WHITE_MAGE2)
            {
                options.Add(FF1Class.WhiteMage);
            }
            if (flags.BLACK_MAGE2)
            {
                options.Add(FF1Class.BlackMage);
            }
            if (flags.NONE_CLASS2)
            {
                options.Add(FF1Class.None);
            }
            updateCharacterFromOptions(2, flags.FORCED2, options, rng);

            if (flags.FIGHTER3)
            {
                options.Add(FF1Class.Fighter);
            }
            if (flags.THIEF3)
            {
                options.Add(FF1Class.Thief);
            }
            if (flags.BLACK_BELT3)
            {
                options.Add(FF1Class.BlackBelt);
            }
            if (flags.RED_MAGE3)
            {
                options.Add(FF1Class.RedMage);
            }
            if (flags.WHITE_MAGE3)
            {
                options.Add(FF1Class.WhiteMage);
            }
            if (flags.BLACK_MAGE3)
            {
                options.Add(FF1Class.BlackMage);
            }
            if (flags.NONE_CLASS3)
            {
                options.Add(FF1Class.None);
            }
            updateCharacterFromOptions(3, flags.FORCED3, options, rng);

            if (flags.FIGHTER4)
            {
                options.Add(FF1Class.Fighter);
            }
            if (flags.THIEF4)
            {
                options.Add(FF1Class.Thief);
            }
            if (flags.BLACK_BELT4)
            {
                options.Add(FF1Class.BlackBelt);
            }
            if (flags.RED_MAGE4)
            {
                options.Add(FF1Class.RedMage);
            }
            if (flags.WHITE_MAGE4)
            {
                options.Add(FF1Class.WhiteMage);
            }
            if (flags.BLACK_MAGE4)
            {
                options.Add(FF1Class.BlackMage);
            }
            if (flags.NONE_CLASS4)
            {
                options.Add(FF1Class.None);
            }
            updateCharacterFromOptions(4, flags.FORCED4, options, rng);

            // Load stats for None
            PutInBank(0x1F, 0xC783, Blob.FromHex("2080B3C931F053EA"));
            PutInBank(0x00, 0xB380, Blob.FromHex("BD0061C9FFD013A9019D0161A9009D07619D08619D0961A931600A0A0A0AA860"));

            //clinic stuff
            PutInBank(0x0E, 0xA6F3, Blob.FromHex("203E9B"));
            PutInBank(0x0E, 0x9B3E, Blob.FromHex("BD0061C9FFD00568684C16A7BD016160"));

            // curing ailments out of battle, allow the waste of things in battle
            PutInBank(0x0E, 0xB388, Blob.FromHex("2077C2EAEAEAEAEAEAEAEA"));
            PutInBank(0x1F, 0xC277, CreateLongJumpTableEntry(0x0F, 0x8BB0));
            PutInBank(0x0F, 0x8BB0, Blob.FromHex("A5626A6A6A29C0AABD0061C9FFD003A90060BD016160"));

            // Better Battle sprite, in 0C_9910_DrawCharacter.asm
            PutInBank(0x0F, 0x8BD0, Blob.FromHex("A86A6A6AAABD0061C9FFF00898AABDA86B8DB36860"));
            PutInBank(0x0C, 0x9910, Blob.FromHex("20A4C8C9FFD001608A0A0AA8"));
            PutInBank(0x1F, 0xC8A4, CreateLongJumpTableEntry(0x0F, 0x8BD0));

            // MapMan for Nones
            PutInBank(0x1F, 0xE92E, Blob.FromHex("20608FEAEAEAEA"));
            PutInBank(0x02, 0x8F60, Blob.FromHex("A9008510AD0061C9FFF00160A92560"));

            // Draw Complex String extension for class FF
            PutInBank(0x1F, 0xC27D, Blob.FromHex("C9FFF0041869F060203EE0A997853EA9C2853F2045DE4C4EE0EA973CA800"));
            PutInBank(0x1F, 0xDF0C, Blob.FromHex("207DC2"));

            // Skip targeting None with aoe spells
            PutInBank(0x0C, 0xB453, Blob.FromHex("20AAC8C9FFF015EA"));
            PutInBank(0x1F, 0xC8AA, CreateLongJumpTableEntry(0x0F, 0x8BF0));
            PutInBank(0x0F, 0x8BF0, Blob.FromHex("ADCE6BAA6A6A6AA8B90061C9FFF0068A09808D8A6C60"));

            // Rewrite class promotion to not promote NONEs, See 0E_95AE_DoClassChange.asm
            PutInBank(0x0E, 0x95AE, Blob.FromHex("A203BCD095B900613006186906990061CA10EFE65660"));
            PutInBank(0x0E, 0x95D0, Blob.FromHex("C0804000"));             // lut used by the above code
        }
Ejemplo n.º 28
0
        private int RangeScale(double value, double lowPercent, double highPercent, double adjustment, MT19337 rng)
        {
            double exponent      = (rng != null) ? (double)rng.Next() / uint.MaxValue : 1.0;        // A number from 0 - 1
            double logLowPercent = Log(lowPercent);
            double logDifference = Log(highPercent) - logLowPercent;

            exponent = exponent * logDifference + logLowPercent;                                                             // For example for 50-200% a number from -0.69 to 0.69, for 200-400% a number from 0.69 to 1.38

            double scaleValue    = Exp(exponent);                                                                            // A number from 0.5 to 2, or 2 to 4
            double adjustedScale = scaleValue > 1 ? (scaleValue - 1) * adjustment + 1 : 1 - ((1 - scaleValue) * adjustment); // Tightens the scale so some stats are not changed by as much. For example for strength (adjustment of 0.25) this becomes 0.875 to 1.25, 1.25 to 1.75 while for hp (adjustment of 1) this stays 0.5 to 2, 2 to 4

            return((int)Round(value * adjustedScale));
        }
Ejemplo n.º 29
0
        public void ShuffleArmorPermissions(MT19337 rng)
        {
            const int ArmorPermissionsOffset = 0x3BFA0;

            ShuffleGearPermissions(rng, ArmorPermissionsOffset);
        }
Ejemplo n.º 30
0
        // Previous RangeScale(), delete if no bugs come up with new RangeScale() - 2020-10-27
        private int oldRangeScale(double value, double lowPercent, double highPercent, double adjustment, MT19337 rng)
        {
            double range            = highPercent - lowPercent;
            double randomRangeScale = rng == null ? range : range * ((double)rng.Next() / uint.MaxValue);
            double actualScale      = lowPercent + randomRangeScale;
            double adjustedScale    = actualScale > 1 ? (actualScale - 1) * adjustment + 1 : 1 - ((1 - actualScale) * adjustment);

            return((int)(adjustedScale * value));
        }
Ejemplo n.º 31
0
        public void ShuffleEnemyFormations(MT19337 rng, FormationShuffleMode shuffleMode)
        {
            if (shuffleMode == FormationShuffleMode.Intrazone)
            {
                // intra-zone shuffle, does not change which formations are in zomes.
                var oldFormations = Get(ZoneFormationsOffset, ZoneFormationsSize * ZoneCount).Chunk(ZoneFormationsSize);
                var newFormations = Get(ZoneFormationsOffset, ZoneFormationsSize * ZoneCount).Chunk(ZoneFormationsSize);

                for (int i = 0; i < ZoneCount; i++)
                {
                    var lowFormations = oldFormations[i].Chunk(4)[0].Chunk(1);                     // shuffle the first 4 formations first
                    lowFormations.Shuffle(rng);
                    newFormations[i][0] = lowFormations[0][0];
                    newFormations[i][1] = lowFormations[1][0];
                    newFormations[i][2] = lowFormations[2][0];
                    newFormations[i][3] = lowFormations[3][0];

                    var shuffleFormations = newFormations[i].SubBlob(2, 6).Chunk(1);                     // get formations 2-8
                    shuffleFormations.Shuffle(rng);
                    for (int j = 2; j < 8; j++)
                    {
                        newFormations[i][j] = shuffleFormations[j - 2][0];
                    }
                }

                Put(ZoneFormationsOffset, newFormations.SelectMany(formation => formation.ToBytes()).ToArray());
            }
            if (shuffleMode == FormationShuffleMode.InterZone)
            {
                // Inter-zone shuffle
                // Get all encounters from zones not surrounding starting area
                List <Blob>      newFormations  = new List <Blob>();
                SortedSet <byte> exclusionZones = new SortedSet <byte>();
                exclusionZones.UnionWith(StartingZones);

                for (byte i = 0; i < ZoneCount; i++)
                {
                    if (StartingZones.Contains(i))
                    {
                        continue;
                    }
                    var zone = Get(ZoneFormationsOffset + (i * ZoneFormationsSize), ZoneFormationsSize);
                    if (zone.ToLongs()[0] == 0)
                    {
                        //some unused overworld zones are zero filled so we catch them here to not pollute the formations list
                        exclusionZones.Add(i);
                    }
                    else
                    {
                        newFormations.AddRange(zone.Chunk(1));
                    }
                }

                newFormations.Shuffle(rng);
                // after shuffling, put original starting zones in so only one write is required
                foreach (byte i in exclusionZones)
                {
                    var startZone = Get(ZoneFormationsOffset + (i * ZoneFormationsSize), ZoneFormationsSize).Chunk(1);
                    newFormations.InsertRange(i * ZoneFormationsSize, startZone);
                }
                Put(ZoneFormationsOffset, newFormations.SelectMany(formation => formation.ToBytes()).ToArray());
            }

            if (shuffleMode == FormationShuffleMode.Randomize)
            {
                // no-pants mode
                var oldFormations         = Get(ZoneFormationsOffset, ZoneFormationsSize * ZoneCount).Chunk(ZoneFormationsSize);
                var newFormations         = Get(ZoneFormationsOffset, ZoneFormationsSize * ZoneCount).Chunk(ZoneFormationsSize);
                var allowableEncounters   = Enumerable.Range(0, 256).ToList();
                var unallowableEncounters = new List <int>()
                {
                    0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD
                };
                allowableEncounters.RemoveAll(x => unallowableEncounters.Contains(x));
                for (byte i = 0; i < ZoneCount; i++)
                {
                    if (StartingZones.Contains(i))
                    {
                        continue;
                    }
                    for (int j = 0; j < ZoneFormationsSize; j++)
                    {
                        newFormations[i][j] = (byte)allowableEncounters.PickRandom(rng);
                    }
                }
                Put(ZoneFormationsOffset, newFormations.SelectMany(formation => formation.ToBytes()).ToArray());
            }
        }
Ejemplo n.º 32
0
        public CompleteMap Generate(MT19337 rng, MapRequirements reqs)
        {
            int sanity = 0;

            while (true)
            {
                if (++sanity == 500)
                {
                    throw new InsaneException("Failed to generate map!");
                }

                try
                {
                    CompleteMap complete = new CompleteMap
                    {
                        Map          = new Map(SentinelDead),
                        Requirements = reqs,
                    };

                    {
                        (int x, int y)coord = (0, 0);
                        reqs.Rooms.ToList().ForEach(room =>
                        {
                            coord.x = rng.Between(0, MapRequirements.Width - 1 - room.Width);
                            complete.Map.Put(coord, room.Tiledata);
                            coord.y += room.Height + rng.Between(0, 5);

                            room.NPCs.ToList().ForEach(npc =>
                            {
                                npc.Coord.x += coord.x;
                                npc.Coord.y += coord.y;
                                reqs.Rom.MoveNpc(reqs.MapId, npc);
                            });
                        });
                    }

                    // Generate rooms first. We'll aim to have enough for all the required chests.
                    Map rooms = complete.Map.Clone();
                    InitializeMap(rng, rooms, 0.33);
                    rooms = DoSimulationStep(rooms, 4, 2, 3);

                    Console.WriteLine($"Room map has {rooms.Count(element => element.Value == SentinelAlive)} walkable tiles.");
                    complete.Map = rooms;
                    Console.Write(complete.AsText());

                    var clone = rooms.Clone();
                    foreach (var el in clone.Where(el => el.Value == SentinelDead))
                    {
                        var roomTile = rooms[el.Coord];

                        foreach (var newTile in roomTile.Surrounding().Concat(roomTile.Left().Surrounding()))
                        {
                            newTile.Value = SentinelDead;
                        }
                    }

                    Console.WriteLine($"Room map has {rooms.Count(element => element.Value == SentinelAlive)} walkable tiles.");

                    complete.Map = rooms;
                    Console.Write(complete.AsText());

                    clone = rooms.Clone();
                    foreach (var el in clone.Where(el => el.Value == SentinelDead))
                    {
                        var roomTile = rooms[el.Coord];
                        if (el.Left().Value == SentinelAlive)
                        {
                            if (el.Up().Value == SentinelAlive)
                            {
                                roomTile.Tile = Tile.RoomBackLeft;
                            }
                            else if (el.Down().Value == SentinelAlive)
                            {
                                roomTile.Tile = Tile.RoomFrontLeft;
                                roomTile.Down().Tile = Tile.InsideWall;
                            }
                            else
                            {
                                roomTile.Tile = Tile.RoomLeft;
                            }
                        }
                        else if (el.Right().Value == SentinelAlive)
                        {
                            if (el.Up().Value == SentinelAlive)
                            {
                                roomTile.Tile = Tile.RoomBackRight;
                            }
                            else if (el.Down().Value == SentinelAlive)
                            {
                                roomTile.Tile = Tile.RoomFrontRight;
                                roomTile.Down().Tile = Tile.InsideWall;
                            }
                            else
                            {
                                roomTile.Tile = Tile.RoomRight;
                            }
                        }
                        else if (el.Up().Value == SentinelAlive)
                        {
                            roomTile.Tile = Tile.RoomBackCenter;
                        }
                        else if (el.Down().Value == SentinelAlive)
                        {
                            roomTile.Tile = Tile.RoomFrontCenter;
                            roomTile.Down().Tile = Tile.InsideWall;
                        }
                        else
                        {
                            roomTile.Tile = Tile.RoomCenter;
                        }
                    }

                    // Carve out a door to all the accessible room tiles.
                    var roomTiles = rooms.Where(el => el.Tile == Tile.RoomCenter);
                    var doorways  = new List <MapElement> {
                    };

                    foreach (var innerTile in roomTiles)
                    {
                        var results = FloodFill(rooms, innerTile.Coord, new List <Tile> {
                            Tile.RoomCenter, Tile.RoomFrontCenter, Tile.Door
                        });
                        if (results[Tile.Door].Any())
                        {
                            continue;
                        }

                        var potentialDoorways = results[Tile.RoomFrontCenter];
                        if (potentialDoorways.Any())
                        {
                            var entryway = potentialDoorways.SpliceRandom(rng);
                            var door     = entryway.Down();
                            var doorway  = door.Down();

                            System.Diagnostics.Debug.Assert(door.Tile == Tile.InsideWall);

                            if (doorway.Value != SentinelAlive)
                            {
                                throw new InsaneException("Doorway not available.");
                            }

                            door.Tile    = Tile.Door;
                            doorway.Tile = Tile.Doorway;
                            doorways.Add(doorway);
                        }
                    }

                    // Place chests now
                    var chestLocations = roomTiles.Where(el => el.Up().Tile == Tile.RoomBackCenter).ToList();
                    if (reqs.Objects.Count() > chestLocations.Count())
                    {
                        throw new InsaneException("Not enough chest locations.");
                    }

                    foreach (byte chest in reqs.Objects)
                    {
                        chestLocations.SpliceRandom(rng).Value = chest;
                    }

                    // Deaden to prepare for second run
                    //foreach (var el in rooms.Where(el => el.Value == SentinelAlive)) { el.Value = SentinelDead; }

                    // Place non-room walls now.

                    /*
                     * for (int sanity2 = 0; sanity2 <501; ++sanity2)
                     * {
                     *      if (sanity2 == 500)
                     *              throw new InsaneException("Couldn't make floors walls.");
                     *
                     *      var walled = rooms.Clone();
                     *      var entrances = doorways.ToList();
                     *      //InitializeMap(rng, walled, 0.4);
                     *      walled = DoSimulationStep(walled, 5, 4, 4);
                     *
                     *      entrances.ForEach(entrance =>
                     *      {
                     *              foreach (var tile in entrance.Surrounding().Where(el => el.Value == SentinelDead))
                     *              {
                     *                      walled[tile.Coord].Value = SentinelAlive;
                     *              }
                     *      });
                     *
                     *      complete.Map = walled;
                     *      Console.Write(complete.AsText());
                     *      bool success = false; // eww
                     *
                     *      // Find a big enough segment to be the main area.
                     *      while (walled.Any(el => el.Value == SentinelAlive))
                     *      {
                     *              var tile = walled.First(el => el.Value == SentinelAlive);
                     *              var results = FloodFill(walled, tile.Coord, new List<Tile> { reqs.Floor, Tile.Doorway }, new List<Tile> { (Tile)SentinelAlive }, reqs.Floor);
                     *
                     *              if (results[reqs.Floor].Count() < 500)
                     *              {
                     *                      // Small section just mark as walls.
                     *                      FloodFill(walled, tile.Coord, new List<Tile> { reqs.Floor }, new List<Tile> { reqs.Floor }, (Tile)SentinelDead);
                     *              }
                     *              else
                     *              {
                     *                      // This is the big section. Make sure all the doors are accessible
                     *                      success = results[Tile.Doorway].Count() == doorways.Count();
                     *                      break;
                     *              }
                     *      }
                     *
                     *      if (success)
                     *      {
                     *              foreach (var el in walled.Where(el => el.Value == SentinelDead))
                     *              {
                     *                      if (el.Up().Tile == reqs.Floor || el.Down().Tile == reqs.Floor || el.Left().Tile == reqs.Floor || el.Right().Tile == reqs.Floor)
                     *                      {
                     *                              el.Tile = reqs.Barrier;
                     *                      } else
                     *                      {
                     *                              el.Tile = reqs.OutOfBounds;
                     *                      }
                     *              }
                     *              complete.Map = walled;
                     *              break;
                     *      }
                     * }
                     */

                    // All the tiles we're editing are now either SentinelAlive or SentinelDead.
                    // Time to map those to real values now.
                    foreach (var el in complete.Map.Where(el => el.Value == SentinelAlive))
                    {
                        el.Tile = reqs.Floor;
                    }

                    Console.WriteLine($"Room map has {complete.Map.Count(element => element.Tile == reqs.Floor)} walkable tiles.");
                    Console.Write(complete.AsText());

                    // Pad all the Alive tiles
                    var locations = complete.Map.Where(element => element.Tile == reqs.Floor).ToList();
                    reqs.Portals.ToList().ForEach(portal =>
                    {
                        var location = locations.SpliceRandom(rng);
                        complete.Map[location.Y, location.X] = portal;

                        if (portal == (byte)Tile.WarpUp)
                        {
                            complete.Entrance = new Coordinate((byte)location.X, (byte)location.Y, CoordinateLocale.Standard);

                            var finalResult = FloodFill(complete.Map, location.Coord, new List <Tile> {
                                reqs.Floor, Tile.WarpUp, Tile.Doorway, Tile.Door, Tile.RoomCenter, Tile.RoomFrontCenter
                            });
                            if (finalResult[Tile.Door].Count() < doorways.Count())
                            {
                                throw new InsaneException("Can't reach all rooms.");
                            }
                        }
                    });

                    Console.Write(complete.AsText());
                    return(complete);
                } catch (InsaneException e)
                {
                    Console.WriteLine(e.ToString());
                }
            }
        }
Ejemplo n.º 33
0
 public void ScaleBossStats(double scale, bool wrapOverflow, bool includeMorale, MT19337 rng, bool increaseOnly)
 {
     Bosses.ForEach(index => ScaleSingleEnemyStats(index, scale, wrapOverflow, includeMorale, rng, increaseOnly));
 }
Ejemplo n.º 34
0
 private void InitializeMap(MT19337 rng, Map map, double initialAliveChance)
 {
     map.Where(element => element.Value == SentinelDead && (double)rng.Next() / uint.MaxValue < initialAliveChance)
     .ToList().ForEach(element => element.Value = SentinelAlive);
 }
Ejemplo n.º 35
0
        // 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, string[] text, MT19337 rng, bool increaseOnly, ItemShopSlot shopItemLocation)
        {
            var scale      = flags.PriceScaleFactor;
            var multiplier = flags.ExpMultiplier;
            var prices     = Get(PriceOffset, PriceSize * PriceCount).ToUShorts();

            for (int i = 0; i < prices.Length; i++)
            {
                var newPrice = Scale(prices[i] / multiplier, scale, 1, rng, increaseOnly);
                prices[i] = (ushort)(flags.WrapPriceOverflow ? ((newPrice - 1) % 0xFFFF) + 1 : 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++)
            {
                text[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)Scale(priceValue / multiplier, scale, 1, rng, increaseOnly);
                    priceBytes = BitConverter.GetBytes(priceValue);
                    Put(ShopPointerBase + pointers[i], priceBytes);
                }
            }
            if (flags.StartingGold)
            {
                var startingGold = BitConverter.ToUInt16(Get(StartingGoldOffset, 2), 0);

                startingGold = (ushort)Min(Scale(startingGold / multiplier, scale, 1, rng, increaseOnly), 0xFFFF);

                Put(StartingGoldOffset, BitConverter.GetBytes(startingGold));
            }
        }
Ejemplo n.º 36
0
        public void MagisizeWeapons(MT19337 rng, bool balanced)
        {
            var Spells = GetSpells();

            if (!balanced)
            {
                Spells.RemoveAll(spell => spell.Data[4] == 0);
                foreach (Item weapon in ItemLists.AllWeapons.Except(ItemLists.AllMagicItem).ToList())
                {
                    WriteItemSpellData(Spells.SpliceRandom(rng), weapon);
                }
            }
            else
            {
                var tieredSpells = new List <List <MagicSpell> > {
                    Spells.GetRange(0, 16), Spells.GetRange(16, 16), Spells.GetRange(32, 16), Spells.GetRange(48, 16)
                };

                var commonOdds = new List <int> {
                    0, 0, 0, 0, 1, 1, 1, 1, 2, 2
                };
                var rareOdds = new List <int> {
                    0, 1, 1, 1, 2, 2, 2, 3, 3, 3
                };
                var legendaryOdds = new List <int> {
                    1, 2, 2, 2, 3, 3, 3, 3, 3, 3
                };

                for (int i = 0; i < 4; i++)
                {
                    tieredSpells[i].RemoveAll(spell => spell.Data[4] == 0);
                }

                foreach (Item weapon in ItemLists.CommonWeaponTier)
                {
                    var selectedTier = commonOdds.PickRandom(rng);
                    while (tieredSpells[selectedTier].Count == 0)
                    {
                        selectedTier = commonOdds.PickRandom(rng);
                    }

                    WriteItemSpellData(tieredSpells[selectedTier].SpliceRandom(rng), weapon);
                }

                foreach (Item weapon in ItemLists.RareWeaponTier.Except(ItemLists.AllMagicItem).ToList())
                {
                    var selectedTier = rareOdds.PickRandom(rng);
                    while (tieredSpells[selectedTier].Count == 0)
                    {
                        selectedTier = rareOdds.PickRandom(rng);
                    }

                    WriteItemSpellData(tieredSpells[selectedTier].SpliceRandom(rng), weapon);
                }

                foreach (Item weapon in ItemLists.LegendaryWeaponTier)
                {
                    var selectedTier = legendaryOdds.PickRandom(rng);
                    while (tieredSpells[selectedTier].Count == 0)
                    {
                        selectedTier = legendaryOdds.PickRandom(rng);
                    }

                    WriteItemSpellData(tieredSpells[selectedTier].SpliceRandom(rng), weapon);
                }

                foreach (Item weapon in ItemLists.UberTier)
                {
                    var selectedTier = Rng.Between(rng, 0, 3);
                    while (tieredSpells[selectedTier].Count == 0)
                    {
                        selectedTier = Rng.Between(rng, 0, 3);
                    }

                    WriteItemSpellData(tieredSpells[selectedTier].SpliceRandom(rng), weapon);
                }
            }
        }
Ejemplo n.º 37
0
        private List <List <byte> > Bucketize(List <byte> bytes, int bucketCount, int bucketSize, MT19337 rng)
        {
            var buckets = new List <List <byte> >();

            for (int i = 0; i < bucketCount; i++)
            {
                buckets.Add(new List <byte>());
            }

            int index = 0;

            while (index < bucketCount)
            {
                buckets[index].Add(bytes[index++]);
            }
            while (index < bytes.Count)
            {
                var bucket = rng.Between(0, buckets.Count - 1);
                if (buckets[bucket].Count < bucketSize)
                {
                    buckets[bucket].Add(bytes[index++]);
                }
            }

            return(buckets);
        }
Ejemplo n.º 38
0
        public static List <Wall> DoSkyCastle4FMaze(MT19337 rng)
        {
            var cells = new EquivalenceNode <Cell> [8, 8];

            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    if (i % 2 == 0 || j % 2 == 0)
                    {
                        cells[j, i] = new EquivalenceNode <Cell>(new Cell(i, j));
                    }
                }
            }

            var walls = new List <Wall>();

            for (int i = 0; i < 8; i += 2)
            {
                for (int j = 0; j < 8; j++)
                {
                    if (j == 7)
                    {
                        walls.Add(new Wall(cells[j, i], cells[0, i]));
                    }
                    else
                    {
                        walls.Add(new Wall(cells[j, i], cells[j + 1, i]));
                    }
                }
            }
            for (int j = 0; j < 8; j += 2)
            {
                for (int i = 0; i < 8; i++)
                {
                    if (i == 7)
                    {
                        walls.Add(new Wall(cells[j, i], cells[j, 0]));
                    }
                    else
                    {
                        walls.Add(new Wall(cells[j, i], cells[j, i + 1]));
                    }
                }
            }

            walls.Shuffle(rng);

            for (int i = 0; i < walls.Count;)
            {
                if (!walls[i].one.IsEquivalentTo(walls[i].two))
                {
                    walls[i].one.MakeEquivalentTo(walls[i].two);
                    walls.RemoveAt(i);
                }
                else
                {
                    i++;
                }
            }

            return(walls);
        }
Ejemplo n.º 39
0
        public Item SelectVendorItem(List <Item> incentives, List <Item> nonincentives, List <Item> treasurePool, List <IRewardSource> incentiveLocationPool, MT19337 rng)
        {
            if (!(bool)_flags.NPCItems)
            {
                return(Item.Bottle);
            }

            var itemShopItem        = Item.Cabin;
            var validShopIncentives = incentives.Where(x => x > Item.None && x <= Item.Soft).ToList();

            if (validShopIncentives.Any() && incentiveLocationPool.Any(x => x.Address == ItemLocations.CaravanItemShop1.Address))
            {
                itemShopItem = validShopIncentives.PickRandom(rng);
                incentives.Remove(itemShopItem);
            }
            else
            {
                itemShopItem = treasurePool.Concat(nonincentives).Where(x => x > Item.None && x <= Item.Soft && x != Item.Shard).ToList().PickRandom(rng);
                if (!nonincentives.Remove(itemShopItem))
                {
                    treasurePool.Remove(itemShopItem);
                }
            }

            return(itemShopItem);
        }
Ejemplo n.º 40
0
        public void ShuffleMagicLevels(MT19337 rng, bool keepPermissions, bool tieredShuffle, bool mixSpellbooks, bool noSpellcrafter)
        {
            var magicSpells = GetSpells();

            if (tieredShuffle && noSpellcrafter)
            {
                // if we are doing a tiered shuffle, swap the position of TMPR and SABR before further shuffling for balance purposes
                MagicSpell tmpTMPR = magicSpells[14];
                magicSpells[14] = magicSpells[54];
                magicSpells[54] = tmpTMPR;
            }

            // First we have to un-interleave white and black spells.
            var whiteSpells = magicSpells.Where((spell, i) => (i / 4) % 2 == 0).ToList();
            var blackSpells = magicSpells.Where((spell, i) => (i / 4) % 2 == 1).ToList();

            if (tieredShuffle)
            {
                // weigh spell probability of landing in a tier based on where it was in the original game
                var whiteSpellList      = new List <MagicSpell> [3];
                var blackSpellList      = new List <MagicSpell> [3];
                var whiteSpellFinalList = new List <MagicSpell> [3];
                var blackSpellFinalList = new List <MagicSpell> [3];
                int mergedSpellDoubler  = 1;
                whiteSpellList[0] = magicSpells.Where((spell, i) => (i / 4) % 2 == 0 && i < 24).ToList();
                whiteSpellList[1] = magicSpells.Where((spell, i) => (i / 4) % 2 == 0 && i < 48 && i >= 24).ToList();
                whiteSpellList[2] = magicSpells.Where((spell, i) => (i / 4) % 2 == 0 && i >= 48).ToList();
                blackSpellList[0] = magicSpells.Where((spell, i) => (i / 4) % 2 == 1 && i < 24).ToList();
                blackSpellList[1] = magicSpells.Where((spell, i) => (i / 4) % 2 == 1 && i < 48 && i >= 24).ToList();
                blackSpellList[2] = magicSpells.Where((spell, i) => (i / 4) % 2 == 1 && i >= 48).ToList();
                if (mixSpellbooks)
                {
                    whiteSpellList[0]  = whiteSpellList[0].Concat(blackSpellList[0]).ToList();
                    whiteSpellList[1]  = whiteSpellList[1].Concat(blackSpellList[1]).ToList();
                    whiteSpellList[2]  = whiteSpellList[2].Concat(blackSpellList[2]).ToList();
                    mergedSpellDoubler = 2;
                }
                whiteSpellFinalList[0] = new List <MagicSpell> {
                };
                whiteSpellFinalList[1] = new List <MagicSpell> {
                };
                whiteSpellFinalList[2] = new List <MagicSpell> {
                };
                blackSpellFinalList[0] = new List <MagicSpell> {
                };
                blackSpellFinalList[1] = new List <MagicSpell> {
                };
                blackSpellFinalList[2] = new List <MagicSpell> {
                };
                whiteSpells.Clear();
                blackSpells.Clear();
                foreach (MagicSpell spell in whiteSpellList[2])
                {
                    // 70% chance of tier 7-8, 25% chance of tier 4-6, 5% chance of tier 1-3
                    int diceRoll = rng.Between(0, 19);
                    if (diceRoll < 14)
                    {
                        whiteSpellFinalList[2].Add(spell);
                    }
                    else if (diceRoll < 19)
                    {
                        whiteSpellFinalList[1].Add(spell);
                    }
                    else
                    {
                        whiteSpellFinalList[0].Add(spell);
                    }
                }
                foreach (MagicSpell spell in whiteSpellList[1])
                {
                    // 60% chance of tier 4-6, 25% chance of tier 1-3, 15% chance of tier 7-8
                    // if a section of the final list is full, move to another section
                    int diceRoll = rng.Between(0, 19);
                    if (diceRoll < 12)
                    {
                        if (whiteSpellFinalList[1].Count >= 12 * mergedSpellDoubler)
                        {
                            if (whiteSpellFinalList[0].Count >= 12 * mergedSpellDoubler)
                            {
                                whiteSpellFinalList[2].Add(spell);
                            }
                            else
                            {
                                whiteSpellFinalList[0].Add(spell);
                            }
                        }
                        else
                        {
                            whiteSpellFinalList[1].Add(spell);
                        }
                    }
                    else if (diceRoll < 17)
                    {
                        if (whiteSpellFinalList[0].Count >= 12 * mergedSpellDoubler)
                        {
                            if (whiteSpellFinalList[1].Count >= 12 * mergedSpellDoubler)
                            {
                                whiteSpellFinalList[2].Add(spell);
                            }
                            else
                            {
                                whiteSpellFinalList[1].Add(spell);
                            }
                        }
                        else
                        {
                            whiteSpellFinalList[0].Add(spell);
                        }
                    }
                    else
                    {
                        if (whiteSpellFinalList[2].Count >= 8 * mergedSpellDoubler)
                        {
                            if (whiteSpellFinalList[1].Count >= 12 * mergedSpellDoubler)
                            {
                                whiteSpellFinalList[0].Add(spell);
                            }
                            else
                            {
                                whiteSpellFinalList[1].Add(spell);
                            }
                        }
                        else
                        {
                            whiteSpellFinalList[2].Add(spell);
                        }
                    }
                }
                foreach (MagicSpell spell in whiteSpellList[0])
                {
                    // fill the remaining tiers with the tier 1-3 base magic
                    if (whiteSpellFinalList[0].Count >= 12 * mergedSpellDoubler)
                    {
                        if (whiteSpellFinalList[1].Count >= 12 * mergedSpellDoubler)
                        {
                            whiteSpellFinalList[2].Add(spell);
                        }
                        else
                        {
                            whiteSpellFinalList[1].Add(spell);
                        }
                    }
                    else
                    {
                        whiteSpellFinalList[0].Add(spell);
                    }
                }
                // and repeat the process for black magic if we didn't mix spellbooks
                if (mixSpellbooks)
                {
                    // if we mixed spellbooks, split the white (merged) spellbook in halves to set the black spell list
                    blackSpellFinalList[0] = whiteSpellFinalList[0].Take(12).ToList();
                    whiteSpellFinalList[0] = whiteSpellFinalList[0].Except(blackSpellFinalList[0]).ToList();
                    blackSpellFinalList[1] = whiteSpellFinalList[1].Take(12).ToList();
                    whiteSpellFinalList[1] = whiteSpellFinalList[1].Except(blackSpellFinalList[1]).ToList();
                    blackSpellFinalList[2] = whiteSpellFinalList[2].Take(8).ToList();
                    whiteSpellFinalList[2] = whiteSpellFinalList[2].Except(blackSpellFinalList[2]).ToList();
                }
                else
                {
                    foreach (MagicSpell spell in blackSpellList[2])
                    {
                        // 70% chance of tier 7-8, 25% chance of tier 4-6, 5% chance of tier 1-3
                        int diceRoll = rng.Between(0, 19);
                        if (diceRoll < 14)
                        {
                            blackSpellFinalList[2].Add(spell);
                        }
                        else if (diceRoll < 19)
                        {
                            blackSpellFinalList[1].Add(spell);
                        }
                        else
                        {
                            blackSpellFinalList[0].Add(spell);
                        }
                    }
                    foreach (MagicSpell spell in blackSpellList[1])
                    {
                        // 60% chance of tier 4-6, 25% chance of tier 1-3, 15% chance of tier 7-8
                        // if a section of the final list is full, move to another section
                        int diceRoll = rng.Between(0, 19);
                        if (diceRoll < 12)
                        {
                            if (blackSpellFinalList[1].Count >= 12)
                            {
                                if (blackSpellFinalList[0].Count >= 12)
                                {
                                    blackSpellFinalList[2].Add(spell);
                                }
                                else
                                {
                                    blackSpellFinalList[0].Add(spell);
                                }
                            }
                            else
                            {
                                blackSpellFinalList[1].Add(spell);
                            }
                        }
                        else if (diceRoll < 17)
                        {
                            if (blackSpellFinalList[0].Count >= 12)
                            {
                                if (blackSpellFinalList[1].Count >= 12)
                                {
                                    blackSpellFinalList[2].Add(spell);
                                }
                                else
                                {
                                    blackSpellFinalList[1].Add(spell);
                                }
                            }
                            else
                            {
                                blackSpellFinalList[0].Add(spell);
                            }
                        }
                        else
                        {
                            if (blackSpellFinalList[2].Count >= 8)
                            {
                                if (blackSpellFinalList[1].Count >= 12)
                                {
                                    blackSpellFinalList[0].Add(spell);
                                }
                                else
                                {
                                    blackSpellFinalList[1].Add(spell);
                                }
                            }
                            else
                            {
                                blackSpellFinalList[2].Add(spell);
                            }
                        }
                    }
                    foreach (MagicSpell spell in blackSpellList[0])
                    {
                        // fill the remaining tiers with the tier 1-3 base magic
                        if (blackSpellFinalList[0].Count >= 12)
                        {
                            if (blackSpellFinalList[1].Count >= 12)
                            {
                                blackSpellFinalList[2].Add(spell);
                            }
                            else
                            {
                                blackSpellFinalList[1].Add(spell);
                            }
                        }
                        else
                        {
                            blackSpellFinalList[0].Add(spell);
                        }
                    }
                }
                // shuffle each of the final lists
                foreach (List <MagicSpell> spellList in whiteSpellFinalList)
                {
                    spellList.Shuffle(rng);
                }
                if (!mixSpellbooks)
                {
                    foreach (List <MagicSpell> spellList in blackSpellFinalList)
                    {
                        spellList.Shuffle(rng);
                    }
                }
                // and append each in turn to the whitespells / blackspells list
                whiteSpells = whiteSpells.Concat(whiteSpellFinalList[0]).ToList();
                whiteSpells = whiteSpells.Concat(whiteSpellFinalList[1]).ToList();
                whiteSpells = whiteSpells.Concat(whiteSpellFinalList[2]).ToList();
                blackSpells = blackSpells.Concat(blackSpellFinalList[0]).ToList();
                blackSpells = blackSpells.Concat(blackSpellFinalList[1]).ToList();
                blackSpells = blackSpells.Concat(blackSpellFinalList[2]).ToList();
            }
            else
            {
                if (mixSpellbooks)
                {
                    var mergedList = magicSpells.ToList();
                    mergedList.Shuffle(rng);
                    whiteSpells = mergedList.Where((spell, i) => (i / 4) % 2 == 0).ToList();
                    blackSpells = mergedList.Where((spell, i) => (i / 4) % 2 == 1).ToList();
                }
                else
                {
                    whiteSpells.Shuffle(rng);
                    blackSpells.Shuffle(rng);
                }
            }

            // Now we re-interleave the spells.
            var shuffledSpells = new List <MagicSpell>();

            for (int i = 0; i < MagicCount; i++)
            {
                var sourceIndex = 4 * (i / 8) + i % 4;
                if ((i / 4) % 2 == 0)
                {
                    shuffledSpells.Add(whiteSpells[sourceIndex]);
                }
                else
                {
                    shuffledSpells.Add(blackSpells[sourceIndex]);
                }
            }

            Put(MagicOffset, shuffledSpells.Select(spell => spell.Data).Aggregate((seed, next) => seed + next));
            Put(MagicNamesOffset, shuffledSpells.Select(spell => spell.Name).Aggregate((seed, next) => seed + next));
            Put(MagicTextPointersOffset, shuffledSpells.Select(spell => spell.TextPointer).ToArray());

            if (keepPermissions)
            {
                // Shuffle the permissions the same way the spells were shuffled.
                for (int c = 0; c < MagicPermissionsCount; c++)
                {
                    var oldPermissions = Get(MagicPermissionsOffset + c * MagicPermissionsSize, MagicPermissionsSize);

                    var newPermissions = new byte[MagicPermissionsSize];
                    for (int i = 0; i < 8; i++)
                    {
                        for (int j = 0; j < 8; j++)
                        {
                            var oldIndex      = shuffledSpells[8 * i + j].Index;
                            var oldPermission = (oldPermissions[oldIndex / 8] & (0x80 >> oldIndex % 8)) >> (7 - oldIndex % 8);
                            newPermissions[i] |= (byte)(oldPermission << (7 - j));
                        }
                    }

                    Put(MagicPermissionsOffset + c * MagicPermissionsSize, newPermissions);
                }
            }

            // Map old indices to new indices.
            var newIndices = new byte[MagicCount];

            for (byte i = 0; i < MagicCount; i++)
            {
                newIndices[shuffledSpells[i].Index] = i;
            }

            // Fix enemy spell pointers to point to where the spells are now.
            var scripts = Get(ScriptOffset, ScriptSize * ScriptCount).Chunk(ScriptSize);

            foreach (var script in scripts)
            {
                // Bytes 2-9 are magic spells.
                for (int i = 2; i < 10; i++)
                {
                    if (script[i] != 0xFF)
                    {
                        script[i] = newIndices[script[i]];
                    }
                }
            }
            Put(ScriptOffset, scripts.SelectMany(script => script.ToBytes()).ToArray());

            // Fix weapon and armor spell pointers to point to where the spells are now.
            var weapons = Get(WeaponOffset, WeaponSize * WeaponCount).Chunk(WeaponSize);

            foreach (var weapon in weapons)
            {
                if (weapon[3] != 0x00)
                {
                    weapon[3] = (byte)(newIndices[weapon[3] - 1] + 1);
                }
            }
            Put(WeaponOffset, weapons.SelectMany(weapon => weapon.ToBytes()).ToArray());

            var armors = Get(ArmorOffset, ArmorSize * ArmorCount).Chunk(ArmorSize);

            foreach (var armor in armors)
            {
                if (armor[3] != 0x00)
                {
                    armor[3] = (byte)(newIndices[armor[3] - 1] + 1);
                }
            }
            Put(ArmorOffset, armors.SelectMany(armor => armor.ToBytes()).ToArray());

            // Fix the crazy out of battle spell system.
            var outOfBattleSpellOffset = MagicOutOfBattleOffset;

            for (int i = 0; i < MagicOutOfBattleCount; i++)
            {
                var oldSpellIndex = Data[outOfBattleSpellOffset] - 0xB0;
                var newSpellIndex = newIndices[oldSpellIndex];

                Put(outOfBattleSpellOffset, new[] { (byte)(newSpellIndex + 0xB0) });

                outOfBattleSpellOffset += MagicOutOfBattleSize;
            }

            // Confused enemies are supposed to cast FIRE, so figure out where FIRE ended up.
            var newFireSpellIndex = shuffledSpells.FindIndex(spell => spell.Data == magicSpells[FireSpellIndex].Data);

            Put(ConfusedSpellIndexOffset, new[] { (byte)newFireSpellIndex });
        }
Ejemplo n.º 41
0
        protected override ItemPlacementResult DoSanePlacement(MT19337 rng, ItemPlacementContext ctx)
        {
            _sanityCounter = 0;
            var incentiveLocationPool   = _incentivesData.IncentiveLocations.ToList();
            var preBlackOrbLocationPool = _incentivesData.AllValidPreBlackOrbItemLocations.ToList();
            var preBlackOrbUnincentivizedLocationPool = preBlackOrbLocationPool.Where(x => !incentiveLocationPool.Any(y => y.Address == x.Address)).ToList();

            if ((bool)_flags.LooseExcludePlacedDungeons)
            {
                preBlackOrbUnincentivizedLocationPool = IncentivizedDungeons();
            }

            Dictionary <MapLocation, Tuple <List <MapChange>, AccessRequirement> > fullLocationRequirements = _overworldMap.FullLocationRequirements;
            Dictionary <MapLocation, OverworldTeleportIndex> overridenOverworld = _overworldMap.OverriddenOverworldLocations;

            List <IRewardSource> placedItems  = null;
            List <Item>          treasurePool = null;
            var itemShopItem = Item.Bottle;

            do
            {
                _sanityCounter++;
                if (_sanityCounter > 20)
                {
                    throw new InsaneException("Item Placement could not meet incentivization requirements!");
                }
                // 1. (Re)Initialize lists inside of loop
                placedItems = ctx.Forced.ToList();
                var incentives    = ctx.Incentivized.ToList();
                var nonincentives = ctx.Unincentivized.ToList();
                treasurePool = ctx.AllTreasures.ToList();
                incentives.Shuffle(rng);
                nonincentives.Shuffle(rng);

                while (incentives.Count() > incentiveLocationPool.Count())
                {
                    nonincentives.Add(incentives.SpliceRandom(rng));
                }

                if (((bool)_flags.NPCItems) || ((bool)_flags.NPCFetchItems))
                {
                    // Identify but don't place caravan item first because among incentive locations it has the smallest set of possible items
                    itemShopItem = SelectVendorItem(incentives, nonincentives, treasurePool, incentiveLocationPool, rng);

                    // unrestricted items can get placed anywhere in the game. Everything else will only be placed where we can reach at this point.
                    List <Item> unrestricted = new List <Item> {
                        Item.Key, Item.Canoe, Item.Floater
                    };

                    // We will place these items in this very order so that we can ensure the bridge is early, and we don't need the floater to find the ship.
                    List <Item> fixedPlacements = new List <Item> {
                        Item.Key, Item.Bridge, Item.Canoe
                    };
                    List <Item> nextPlacements = new List <Item> {
                        Item.Ship, Item.Canal
                    };
                    List <Item> lastPlacements = new List <Item> {
                        Item.Floater, Item.Lute, Item.Crown, Item.Crystal, Item.Herb, Item.Tnt, Item.Adamant,
                        Item.Slab, Item.Ruby, Item.Rod, Item.Chime, Item.Tail, Item.Cube, Item.Bottle, Item.Oxyale
                    };

                    if ((bool)_flags.EarlierRuby)
                    {
                        nextPlacements.Add(Item.Ruby);
                        lastPlacements.Remove(Item.Ruby);
                    }

                    nextPlacements.Shuffle(rng);
                    lastPlacements.Shuffle(rng);
                    var allPlacements = fixedPlacements.Concat(nextPlacements).Concat(lastPlacements);

                    foreach (var item in allPlacements)
                    {
                        if (placedItems.Any(x => x.Item == item))
                        {
                            continue;
                        }

                        if (item == itemShopItem)
                        {
                            placedItems.Add(new ItemShopSlot(_caravanItemLocation, itemShopItem));
                            itemShopItem = Item.None;
                            continue;
                        }

                        var(_, mapLocations, requirements) = CheckSanity(placedItems, fullLocationRequirements, _flags);
                        var isIncentive  = incentives.Contains(item);
                        var locationPool = isIncentive ? incentiveLocationPool : preBlackOrbUnincentivizedLocationPool;
                        var itemPool     = isIncentive ? incentives : nonincentives;

                        System.Diagnostics.Debug.Assert(itemPool.Contains(item));

                        // Can we find a home for this item at this point in the exploration?
                        var rewardSources = locationPool.Where(x => !placedItems.Any(y => y.Address == x.Address) &&
                                                               x.Address != ItemLocations.CaravanItemShop1.Address &&
                                                               (unrestricted.Contains(item) || IsRewardSourceAccessible(x, requirements, mapLocations)))
                                            .ToList();

                        // If we can great, if not, we'll revisit after we get through everything.
                        if (rewardSources.Any())
                        {
                            itemPool.Remove(item);
                            placedItems.Add(NewItemPlacement(rewardSources.PickRandom(rng), item));
                        }
                    }
                }

                // This is a junk item that didn't get placed in the above loop.
                if (!placedItems.Any(item => item.Address == _caravanItemLocation.Address))
                {
                    placedItems.Add(new ItemShopSlot(_caravanItemLocation, itemShopItem));
                }

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

                // 6. Then place remanining incentive items and unincentivized quest items in any other chest before ToFR
                var leftoverItems = incentives.Concat(nonincentives).ToList();
                leftoverItems.Shuffle(rng);

                var leftoverItemLocations = preBlackOrbLocationPool.Where(x => !placedItems.Any(y => y.Address == x.Address)).ToList();

                if ((bool)_flags.LooseExcludePlacedDungeons)
                {
                    leftoverItemLocations = preBlackOrbUnincentivizedLocationPool.Where(x => !placedItems.Any(y => y.Address == x.Address)).ToList();
                }

                foreach (var leftoverItem in leftoverItems)
                {
                    placedItems.Add(NewItemPlacement(leftoverItemLocations.SpliceRandom(rng), leftoverItem));
                }

                // 7. Check sanity and loop if needed
            } while (!CheckSanity(placedItems, fullLocationRequirements, _flags).Complete);

            return(new ItemPlacementResult {
                PlacedItems = placedItems, RemainingTreasures = treasurePool
            });
        }
 public DefaultHintPlacmentProvider(MT19337 _rng, NPCdata _npcData, Flags _flags, OverworldMap _overworldMap, FF1Rom _rom) : base(_rng, _npcData, _flags, _overworldMap, _rom)
 {
     PlacementPools = StrategyDic.Select(s => (key: s.Key, values: s.Value.SelectMany(l => LocationDic[l]))).ToDictionary(s => s.key, s => s.values.ToList());
 }
Ejemplo n.º 43
0
        public List <IRewardSource> PlaceSaneItems(MT19337 rng)
        {
            var incentivePool = _incentivesData.IncentiveItems.Where(x => _allTreasures.Contains(x)).ToList();
            var forcedItems   = _incentivesData.ForcedItemPlacements.ToList();

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

            var treasurePool = _allTreasures.ToList();

            if ((bool)_flags.GuaranteedRuseItem)
            {
                unincentivizedQuestItems.Add(Item.PowerRod);
            }
            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);
            }

            ItemPlacementContext ctx = new ItemPlacementContext
            {
                Forced         = forcedItems,
                Incentivized   = incentivePool,
                Unincentivized = unincentivizedQuestItems,
                AllTreasures   = treasurePool.ToList(),
            };

            ItemPlacementResult result = DoSanePlacement(rng, ctx);
            var placedItems            = result.PlacedItems;

            treasurePool = result.RemainingTreasures;

            if ((bool)_flags.FreeBridge)
            {
                placedItems = placedItems.Select(x => x.Item != Item.Bridge ? x : NewItemPlacement(x, ReplacementItem)).ToList();
            }
            if ((bool)_flags.FreeAirship)
            {
                placedItems = placedItems.Select(x => x.Item != Item.Floater ? x : NewItemPlacement(x, ReplacementItem)).ToList();
            }
            if ((bool)_flags.FreeShip)
            {
                placedItems = placedItems.Select(x => x.Item != Item.Ship ? x : NewItemPlacement(x, ReplacementItem)).ToList();
            }
            if ((bool)_flags.FreeCanal)
            {
                placedItems = placedItems.Select(x => x.Item != Item.Canal ? x : NewItemPlacement(x, ReplacementItem)).ToList();
            }
            if ((bool)_flags.FreeCanoe)
            {
                placedItems = placedItems.Select(x => x.Item != Item.Canoe ? x : NewItemPlacement(x, ReplacementItem)).ToList();
            }
            if ((bool)_flags.FreeLute)
            {
                placedItems = placedItems.Select(x => x.Item != Item.Lute ? x : NewItemPlacement(x, ReplacementItem)).ToList();
            }
            if ((bool)_flags.FreeTail || (bool)_flags.NoTail)
            {
                placedItems = placedItems.Select(x => x.Item != Item.Tail ? x : NewItemPlacement(x, ReplacementItem)).ToList();
            }

            if (_flags.Spoilers || Debugger.IsAttached)
            {
                Console.WriteLine($"ItemPlacement::PlaceSaneItems required {_sanityCounter} iterations.");
                Console.WriteLine("");
                Console.WriteLine("Item     Entrance  ->  Floor  ->  Source                             Requirements");
                Console.WriteLine("----------------------------------------------------------------------------------------------------");

                var sorted = placedItems.Where(item => item.Item != Item.Shard).ToList();
                sorted.Sort((IRewardSource lhs, IRewardSource rhs) => lhs.Item.ToString().CompareTo(rhs.Item.ToString()));
                sorted.ForEach(item =>
                {
                    if (_overworldMap.FullLocationRequirements.TryGetValue(item.MapLocation, out var flr))
                    {
                        var overworldLocation = item.MapLocation.ToString();
                        if (_overworldMap.OverriddenOverworldLocations != null && _overworldMap.OverriddenOverworldLocations.TryGetValue(item.MapLocation, out var overriden))
                        {
                            overworldLocation = overriden.ToString();
                        }

                        var itemStr = item.Item.ToString().PadRight(9);
                        var locStr  = $"{overworldLocation} -> {item.MapLocation} -> {item.Name} ".PadRight(60);
                        var changes = $"({String.Join(" OR ", flr.Item1.Select(mapChange => mapChange.ToString()).ToArray())})";
                        var reqs    = flr.Item2.ToString().CompareTo("None") == 0 ? "" : $" AND {flr.Item2.ToString()}";
                        Console.WriteLine($"{itemStr}{locStr}{changes}{reqs}");
                    }
                });
            }

            // 8. Place all remaining unincentivized treasures or incentivized non-quest items that weren't placed
            var itemLocationPool = _incentivesData.AllValidItemLocations.ToList();

            itemLocationPool = itemLocationPool.Where(x => !x.IsUnused && !placedItems.Any(y => y.Address == x.Address)).ToList();

            MoreConsumableChests.Work(_flags, treasurePool, rng);

            if ((bool)_flags.NoMasamune)
            {
                // Remove Masamune chest from shuffle
                treasurePool.Remove(Item.Masamune);
                treasurePool.Add(Item.Cabin);
            }
            else if ((bool)_flags.GuaranteedMasamune)
            {
                // Remove Masamune chest from shuffle, Remove Cabin from item pool
                itemLocationPool = itemLocationPool.Where(x => !x.Equals(ItemLocations.ToFRMasmune)).ToList();
                treasurePool.Remove(Item.Cabin);

                // Send Masamune Home is ignored when Masamune is incentivized
                if (!incentivePool.Contains(Item.Masamune))
                {
                    if ((bool)_flags.SendMasamuneHome)
                    {
                        // Remove Masamune from treasure pool (This will also causes Masamune to not be placed by RandomLoot)
                        treasurePool.Remove(Item.Masamune);
                        treasurePool.Add(Item.Cabin);
                    }
                }
            }

            foreach (var placedItem in placedItems)
            {
                incentivePool.Remove(placedItem.Item);
            }
            treasurePool.AddRange(incentivePool);

            Debug.Assert(treasurePool.Count() == itemLocationPool.Count());

            if ((bool)_flags.RandomLoot)
            {
                // We want to leave out anything incentivized (and thus already placed), but
                // add all the other stuff that you can't find in vanilla.
                var randomTreasure = treasurePool.ToList();
                randomTreasure.AddRange(ItemLists.CommonWeaponTier);
                randomTreasure.AddRange(ItemLists.CommonArmorTier);
                randomTreasure.Add(Item.CatClaw);
                randomTreasure.Add(Item.SteelArmor);

                ItemGenerator generator = new ItemGenerator(randomTreasure, _flags.WorldWealth);
                treasurePool = treasurePool.Select(treasure => generator.GetItem(rng)).ToList();
            }

            if ((bool)_flags.BetterTrapChests)
            {
                // First we'll make a list of all 'notable' treasure.
                var notableTreasureList = new List <Item>()
                                          .Concat(ItemLists.UberTier)
                                          .Concat(ItemLists.LegendaryWeaponTier)
                                          .Concat(ItemLists.LegendaryArmorTier)
                                          .Concat(ItemLists.RareWeaponTier)
                                          .Concat(ItemLists.RareArmorTier);
                // Convert the list to a HashSet since we'll be doing lookups in it.
                var notableTreasure = new HashSet <Item>(notableTreasureList);

                // We sort the treasure pool based on value (sort of) and pull out the highest ranked ones to put
                // in the trap chests we picked out.
                var notableTreasurePool = treasurePool.Where(item => notableTreasure.Contains(item)).ToList();

                // Since some chests might be incentivized, remove those that aren't in the pool.
                var trapChestPool = TrapChests.Where(chest => itemLocationPool.Contains(chest));

                foreach (var chest in trapChestPool)
                {
                    // It seems unlikely that is possible, but just in case.
                    if (!notableTreasurePool.Any())
                    {
                        break;
                    }

                    // Pick a random treasure and place it.
                    var treasure = notableTreasurePool.SpliceRandom(rng);
                    placedItems.Add(NewItemPlacement(chest, treasure));

                    // Since it was placed, remove both the item and location from the remaining pool.
                    treasurePool.Remove(treasure);
                    itemLocationPool.Remove(chest);
                }

                // This should still be true at the end, so make sure it is
                Debug.Assert(treasurePool.Count() == itemLocationPool.Count());
            }

            treasurePool.Shuffle(rng);
            itemLocationPool.Shuffle(rng);

            var leftovers = treasurePool.Zip(itemLocationPool, (treasure, location) => NewItemPlacement(location, treasure));

            placedItems.AddRange(leftovers);
            return(placedItems);
        }
Ejemplo n.º 44
0
        private void GenerateButton_Click(object sender, RoutedEventArgs e)
        {
            var rom = new FF1Rom(_filename);
            var rng = new MT19337(BitConverter.ToUInt32(_seed, 0));

            if (TreasuresCheckBox.IsChecked == true)
            {
                rom.ShuffleTreasures(rng);
            }

            if (ShopsCheckBox.IsChecked == true)
            {
                rom.ShuffleShops(rng);
            }

            if (MagicShopsCheckBox.IsChecked == true)
            {
                rom.ShuffleMagicShops(rng);
            }

            if (MagicLevelsCheckBox.IsChecked == true)
            {
                rom.ShuffleMagicLevels(rng, MagicPermissionsCheckBox.IsChecked ?? false);
            }

            if (EnemyScriptsCheckBox.IsChecked == true)
            {
                rom.ShuffleEnemyScripts(rng);
            }

            if (EnemySkillsSpellsCheckBox.IsChecked == true)
            {
                rom.ShuffleEnemySkillsSpells(rng);
            }

            if (EnemyStatusAttacksCheckBox.IsChecked == true)
            {
                rom.ShuffleEnemyStatusAttacks(rng);
            }

            if (EarlyRodCheckBox.IsChecked == true)
            {
                rom.EnableEarlyRod();
            }

            if (NoPartyShuffleCheckBox.IsChecked == true)
            {
                rom.DisablePartyShuffle();
            }

            if (PriceScaleFactorSlider.Value > 1)
            {
                rom.ScalePrices(PriceScaleFactorSlider.Value, rng);
            }

            if (EnemyScaleFactorSlider.Value > 1)
            {
                rom.ScaleEnemyStats(EnemyScaleFactorSlider.Value, rng);
            }

            if (ExpMultiplierSlider.Value > 1 || ExpBonusSlider.Value > 0)
            {
                rom.ExpGoldBoost(ExpBonusSlider.Value*10, ExpMultiplierSlider.Value);
            }

            var seedText = _seed.ToHex();
            rom.WriteSeedAndFlags(Version, seedText, FlagsTextBox.Text);

            var fileRoot = _filename.Substring(0, _filename.LastIndexOf("."));
            var outputFilename = $"{fileRoot}_{FlagsTextBox.Text}_{seedText}.nes";
            rom.Save(outputFilename);

            MessageBox.Show($"Finished generating new ROM: {outputFilename}", "Done");
        }