예제 #1
0
        public void PartyRandomize(MT19337 rng, int numberForced)
        {
            // Always randomize all 4 default members (but don't force if not needed)
            Data[0x3A0AE] = (byte)rng.Between(0, 5);
            Data[0x3A0BE] = (byte)rng.Between(0, 5);
            Data[0x3A0CE] = (byte)rng.Between(0, 5);
            Data[0x3A0DE] = (byte)rng.Between(0, 5);

            if (numberForced <= 0)
            {
                return;
            }

            Data[0x39D35] = 0xE0;
            Data[0x39D36] = (byte)(numberForced * 0x10);
            Put(0x39D37, Blob.FromHex("30DFFE0003BD0003E906D0039D0003A9018537"));

            /* Starting at 0x39D35 (which is just after LDX char_index)
             * CPX ____(numberForced * 0x10)____
             * BMI @MainLoop
             * INC ptygen_class, X
             * LDA ptygen_class, X
             * SBC #$06
             * BNE :+
             *   STA ptygen_class, X
             * : LDA #$01
             *   STA menustall
             */
        }
예제 #2
0
        private void KillShops(ShopType shopType, ShopKillMode mode, ShopKillFactor factor, bool excludeConeria)
        {
            if (mode == ShopKillMode.Random)
            {
                mode = (ShopKillMode)Math.Max(rng.Between(-3, 4), 0);
            }
            if (factor == ShopKillFactor.KillRandom)
            {
                factor = (ShopKillFactor)rng.Between(0, 4);
            }

            switch (mode)
            {
            case ShopKillMode.WholeShops:
                KillWholeShops(shopType, factor, excludeConeria);
                break;

            case ShopKillMode.AllItemsOfAKind:
                KillAllItemsOfAKind(shopType, factor, excludeConeria);
                break;

            case ShopKillMode.Spread:
                KillSpreads(shopType, factor, excludeConeria);
                break;

            case ShopKillMode.Chaos:
                KillChaos(shopType, factor, excludeConeria);
                break;

            default:
                break;
            }
        }
예제 #3
0
        public void GenerateRooms(MT19337 rng)
        {
            // this function generates all the rooms and hallways for this BSPMapNode and all of its children.
            if (LeftChild == null && RightChild == null)
            {
                if (WalkableSpace != null)
                {
                    return;
                }

                // the room can be between 3 x 3 tiles to the size of the leaf - 2.
                var roomWidth  = rng.Between(BSPTreeEngine.MIN_ROOM_WIDTH, Width - 2);
                var roomHeight = rng.Between(BSPTreeEngine.MIN_ROOM_HEIGHT, Height - 2);

                // place the room within the BSPMapNode, but don't put it right
                // against the side of the BSPMapNode (that would merge rooms together)
                var roomStartX = rng.Between(1, Width - roomWidth - 1);
                var roomStartY = rng.Between(1, Height - roomHeight - 1);
                WalkableSpace = new Rectangle(X + roomStartX, Y + roomStartY, roomWidth, roomHeight);
                return;
            }

            // subleafs:
            LeftChild?.GenerateRooms(rng);
            RightChild?.GenerateRooms(rng);

            // both leafs exist. we know their GetRandomRoom shouldn't return null, because we just called GenerateRooms.
            if (LeftChild != null && RightChild != null)
            {
                MakeHallway(rng, (Rectangle)LeftChild.GetRandomRoom(rng), (Rectangle)RightChild.GetRandomRoom(rng));
            }
        }
예제 #4
0
        public static void Work(IItemPlacementFlags flags, List <Item> treasurePool, MT19337 rng)
        {
            int count = treasurePool.Count;

            var consumableChestSet    = ConsumableChestSets[flags.MoreConsumableChests];
            var extConsumableChestSet = flags.ExtConsumableSet != ExtConsumableSet.None ? ExtConsumableChestSets[flags.ExtConsumableChests] : ExtConsumableChestSets[ExtConsumableChestSet.None];

            if (flags.MoreConsumableChests == ConsumableChestSet.Random || flags.MoreConsumableChests == ConsumableChestSet.RandomLow)
            {
                consumableChestSet =
                    (
                        Tents : rng.Between(0, consumableChestSet.Tents),
                        Cabins : rng.Between(0, consumableChestSet.Cabins),
                        Houses : rng.Between(0, consumableChestSet.Houses),
                        Heals : rng.Between(0, consumableChestSet.Heals),
                        Pures : rng.Between(0, consumableChestSet.Pures),
                        Softs : rng.Between(0, consumableChestSet.Softs)
                    );
            }

            if (flags.ExtConsumableSet != ExtConsumableSet.None && (flags.ExtConsumableChests == ExtConsumableChestSet.Random || flags.ExtConsumableChests == ExtConsumableChestSet.RandomLow))
            {
                extConsumableChestSet =
                    (
                        WoodenNunchucks : rng.Between(0, extConsumableChestSet.WoodenNunchucks),
                        SmallKnives : rng.Between(0, extConsumableChestSet.SmallKnives),
                        WoodenRods : rng.Between(0, extConsumableChestSet.WoodenRods),
                        Rapiers : rng.Between(0, extConsumableChestSet.Rapiers)
                    );
            }

            int requestedchests = consumableChestSet.Tents + consumableChestSet.Cabins + consumableChestSet.Houses + consumableChestSet.Heals + consumableChestSet.Pures + consumableChestSet.Softs + extConsumableChestSet.WoodenNunchucks + extConsumableChestSet.SmallKnives + extConsumableChestSet.WoodenRods + extConsumableChestSet.Rapiers;

            int removedchests = 0;

            RemoveConsumableChests(flags, treasurePool, ref removedchests);
            RemoveGoldChests(treasurePool, requestedchests, ref removedchests);

            if (flags.ExtConsumableSet != ExtConsumableSet.None)
            {
                AddConsumableChests(treasurePool, extConsumableChestSet.WoodenNunchucks, Item.WoodenNunchucks, ref removedchests);
                AddConsumableChests(treasurePool, extConsumableChestSet.SmallKnives, Item.SmallKnife, ref removedchests);
                AddConsumableChests(treasurePool, extConsumableChestSet.WoodenRods, Item.WoodenRod, ref removedchests);
                AddConsumableChests(treasurePool, extConsumableChestSet.Rapiers, Item.Rapier, ref removedchests);
            }

            AddConsumableChests(treasurePool, consumableChestSet.Tents, Item.Tent, ref removedchests);
            AddConsumableChests(treasurePool, consumableChestSet.Cabins, Item.Cabin, ref removedchests);
            AddConsumableChests(treasurePool, consumableChestSet.Houses, Item.House, ref removedchests);
            AddConsumableChests(treasurePool, consumableChestSet.Heals, Item.Heal, ref removedchests);
            AddConsumableChests(treasurePool, consumableChestSet.Pures, Item.Pure, ref removedchests);
            AddConsumableChests(treasurePool, consumableChestSet.Softs, Item.Soft, ref removedchests);

            AddGoldChests(treasurePool, removedchests);

            if (treasurePool.Count != count)
            {
                throw new Exception("Sorry, I f****d it up!");
            }
        }
예제 #5
0
        public void UpdateMagicAutohitThreshold(MT19337 rng, AutohitThreshold threshold)
        {
            short limit = 300;

            switch (threshold)
            {
            case AutohitThreshold.Vanilla: limit = 300; break;

            case AutohitThreshold.Autohit600: limit = 600; break;

            case AutohitThreshold.Autohit900: limit = 900; break;

            case AutohitThreshold.Autohit1200: limit = 1200; break;

            case AutohitThreshold.Autohit65535: limit = short.MaxValue; break;

            case AutohitThreshold.Autohit300to600: limit = (short)(rng.Between(1, 2) * 300); break;

            case AutohitThreshold.Autohit300to900: limit = (short)(rng.Between(1, 3) * 300); break;

            case AutohitThreshold.Autohit300to1200: limit = (short)(rng.Between(1, 4) * 300); break;

            case AutohitThreshold.Any:
            {
                short[] any = { 300, 600, 900, 1200, short.MaxValue };
                limit = any.PickRandom(rng);
                break;
            }
            }

            // Set the low and high bytes of the limit which are then loaded and compared to the targets hp.
            Data[0x33AE0] = (byte)(limit & 0x00ff);
            Data[0x33AE5] = (byte)((limit >> 8) & 0x00ff);
        }
예제 #6
0
        public bool Split(MT19337 rng)
        {
            // begin splitting the leaf into two children
            if (LeftChild != null || RightChild != null)
            {
                return(false);    // we're already split! Abort!
            }
            // determine direction of split
            // if the width is >25% larger than height, we split vertically
            // if the height is >25% larger than the width, we split horizontally
            // otherwise we split randomly
            bool split_horizontally;

            if (Width > Height && Width / Height >= 1.25)
            {
                split_horizontally = false;
            }
            else if (Height > Width && Height / Width >= 1.25)
            {
                split_horizontally = true;
            }
            else
            {
                split_horizontally = rng.Between(0, 1) == 0;
            }

            int max = (split_horizontally ? Height : Width) - BSPTreeEngine.MIN_LEAF_SIZE;     // determine the maximum height or width

            if (max <= BSPTreeEngine.MIN_LEAF_SIZE)
            {
                return(false);                                         // the area is too small to split any more...
            }
            int split = rng.Between(BSPTreeEngine.MIN_LEAF_SIZE, max); // determine where we're going to split

            // create our left and right children based on the direction of the split
            if (split_horizontally)
            {
                LeftChild  = new BSPMapNode(X, Y, Width, split);
                RightChild = new BSPMapNode(X, Y + split, Width, Height - split);
            }
            else
            {
                LeftChild  = new BSPMapNode(X, Y, split, Height);
                RightChild = new BSPMapNode(X + split, Y, Width - split, Height);
            }

            return(true);    // split successful!
        }
예제 #7
0
        //offset lookups
        //0 - hit
        //1 - dmg
        //2 - crit
        //3 - weapon spell index
        //4 - elemental weaknesses
        //5 - type weaknesses
        //6 - weapon type sprite
        //7 - weapon sprite palette color

        public void RandomWeaponBonus(MT19337 rng, int min, int max, bool excludeMasa)
        {
            //get base stats
            Weapon currentWeapon;

            for (int i = 0; i < WeaponCount; i++)
            {
                if (i != 39 || !excludeMasa)
                {
                    currentWeapon = new Weapon(i, this);
                    int bonus = rng.Between(min, max);
                    if (bonus != 0)
                    {
                        //adjust stats
                        //clamp to 1 dmg min, 0 hit min, 50 hit maximum
                        currentWeapon.HitBonus = (byte)Math.Max(0, (int)(currentWeapon.HitBonus + (3 * bonus)));
                        currentWeapon.HitBonus = (byte)Math.Min(50, (int)(currentWeapon.HitBonus));
                        currentWeapon.Damage   = (byte)Math.Max(1, (int)currentWeapon.Damage + (2 * bonus));
                        currentWeapon.Crit     = (byte)Math.Max(1, (int)currentWeapon.Crit + (3 * bonus));

                        //change last two non icon characters to -/+bonus
                        string bonusString = string.Format((bonus > 0) ? "+{0}" : "{0}", bonus.ToString());
                        byte[] bonusBytes  = FF1Text.TextToBytes(bonusString);

                        int iconIndex = currentWeapon.NameBytes[6] > 200 && currentWeapon.NameBytes[6] != 255 ? 5 : 6;
                        for (int j = 0; j < bonusBytes.Length - 1; j++)
                        {
                            currentWeapon.NameBytes[iconIndex - j] = bonusBytes[bonusBytes.Length - 2 - j];
                        }

                        currentWeapon.writeWeaponMemory(this);
                    }
                }
            }
        }
예제 #8
0
        private static bool ConvertTriState(bool?tristate, MT19337 rng)
        {
            int  rngval = rng.Between(0, 1);
            bool rval   = tristate ?? (rngval == 0);

            return(rval);
        }
예제 #9
0
        public Rectangle?GetRandomRoom(MT19337 rng)
        {
            if (WalkableSpace != null)
            {
                return(WalkableSpace);
            }

            var leftRoom  = LeftChild.GetRandomRoom(rng);
            var rightRoom = RightChild.GetRandomRoom(rng);

            if (leftRoom == null)
            {
                if (rightRoom == null)
                {
                    return(null);
                }
                return(rightRoom);
            }

            if (rightRoom == null)
            {
                return(leftRoom);
            }

            // got both
            return((rng.Between(0, 1) == 0) ? leftRoom : rightRoom);
        }
예제 #10
0
        public void RandomArmorBonus(MT19337 rng, int min, int max, bool cleanNames)
        {
            //get base stats
            Armor currentArmor;

            for (int i = 0; i < ArmorCount; i++)
            {
                currentArmor = new Armor(i, this);
                int bonus = rng.Between(min, max);
                if (bonus != 0)
                {
                    //body armor(indexes 0 - 15) +2/-2 +2/-2 weight
                    //shield(indexes 16 - 24) +1/-1 +1/-1 weight
                    //helm(indexes 25 - 31) +1/-1 +1/-1 weight
                    //hand(indexes 32 - 39) +1/-1 +1/-1 weight
                    int armorTypeAbsorbBonus = i < 16 ? 2 : 1;
                    int armorTypeWeightBonus = i < 16 ? 2 : 1;

                    //adjust stats
                    //clamp minimums to 0 weight, and 1 absorb
                    currentArmor.Weight = (byte)Math.Max(0, currentArmor.Weight - (armorTypeWeightBonus * bonus));
                    currentArmor.Absorb = (byte)Math.Max(1, currentArmor.Absorb + (armorTypeAbsorbBonus * bonus));

                    //change last two non icon characters to -/+bonus
                    string bonusString = string.Format((bonus > 0) ? "+{0}" : "{0}", bonus.ToString());
                    byte[] bonusBytes  = FF1Text.TextToBytes(bonusString);

                    // Adjusts blursed armor names to be more understandable
                    if (cleanNames)
                    {
                        if (currentArmor.Name[0..6] == "Copper")
예제 #11
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);
        }
예제 #12
0
        public void ShuffleLeader(MT19337 rng)
        {
            byte leader = (byte)(rng.Between(0, 3) << 6);

            Data[0x7D8BC] = leader;
            Data[0x7E933] = leader;
        }
예제 #13
0
        //offset lookups
        //0 - hit
        //1 - dmg
        //2 - crit
        //3 - weapon spell index
        //4 - elemental weaknesses
        //5 - type weaknesses
        //6 - weapon type sprite
        //7 - weapon sprite palette color

        public List <int> RandomWeaponBonus(MT19337 rng, int min, int max, bool excludeMasa, bool cleanNames)
        {
            //get base stats
            Weapon     currentWeapon;
            List <int> blurseValues = new();

            for (int i = 0; i < WeaponCount; i++)
            {
                if (i != 39 || !excludeMasa)
                {
                    currentWeapon = new Weapon(i, this);
                    int bonus = rng.Between(min, max);
                    blurseValues.Add(bonus);
                    if (bonus != 0)
                    {
                        //adjust stats
                        //clamp to 1 dmg min, 0 hit min, 50 hit maximum
                        currentWeapon.HitBonus = (byte)Math.Max(0, (int)(currentWeapon.HitBonus + (3 * bonus)));
                        currentWeapon.HitBonus = (byte)Math.Min(50, (int)(currentWeapon.HitBonus));
                        currentWeapon.Damage   = (byte)Math.Max(1, (int)currentWeapon.Damage + (2 * bonus));
                        currentWeapon.Crit     = (byte)Math.Max(1, (int)currentWeapon.Crit + (3 * bonus));

                        // Shortens names to make more sense when there are only 4 letters available
                        // Most of these are only used with Weaponizer, but some are vanilla
                        if (cleanNames)
                        {
                            // I didn't use a switch+case here because of the mix of name lengths being checked, but I'm sure this could be more elegant and anyone can feel free to clean it up
                            if (currentWeapon.Name[0..5] == "Shock")
예제 #14
0
        public void RandomEnemyStatusAttacks(MT19337 rng, bool AllowUnsafePirates, bool DisableStunTouch)
        {
            var enemies = Get(EnemyOffset, EnemySize * EnemyCount).Chunk(EnemySize);

            List <(byte touch, byte element)> statusElements = new List <(byte touch, byte element)>()
            {
                (0x04, 0x02),                 //Poison Touch = Poison
                (0x08, 0x01),                 //Dark Touch = Status
                (0x10, 0x01),                 //Stun Touch = Status
                (0x20, 0x01),                 //Sleep Touch = Status
                (0x40, 0x01),                 //Mute Touch = Status
            };

            if (DisableStunTouch)
            {
                statusElements.Remove((0x10, 0x01));
            }

            (byte touch, byte element)deathElement = (0x01, 0x08);              //Death Touch = Death Element
            (byte touch, byte element)stoneElement = (0x02, 0x02);              //Stone Touch = Poison

            for (int i = 0; i < EnemyCount; i++)
            {
                if (!AllowUnsafePirates)
                {
                    if (i == 15)                     //pirates
                    {
                        continue;
                    }
                }

                int roll = rng.Between(0, 128);
                if (roll < 1)                 //1 vanilla death toucher
                {
                    //Death Touch
                    var(touch, element) = deathElement;
                }
                else if (roll < 2)                 //1 vanilla stone toucher
                {
                    //Stone Touch
                    var(touch, element) = stoneElement;
                }
                else if (roll < 37)                 //35 enemies with other assorted status touches
                {
                    var(touch, element) = statusElements.PickRandom(rng);
                    enemies[i][15]      = touch;
                    enemies[i][14]      = element;
                }
                else
                {
                    //Otherwise, the enemy has no touch or associated element.
                    enemies[i][14] = 0x00;
                    enemies[i][15] = 0x00;
                }
            }

            Put(EnemyOffset, enemies.SelectMany(enemy => enemy.ToBytes()).ToArray());
        }
        private void SwapMirage3F()
        {
            if (rng.Between(0, 1) == 0)
            {
                return;
            }

            var map = maps[(int)MapId.MirageTower3F];

            SwapTiles(map, 0x08, 0x01, 0x0E, 0x08);
            SwapTele(MapId.MirageTower3F, 0x08, 0x01, 0x0E, 0x08);

            DuplicateTile(MapId.MirageTower3F, 0x03, 0x07, 0x02, 0x07);
            DuplicateTile(MapId.MirageTower3F, 0x03, 0x08, 0x02, 0x08);
            DuplicateTile(MapId.MirageTower3F, 0x03, 0x09, 0x02, 0x09);
            DuplicateTile(MapId.MirageTower3F, 0x03, 0x0A, 0x02, 0x0A);

            SwapTiles(map, 0x08, 0x0B, 0x03, 0x08);
            SwapTiles(map, 0x08, 0x0C, 0x03, 0x09);
            SwapTiles(map, 0x08, 0x0D, 0x03, 0x0A);
            SwapTiles(map, 0x08, 0x0E, 0x03, 0x0B);

            DuplicateTile(MapId.MirageTower3F, 0x08, 0x03, 0x03, 0x07);
            DuplicateTile(MapId.MirageTower3F, 0x07, 0x0C, 0x08, 0x0C);
            DuplicateTile(MapId.MirageTower3F, 0x07, 0x0B, 0x08, 0x0B);
        }
예제 #16
0
        public void EnableOrbHunt(MT19337 rng)
        {
            // Replace unused CANOE string and EarthOrb pointer with whatever we're calling the scavenged item.
            Put(0x2B981, FF1Text.TextToBytes("SHARD  ", false, FF1Text.Delimiter.Null));
            Data[0x2B72A] = 0x81;

            // Now actually print shards when printing items, and print them with quantity
            Data[0x7EF49] = 0x15;
            Data[0x7EF91] = 0x15;

            // Replace the upper two tiles of the unlit orb with an empty and found shard.
            // These are at tile address $76 and $77 respectively.
            Put(0x37760, Blob.FromHex("001C22414141221CFFE3DDBEBEBEDDE3001C3E7F7F7F3E1CFFFFE3CFDFDFFFFF"));

            // Hard code the total number of orbs and where we start depending on how many are needed
            int    goal       = rng.Between(16, 24);
            String hexCount   = goal.ToString("X2");
            String ppuLowByte = goal <= 24 ? "63" : "43";

            // Fancy orb drawing code, see 0E_B8D7_DrawOrbBox.asm
            Put(0x3B87D, Blob.FromHex($"A9{ppuLowByte}8511A977A00048AD0220A9208D0620A51118692085118D0620900DAD0220A9218D0620A9038D062068A200CC3560D002A976C0{hexCount}D001608D0720C8E8E006D0EB1890C3"));

            // Black Orb Override to jump to the final floor. This allows us to give out some last minute loot and
            // and make the repeated attempts the final battle strategy take a little longer due to some walking.
            Put(0x39502, Blob.FromHex($"AD3560C9{hexCount}300CA0CA209690E67DE67DA51160A51260"));

            Put(0x7CDB3, Blob.FromHex("08CE"));
            Data[0x00D80] = 0x80;             // Map edits
            Data[0x02D01] = 0x0F;
            Data[0x02D41] = 0x03;
            Data[0x02D81] = 0x3B;

            // ToFR Map Hacks
            Put(0x1A899, Blob.FromHex("0C00810502BF38000462615D5E5F0402BF3334B0020004638405600402B00235BF2D3430B10303840905B1033035BF2A34B1033330068409083032B10335BF2834B10233B00231300684021004118402083031B00232B10235BF2732313330B104300687050830B10430323133BF2634B10233B10538B00336B00338B10532B10235BF2532313330B1053831B8023AB802"));

            // A little narrative overhaul.
            Put(0x289B2, FF1Text.TextToBytes("The SHARDS coalesce to\nrestore the Black ORB.\n\nBrave Light Warriors....\nDestroy the Evil within!"));             // Black Orb Text
            Put(0x28CF8, FF1Text.TextToBytes("Ah, the Light Warriors!\n\nSo you have collected\nthe SHARDS and restored\nthe BLACK ORB."));
            Put(0x28D57, FF1Text.TextToBytes("Thus you've travelled\n2000 years into the past\nto try to stop me?\n\nStep forward then,\nto your peril!"));
            Put(0x28DAF, FF1Text.TextToBytes("Oh, Light Warriors!\nSuch arrogant bravery.\n\nLet us see whom history\nremembers. En Garde!"));

            // This ugliness picks a random FinalFormation
            transformFinalFormation((FinalFormation)rng.Between(0, Enum.GetValues(typeof(FinalFormation)).Length - 1));
        }
예제 #17
0
        public void RandomEnemyStatusAttacks(MT19337 rng, bool AllowUnsafePirates)
        {
            var enemies = Get(EnemyOffset, EnemySize * EnemyCount).Chunk(EnemySize);

            byte[] statusElements = new byte[8];
            statusElements[0] = 0x08;             //Death Touch = Death Element
            statusElements[1] = 0x02;             //Stone Touch = Poison
            statusElements[2] = 0x02;             //Poison Touch = Poison
            statusElements[3] = 0x01;             //Dark Touch = Status
            statusElements[4] = 0x01;             //Stun Touch = Status
            statusElements[5] = 0x01;             //Sleep Touch = Status
            statusElements[6] = 0x01;             //Mute Touch = Status
            statusElements[7] = 0x01;             //Conf Touch = Status

            for (int i = 0; i < EnemyCount; i++)
            {
                if (!AllowUnsafePirates)
                {
                    if (i == 15)                     //pirates
                    {
                        continue;
                    }
                }
                //We don't want EVERYTHING to have a status, so let's add a bias amount - say, 25%?
                if (rng.Between(0, 5) == 0)
                {
                    int  status  = rng.Between(0, 7);
                    byte element = statusElements[status];
                    enemies[i][14] = (byte)(0x01 << status);
                    enemies[i][15] = element;
                }
                else
                {
                    enemies[i][14] = 0x00;
                    enemies[i][15] = 0x00;
                }
            }

            Put(EnemyOffset, enemies.SelectMany(enemy => enemy.ToBytes()).ToArray());
        }
예제 #18
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();
        }
예제 #19
0
        public void RandomEnemyStatusAttacks(MT19337 rng, bool AllowUnsafePirates)
        {
            var enemies = Get(EnemyOffset, EnemySize * EnemyCount).Chunk(EnemySize);

            List <(byte touch, byte element)> statusElements = new List <(byte touch, byte element)>()
            {
                (0x01, 0x08),                 //Death Touch = Death Element
                (0x02, 0x02),                 //Stone Touch = Poison
                (0x04, 0x02),                 //Poison Touch = Poison
                (0x08, 0x01),                 //Dark Touch = Status
                (0x10, 0x01),                 //Stun Touch = Status
                (0x20, 0x01),                 //Sleep Touch = Status
                (0x40, 0x01),                 //Mute Touch = Status
            };

            for (int i = 0; i < EnemyCount; i++)
            {
                if (!AllowUnsafePirates)
                {
                    if (i == 15)                     //pirates
                    {
                        continue;
                    }
                }
                //Vanilla ratio is 37/128, hence the magic numbers
                if (rng.Between(0, 128) < 37)
                {
                    var(touch, element) = statusElements.PickRandom(rng);
                    enemies[i][15]      = touch;
                    enemies[i][14]      = element;
                }
                else
                {
                    //Otherwise, the enemy has no touch or associated element.
                    enemies[i][14] = 0x00;
                    enemies[i][15] = 0x00;
                }
            }

            Put(EnemyOffset, enemies.SelectMany(enemy => enemy.ToBytes()).ToArray());
        }
        public static void Work(IItemPlacementFlags flags, List <Item> treasurePool, MT19337 rng)
        {
            //var x = treasurePool.GroupBy(i => i).Select(g => (g.Key, g.Count())).OrderBy(g => (int)g.Key).ToList();

            if (flags.MoreConsumableChests == ConsumableChestSet.Vanilla)
            {
                return;
            }

            int count = treasurePool.Count;

            var consumableChestSet = ConsumableChestSets[flags.MoreConsumableChests];

            if (flags.MoreConsumableChests == ConsumableChestSet.Random || flags.MoreConsumableChests == ConsumableChestSet.RandomLow)
            {
                consumableChestSet =
                    (
                        Tents : rng.Between(0, consumableChestSet.Tents),
                        Cabins : rng.Between(0, consumableChestSet.Cabins),
                        Houses : rng.Between(0, consumableChestSet.Houses),
                        Heals : rng.Between(0, consumableChestSet.Heals),
                        Pures : rng.Between(0, consumableChestSet.Pures),
                        Softs : rng.Between(0, consumableChestSet.Softs)
                    );
            }

            int requestedchests = consumableChestSet.Tents + consumableChestSet.Cabins + consumableChestSet.Houses + consumableChestSet.Heals + consumableChestSet.Pures + consumableChestSet.Softs;

            int removedchests = 0;

            RemoveConsumableChests(treasurePool, ref removedchests);
            RemoveGoldChests(treasurePool, requestedchests, ref removedchests);

            AddConsumableChests(treasurePool, consumableChestSet.Tents, Item.Tent, ref removedchests);
            AddConsumableChests(treasurePool, consumableChestSet.Cabins, Item.Cabin, ref removedchests);
            AddConsumableChests(treasurePool, consumableChestSet.Houses, Item.House, ref removedchests);
            AddConsumableChests(treasurePool, consumableChestSet.Heals, Item.Heal, ref removedchests);
            AddConsumableChests(treasurePool, consumableChestSet.Pures, Item.Pure, ref removedchests);
            AddConsumableChests(treasurePool, consumableChestSet.Softs, Item.Soft, ref removedchests);

            AddGoldChests(treasurePool, removedchests);

            if (treasurePool.Count != count)
            {
                throw new Exception("Sorry, I f****d it up!");
            }
        }
예제 #21
0
        public void PubReplaceClinic(MT19337 rng)
        {
            List <byte> pub_lut = new List <byte> {
                0x0, 0x1, 0x2, 0x3, 0x4, 0x5
            };

            pub_lut.Shuffle(rng);
            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.
            int lefein_class = rng.Between(0, 5);

            pub_lut.Add((byte)lefein_class);
            Put(0x38066, Blob.FromHex("9D8A9F8E9B97"));             // Replaces "CLINIC" with "TAVERN"
            // EnterClinic
            PutInBank(0x0E, 0xA5A1, Blob.FromHex("60A90085248525205BAAA0FFC8B91DA7991003D0F7A902991003C8A648BD0A9D69F0991003C8A905991003C8A9C5991003C8A900991003A910853EA903853F2032AA20D7A6A5628D0C03B0B4209BAA20C2A8B0ADA562D0A92089A6AD0C03186D0C036D0C03AABD10036A6A6A29C04818201C9D68B08BAAA562D0458A690A8510A96185118A488512A9638513A000A90091109112C8C00A30F7C040D0F59D266120E99CA448B90A9D9D00612071C2A00E20799068AA900918BD006169069D0061A9019D0A61A9009D016120349DEAEAEAEA200CE92078A7A921205BAA2043A7A5240525F0F74CA2A5"));
            PutInBank(0x0E, 0x9D0A, pub_lut.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;
        }
예제 #22
0
        public void RandomArmorBonus(MT19337 rng, int min, int max)
        {
            //get base stats
            Armor currentArmor;

            for (int i = 0; i < ArmorCount; i++)
            {
                currentArmor = new Armor(i, this);
                int bonus = rng.Between(min, max);
                if (bonus != 0)
                {
                    //body armor(indexes 0 - 15) +2/-2 +2/-2 weight
                    //shield(indexes 16 - 24) +1/-1 +1/-1 weight
                    //helm(indexes 25 - 31) +1/-1 +1/-1 weight
                    //hand(indexes 32 - 39) +1/-1 +1/-1 weight
                    int armorTypeAbsorbBonus = i < 16 ? 2 : 1;
                    int armorTypeWeightBonus = i < 16 ? 2 : 1;

                    //adjust stats
                    //clamp minimums to 0 weight, and 1 absorb
                    currentArmor.Weight = (byte)Math.Max(0, currentArmor.Weight - (armorTypeWeightBonus * bonus));
                    currentArmor.Absorb = (byte)Math.Max(1, currentArmor.Absorb + (armorTypeAbsorbBonus * bonus));

                    //change last two non icon characters to -/+bonus
                    string bonusString = string.Format((bonus > 0) ? "+{0}" : "{0}", bonus.ToString());
                    byte[] bonusBytes  = FF1Text.TextToBytes(bonusString);

                    int iconIndex = currentArmor.NameBytes[6] > 200 && currentArmor.NameBytes[6] != 255 ? 5 : 6;
                    for (int j = 0; j < bonusBytes.Length - 1; j++)
                    {
                        currentArmor.NameBytes[iconIndex - j] = bonusBytes[bonusBytes.Length - 2 - j];
                    }

                    currentArmor.writeArmorMemory(this);
                }
            }
        }
예제 #23
0
        public void BuildExpChests()
        {
            rom.PutInBank(0x1F, 0xDDD0, Blob.FromHex("A9B648A9FF48A9114C03FE8A20DA8760"));

            int expChestCountPercent = rng.Between(flags.ExpChestConversionMin, flags.ExpChestConversionMax);

            int    expChestCount = treasureData.Data.Where(g => g >= Item.Gold10).Count() * expChestCountPercent / 100;
            double lowScale      = (double)flags.ExpChestMinReward / (double)BaseExp;
            double highScale     = (double)flags.ExpChestMaxReward / (double)BaseExp;

            if (expChestCount == 0)
            {
                return;
            }

            LoadData();

            var unusedGoldDic = new HashSet <int>(rom.UnusedGoldItems.Cast <int>());

            // construct a dictionary and get a shuffled index into it.
            var goldItems = ItemLists.AllGoldTreasure.Select(g => (item: g, price: itemPrices[g], name: itemNames[(int)g])).ToList();

            goldItems.Shuffle(rng);
            var goldItemsDic = goldItems.Select((g, i) => (shuffleindex: i, item: g.item, price: g.price, name: g.name)).ToDictionary(g => g.item);

            var expItems = new HashSet <Item>(treasureData.Data
                                              .Where(g => goldItemsDic.ContainsKey(g) && !unusedGoldDic.Contains((int)g))
                                              .Select(g => (item: g, shuffleindex: goldItemsDic[g].shuffleindex))
                                              .OrderBy(g => g.shuffleindex)
                                              .Take(expChestCount)
                                              .Select(g => g.item)
                                              .Distinct());

            var firstExpItem = RepackGoldExpItems(goldItemsDic, expItems, unusedGoldDic);

            for (int i = (int)firstExpItem; i < 176; i++)
            {
                if (unusedGoldDic.Contains(i))
                {
                    continue;
                }

                var e = (Item)i;

                var exp = (ushort)Math.Min(Math.Max(FF1Rom.RangeScale(BaseExp, lowScale, highScale, 1.0, rng), 0), 65535);
                itemPrices[e]     = exp;
                itemNames[(int)e] = exp.ToString() + " EXP";
            }

            FirstExpItem = firstExpItem;
            if (flags.Archipelago)
            {
                rom.PutInBank(0x11, 0xB446, new byte[] { (byte)firstExpItem });
            }
            else
            {
                rom.PutInBank(0x11, 0xB447, new byte[] { (byte)firstExpItem });
            }


            var result = treasureData.Data.Where(x => x > Item.Gold10).OrderBy(x => x).Select(x => itemNames[(int)x]).ToList();

            StoreData();
        }
예제 #24
0
        public void ShuffleMusic(MusicShuffle mode, MT19337 rng)
        {
            switch (mode)
            {
            case MusicShuffle.Standard:
                List <byte> overworldTracks = new List <byte> {
                    0x41, 0x42, 0x44, 0x45, 0x46, 0x47, 0x4A, 0x4F
                };
                List <byte> townTracks = new List <byte> {
                    0x41, 0x42, 0x45, 0x46, 0x47, 0x48, 0x4A, 0x4F, 0x51
                };
                List <byte> dungeonTracks = new List <byte> {
                    0x41, 0x42, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x52, 0x53
                };

                overworldTracks.Shuffle(rng);
                townTracks.Shuffle(rng);
                dungeonTracks.Shuffle(rng);

                //Overworld
                Data[0x7C649] = overworldTracks[0];
                Data[0x7C6F9] = overworldTracks[0];
                Data[0x7C75A] = overworldTracks[0];
                Data[0x7C75B] = overworldTracks[0];

                //Ship
                Data[0x7C62D] = overworldTracks[1];
                Data[0x7C75D] = overworldTracks[1];

                //Airship
                Data[0x7C235] = overworldTracks[2];
                Data[0x7C761] = overworldTracks[2];

                //Remove used songs from other pools
                var usedTracks = overworldTracks.Take(3).ToList();
                townTracks = townTracks.Except(usedTracks).ToList();

                //Town
                Data[0x7CFC3] = townTracks[0];

                //Castle
                Data[0x7CFC4] = townTracks[1];

                //Shop
                Data[0x3A351] = townTracks[2];
                Data[0x3A56E] = townTracks[2];
                Data[0x3A597] = townTracks[2];

                //Menu
                Data[0x3ADB4] = townTracks[3];
                Data[0x3B677] = townTracks[3];
                Data[0x3997F] = townTracks[3];                         //Lineup menu

                //Remove used songs from other pools
                usedTracks.AddRange(townTracks.Take(4));
                dungeonTracks = dungeonTracks.Except(usedTracks).ToList();

                //Dungeons
                Data[0x7CFC5] = dungeonTracks[0];
                Data[0x7CFC6] = dungeonTracks[1];
                Data[0x7CFC7] = dungeonTracks[2];
                Data[0x7CFC8] = dungeonTracks[3];
                Data[0x7CFC9] = dungeonTracks[4];
                Data[0x7CFCA] = dungeonTracks[5];

                break;

            case MusicShuffle.Nonsensical:                     //They asked for it...
                List <byte> tracks = new List <byte> {
                    0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x51, 0x52, 0x53, 0x55
                };
                tracks.Shuffle(rng);

                //Overworld
                Data[0x7C649] = tracks[0];
                Data[0x7C6F9] = tracks[0];
                Data[0x7C75A] = tracks[0];
                Data[0x7C75B] = tracks[0];

                //Ship
                Data[0x7C62D] = tracks[1];
                Data[0x7C75D] = tracks[1];

                //Airship
                Data[0x7C235] = tracks[2];
                Data[0x7C761] = tracks[2];

                //Tilesets 1-8
                Data[0x7CFC3] = tracks[3];                         //Town
                Data[0x7CFC4] = tracks[4];                         //Castle
                Data[0x7CFC5] = tracks[5];
                Data[0x7CFC6] = tracks[6];
                Data[0x7CFC7] = tracks[7];
                Data[0x7CFC8] = tracks[8];
                Data[0x7CFC9] = tracks[9];
                Data[0x7CFCA] = tracks[10];

                //Title
                Data[0x3A226] = tracks[11];

                //Shop
                Data[0x3A351] = tracks[12];
                Data[0x3A56E] = tracks[12];
                Data[0x3A597] = tracks[12];

                //Menu
                Data[0x3ADB4] = tracks[13];
                Data[0x3B677] = tracks[13];
                Data[0x3997F] = tracks[13];                         //Lineup menu

                //Ending
                Data[0x37804] = tracks[14];

                //Bridge Cutscene
                Data[0x3784E] = tracks[15];

                //Battle Fanfare
                Data[0x31E44] = tracks[16];

                //Gameover
                Data[0x2DAF6] = tracks[17];

                //Battle
                //Data[0x2D9C1] = Songs[rng.Between(0, Songs.Count - 1)];

                //Mini Things
                Data[0x36E86] = tracks[rng.Between(0, tracks.Count - 1)];                         //minigame
                Data[0x27C0D] = tracks[rng.Between(0, tracks.Count - 1)];                         //minimap

                break;

            case MusicShuffle.MusicDisabled:
                //Set Sq1, Sq2, and Tri channels for crystal theme all point to the same music data
                Put(0x34000, Blob.FromHex("C080C080C080"));
                //Overwrite beginning of crystal theme with a song that initializes properly but plays no notes
                Put(0x340C0, Blob.FromHex("FDF805E0D8C7D0C480"));

                List <int> AllSongs = new List <int>
                {
                    0x7C649, 0x7C6F9, 0x7C75A, 0x7C75B, 0x7C62D, 0x7C75D,
                    0x7C235, 0x7C761, 0x7CFC3, 0x7CFC4, 0x7CFC5, 0x7CFC6,
                    0x7CFC7, 0x7CFC8, 0x7CFC9, 0x7CFCA, 0x3A226, 0x3A351,
                    0x3A56E, 0x3A597, 0x3ADB4, 0x3B677, 0x3997F, 0x37804,
                    0x3784E, 0x31E44, 0x2DAF6, 0x2D9C1, 0x36E86, 0x27C0D
                };
                //Set all music playback calls to play the new empty song
                foreach (int address in AllSongs)
                {
                    Data[address] = 0x41;
                }

                break;
            }
        }
예제 #25
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;
				}
			}
		}
예제 #26
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;
		}
예제 #27
0
        private void GenerateTree(MT19337 rng)
        {
            root      = new BSPMapNode(0, 0, MapRequirements.Width, MapRequirements.Height);
            all_nodes = new List <BSPMapNode> {
                root
            };
            leaf_nodes = new HashSet <BSPMapNode> {
                root
            };

            while (true)
            {
                var new_leafs = new List <BSPMapNode>();

                foreach (var split_leaf in leaf_nodes.ToList().
                         Where(l => l.Width > MAX_LEAF_SIZE || l.Height > MAX_LEAF_SIZE || rng.Between(0, 3) < 3). // always split if too big, or on a 3/4 chance.
                         Where(l => l.Split(rng)))                                                                 // only select leafs that actually successfully split
                {
                    // if we did split, add the new leafs to the list
                    new_leafs.Add(split_leaf.LeftChild);
                    new_leafs.Add(split_leaf.RightChild);

                    leaf_nodes.Remove(split_leaf);
                }

                if (new_leafs.Count == 0)
                {
                    break;
                }

                all_nodes.AddRange(new_leafs);
                leaf_nodes.UnionWith(new_leafs);
            }
        }
예제 #28
0
        private void MakeHallway(MT19337 rng, Rectangle l, Rectangle r)
        {
            // now we connect these two rooms together with hallways.
            // this looks pretty complicated, but it's just trying to figure out which point is where and then either draw a straight line, or a pair of lines to make a right-angle to connect them.
            // you could do some extra logic to make your halls more bendy, or do some more advanced things if you wanted.

            Hallways = new List <Rectangle>();

            int hallway_width = rng.Between(0, 4) == 4
                                ? rng.Between(1, BSPTreeEngine.MAX_HALLWAY_WIDTH)
                                : BSPTreeEngine.MAX_HALLWAY_WIDTH;

            var point1       = new Point(rng.Between(l.Left, l.Right - hallway_width), rng.Between(l.Top, l.Bottom - hallway_width));
            var point2       = new Point(rng.Between(r.Left, r.Right - hallway_width), rng.Between(r.Top, r.Bottom - hallway_width));
            int x_difference = point2.X - point1.X;
            int y_difference = point2.Y - point1.Y;


            if (x_difference < 0)
            {
                if (y_difference < 0)
                {
                    if (rng.Between(0, 1) == 0)
                    {
                        Hallways.Add(new Rectangle(point2.X, point1.Y, Math.Abs(x_difference), hallway_width));
                        Hallways.Add(new Rectangle(point2.X, point2.Y, hallway_width, Math.Abs(y_difference)));
                    }
                    else
                    {
                        Hallways.Add(new Rectangle(point2.X, point2.Y, Math.Abs(x_difference), hallway_width));
                        Hallways.Add(new Rectangle(point1.X, point2.Y, hallway_width, Math.Abs(y_difference)));
                    }
                }
                else if (y_difference > 0)
                {
                    if (rng.Between(0, 1) == 0)
                    {
                        Hallways.Add(new Rectangle(point2.X, point1.Y, Math.Abs(x_difference), hallway_width));
                        Hallways.Add(new Rectangle(point2.X, point1.Y, hallway_width, Math.Abs(y_difference)));
                    }
                    else
                    {
                        Hallways.Add(new Rectangle(point2.X, point2.Y, Math.Abs(x_difference), hallway_width));
                        Hallways.Add(new Rectangle(point1.X, point1.Y, hallway_width, Math.Abs(y_difference)));
                    }
                }
                else         // if (h == 0)
                {
                    Hallways.Add(new Rectangle(point2.X, point2.Y, Math.Abs(x_difference), hallway_width));
                }
            }
            else if (x_difference > 0)
            {
                if (y_difference < 0)
                {
                    if (rng.Between(0, 1) == 0)
                    {
                        Hallways.Add(new Rectangle(point1.X, point2.Y, Math.Abs(x_difference), hallway_width));
                        Hallways.Add(new Rectangle(point1.X, point2.Y, hallway_width, Math.Abs(y_difference)));
                    }
                    else
                    {
                        Hallways.Add(new Rectangle(point1.X, point1.Y, Math.Abs(x_difference), hallway_width));
                        Hallways.Add(new Rectangle(point2.X, point2.Y, hallway_width, Math.Abs(y_difference)));
                    }
                }
                else if (y_difference > 0)
                {
                    if (rng.Between(0, 1) == 0)
                    {
                        Hallways.Add(new Rectangle(point1.X, point1.Y, Math.Abs(x_difference), hallway_width));
                        Hallways.Add(new Rectangle(point2.X, point1.Y, hallway_width, Math.Abs(y_difference)));
                    }
                    else
                    {
                        Hallways.Add(new Rectangle(point1.X, point2.Y, Math.Abs(x_difference), hallway_width));
                        Hallways.Add(new Rectangle(point1.X, point1.Y, hallway_width, Math.Abs(y_difference)));
                    }
                }
                else         // if (y_difference == 0)
                {
                    Hallways.Add(new Rectangle(point1.X, point1.Y, Math.Abs(x_difference), hallway_width));
                }
            }
            else         // if (x_difference == 0)
            {
                if (y_difference < 0)
                {
                    Hallways.Add(new Rectangle(point2.X, point2.Y, hallway_width, Math.Abs(y_difference)));
                }
                else if (y_difference > 0)
                {
                    Hallways.Add(new Rectangle(point1.X, point1.Y, hallway_width, Math.Abs(y_difference)));
                }
            }
        }
예제 #29
0
        private Point PointInAnyRandomRoom(MT19337 rng)
        {
            var some_room = (Rectangle)root.GetRandomRoom(rng);

            return(new Point(some_room.X + rng.Between(1, some_room.Width - 1), some_room.Y + rng.Between(1, some_room.Height - 1)));
        }
예제 #30
0
        public void Randomize(Blob seed, Flags flags, Preferences preferences)
        {
            MT19337 rng;

            using (SHA256 hasher = SHA256.Create())
            {
                Blob FlagsBlob    = Encoding.UTF8.GetBytes(Flags.EncodeFlagsText(flags));
                Blob SeedAndFlags = Blob.Concat(new Blob[] { FlagsBlob, seed });
                Blob hash         = hasher.ComputeHash(SeedAndFlags);
                rng = new MT19337(BitConverter.ToUInt32(hash, 0));
            }
            if (flags.TournamentSafe)
            {
                AssureSafe();
            }

            UpgradeToMMC3();
            MakeSpace();
            Bank1E();
            Bank1B();
            EasterEggs();
            DynamicWindowColor(preferences.MenuColor);
            PermanentCaravan();
            ShiftEarthOrbDown();
            CastableItemTargeting();
            FixEnemyPalettes();       // fixes a bug in the original game's programming that causes third enemy slot's palette to render incorrectly
            FixWarpBug();             // The warp bug must be fixed for magic level shuffle and spellcrafter
            SeparateUnrunnables();
            var talkroutines = new TalkRoutines();
            var npcdata      = new NPCdata(this);

            UpdateDialogs(npcdata);

            if (flags.TournamentSafe)
            {
                Put(0x3FFE3, Blob.FromHex("66696E616C2066616E74617379"));
            }

            flags = Flags.ConvertAllTriState(flags, rng);

            TeleportShuffle teleporters      = new TeleportShuffle();
            var             palettes         = OverworldMap.GeneratePalettes(Get(OverworldMap.MapPaletteOffset, MapCount * OverworldMap.MapPaletteSize).Chunk(OverworldMap.MapPaletteSize));
            var             overworldMap     = new OverworldMap(this, flags, palettes, teleporters);
            var             maps             = ReadMaps();
            var             shopItemLocation = ItemLocations.CaravanItemShop1;
            var             oldItemNames     = ReadText(ItemTextPointerOffset, ItemTextPointerBase, ItemTextPointerCount);

            if (flags.EFGWaterfall || flags.EFGEarth1 || flags.EFGEarth2)
            {
                MapRequirements      reqs;
                MapGeneratorStrategy strategy;
                MapGenerator         generator = new MapGenerator();
                if (flags.EFGWaterfall)
                {
                    reqs = new MapRequirements
                    {
                        MapId = MapId.Waterfall,
                        Rom   = this,
                    };
                    strategy = MapGeneratorStrategy.WaterfallClone;
                    CompleteMap waterfall = generator.Generate(rng, strategy, reqs);

                    // Should add more into the reqs so that this can be done inside the generator.
                    teleporters.Waterfall.SetEntrance(waterfall.Entrance);
                    overworldMap.PutOverworldTeleport(OverworldTeleportIndex.Waterfall, teleporters.Waterfall);
                    maps[(int)MapId.Waterfall] = waterfall.Map;
                }

                if (flags.EFGEarth1)
                {
                    reqs = new MapRequirements
                    {
                        MapId = MapId.EarthCaveB1,
                        Rom   = this,
                    };

                    strategy = MapGeneratorStrategy.Square;
                    var earthB1 = generator.Generate(rng, strategy, reqs);

                    // Should add more into the reqs so that this can be done inside the generator.
                    teleporters.EarthCave1.SetEntrance(earthB1.Entrance);
                    overworldMap.PutOverworldTeleport(OverworldTeleportIndex.EarthCave1, teleporters.EarthCave1);
                    maps[(int)MapId.EarthCaveB1] = earthB1.Map;
                }
                if (flags.EFGEarth2)
                {
                    reqs = new MapRequirements
                    {
                        MapId = MapId.EarthCaveB2,
                        Rom   = this,
                    };

                    strategy = MapGeneratorStrategy.Square;
                    var earthB2 = generator.Generate(rng, strategy, reqs);

                    // Should add more into the reqs so that this can be done inside the generator.
                    teleporters.EarthCave2.SetEntrance(earthB2.Entrance);
                    overworldMap.PutStandardTeleport(TeleportIndex.EarthCave2, teleporters.EarthCave2, OverworldTeleportIndex.EarthCave1);
                    maps[(int)MapId.EarthCaveB2] = earthB2.Map;
                }
            }

            var flippedMaps = new List <MapId>();

            if ((bool)flags.FlipDungeons)
            {
                flippedMaps = HorizontalFlipDungeons(rng, maps, teleporters, overworldMap);
            }

            if ((bool)flags.RandomizeFormationEnemizer)
            {
                DoEnemizer(rng, (bool)flags.RandomizeEnemizer, (bool)flags.RandomizeFormationEnemizer, flags.EnemizerDontMakeNewScripts);
            }

            if (preferences.ModernBattlefield)
            {
                EnableModernBattlefield();
            }

            if ((bool)flags.TitansTrove)
            {
                EnableTitansTrove(maps);
            }

            if ((bool)flags.LefeinShops)
            {
                EnableLefeinShops(maps);
            }

            // This has to be done before we shuffle spell levels.
            if (flags.SpellBugs)
            {
                FixSpellBugs();
            }

            //must be done before spells get shuffled around otherwise we'd be changing a spell that isnt lock
            if (flags.LockMode != LockHitMode.Vanilla)
            {
                ChangeLockMode(flags.LockMode);
            }

            if (flags.EnemySpellsTargetingAllies)
            {
                FixEnemyAOESpells();
            }

            if (flags.AllSpellLevelsForKnightNinja)
            {
                KnightNinjaChargesForAllLevels();
            }

            if (flags.BuffHealingSpells)
            {
                BuffHealingSpells();
            }

            UpdateMagicAutohitThreshold(rng, flags.MagicAutohitThreshold);

            if ((bool)flags.GenerateNewSpellbook)
            {
                CraftNewSpellbook(rng, (bool)flags.SpellcrafterMixSpells, flags.LockMode, (bool)flags.MagicLevels, (bool)flags.SpellcrafterRetainPermissions);
            }

            if ((bool)flags.MagisizeWeapons)
            {
                MagisizeWeapons(rng, (bool)flags.MagisizeWeaponsBalanced);
            }

            if ((bool)flags.ItemMagic)
            {
                ShuffleItemMagic(rng, (bool)flags.BalancedItemMagicShuffle);
            }

            if ((bool)flags.GuaranteedRuseItem)
            {
                CraftRuseItem();
            }

            if ((bool)flags.ShortToFR)
            {
                ShortenToFR(maps, (bool)flags.PreserveFiendRefights, (bool)flags.PreserveAllFiendRefights, rng);
            }

            if (((bool)flags.Treasures) && flags.ShardHunt && !flags.FreeOrbs)
            {
                EnableShardHunt(rng, talkroutines, flags.ShardCount);
            }

            if ((bool)flags.TransformFinalFormation && !flags.SpookyFlag)
            {
                TransformFinalFormation((FinalFormation)rng.Between(0, Enum.GetValues(typeof(FinalFormation)).Length - 1), flags.EvadeCap);
            }

            var maxRetries = 8;

            for (var i = 0; i < maxRetries; i++)
            {
                try
                {
                    overworldMap = new OverworldMap(this, flags, palettes, teleporters);
                    if (((bool)flags.Entrances || (bool)flags.Floors || (bool)flags.Towns) && ((bool)flags.Treasures) && ((bool)flags.NPCItems))
                    {
                        overworldMap.ShuffleEntrancesAndFloors(rng, flags);

                        // Disable the Princess Warp back to Castle Coneria
                        if ((bool)flags.Entrances || (bool)flags.Floors)
                        {
                            talkroutines.ReplaceChunk(newTalkRoutines.Talk_Princess1, Blob.FromHex("20CC90"), Blob.FromHex("EAEAEA"));
                        }
                    }

                    if ((bool)flags.Treasures && (bool)flags.ShuffleObjectiveNPCs)
                    {
                        overworldMap.ShuffleObjectiveNPCs(rng);
                    }

                    IncentiveData incentivesData = new IncentiveData(rng, flags, overworldMap, shopItemLocation);

                    if (((bool)flags.Shops))
                    {
                        var excludeItemsFromRandomShops = new List <Item>();
                        if ((bool)flags.Treasures)
                        {
                            excludeItemsFromRandomShops = incentivesData.ForcedItemPlacements.Select(x => x.Item).Concat(incentivesData.IncentiveItems).ToList();
                        }

                        if (!((bool)flags.RandomWaresIncludesSpecialGear))
                        {
                            excludeItemsFromRandomShops.AddRange(ItemLists.SpecialGear);
                            if ((bool)flags.GuaranteedRuseItem)
                            {
                                excludeItemsFromRandomShops.Add(Item.PowerRod);
                            }
                        }

                        if ((bool)flags.NoMasamune)
                        {
                            excludeItemsFromRandomShops.Add(Item.Masamune);
                        }

                        shopItemLocation = ShuffleShops(rng, (bool)flags.ImmediatePureAndSoftRequired, ((bool)flags.RandomWares), excludeItemsFromRandomShops, flags.WorldWealth);
                        incentivesData   = new IncentiveData(rng, flags, overworldMap, shopItemLocation);
                    }

                    if ((bool)flags.Treasures)
                    {
                        generatedPlacement = ShuffleTreasures(rng, flags, incentivesData, shopItemLocation, overworldMap, teleporters);
                    }
                    break;
                }
                catch (InsaneException e)
                {
                    Console.WriteLine(e.Message);
                    if (maxRetries > (i + 1))
                    {
                        continue;
                    }
                    throw new InvalidOperationException(e.Message);
                }
            }

            // Change Astos routine so item isn't lost in wall of text
            if ((bool)flags.NPCItems || (bool)flags.NPCFetchItems || (bool)flags.ShuffleAstos)
            {
                talkroutines.Replace(newTalkRoutines.Talk_Astos, Blob.FromHex("A674F005BD2060F027A5738561202096B020A572203D96A575200096A476207F90207392A5611820109F201896A9F060A57060"));
            }

            npcdata.UpdateItemPlacement(generatedPlacement);

            if ((bool)flags.MagicShopLocs)
            {
                ShuffleMagicLocations(rng);
            }

            if (((bool)flags.MagicShops))
            {
                ShuffleMagicShops(rng);
            }

            if (((bool)flags.MagicLevels))
            {
                ShuffleMagicLevels(rng, ((bool)flags.MagicPermissions), (bool)flags.MagicLevelsTiered, (bool)flags.MagicLevelsMixed, (bool)!flags.GenerateNewSpellbook);
            }

            new StartingInventory(rng, flags, this).SetStartingInventory();

            /*
             * if (flags.WeaponPermissions)
             * {
             *      ShuffleWeaponPermissions(rng);
             * }
             *
             * if (flags.ArmorPermissions)
             * {
             *      ShuffleArmorPermissions(rng);
             * }
             */

            if (flags.SaveGameWhenGameOver)
            {
                EnableSaveOnDeath(flags);
            }

            // Ordered before RNG shuffle. In the event that both flags are on, RNG shuffle depends on this.
            if (((bool)flags.FixMissingBattleRngEntry))
            {
                FixMissingBattleRngEntry();
            }

            if (((bool)flags.Rng))
            {
                ShuffleRng(rng);
            }

            if (((bool)flags.EnemyScripts))
            {
                ShuffleEnemyScripts(rng, (bool)flags.AllowUnsafePirates, (bool)!flags.BossScriptsOnly, ((bool)flags.EnemySkillsSpellsTiered || (bool)flags.ScaryImps), (bool)flags.ScaryImps);
            }

            if (((bool)flags.EnemySkillsSpells))
            {
                if ((bool)flags.EnemySkillsSpellsTiered && (bool)!flags.BossSkillsOnly)
                {
                    GenerateBalancedEnemyScripts(rng, (bool)flags.SwolePirates);
                    ShuffleEnemySkillsSpells(rng, false);
                }
                else
                {
                    ShuffleEnemySkillsSpells(rng, (bool)!flags.BossSkillsOnly);
                }
            }

            if (((bool)flags.EnemyStatusAttacks))
            {
                if (((bool)flags.RandomStatusAttacks))
                {
                    RandomEnemyStatusAttacks(rng, (bool)flags.AllowUnsafePirates, (bool)flags.DisableStunTouch);
                }
                else
                {
                    ShuffleEnemyStatusAttacks(rng, (bool)flags.AllowUnsafePirates);
                }
            }

            if (flags.Runnability == Runnability.Random)
            {
                flags.Runnability = (Runnability)Rng.Between(rng, 0, 3);
            }

            if (flags.Runnability == Runnability.AllRunnable)
            {
                CompletelyRunnable();
            }
            else if (flags.Runnability == Runnability.AllUnrunnable)
            {
                CompletelyUnrunnable();
            }
            else if (flags.Runnability == Runnability.Shuffle)
            {
                ShuffleUnrunnable(rng);
            }

            // Always on to supply the correct changes for WaitWhenUnrunnable
            AllowStrikeFirstAndSurprise(flags.WaitWhenUnrunnable, (bool)flags.UnrunnablesStrikeFirstAndSurprise);

            if (((bool)flags.EnemyFormationsSurprise))
            {
                ShuffleSurpriseBonus(rng);
            }

            // Put this before other encounter / trap tile edits.
            if ((bool)flags.AllowUnsafeMelmond)
            {
                EnableMelmondGhetto(flags.EnemizerEnabled);
            }

            // After unrunnable shuffle and before formation shuffle. Perfect!
            if (flags.WarMECHMode != WarMECHMode.Vanilla)
            {
                WarMECHNpc(flags.WarMECHMode, npcdata, rng, maps);
            }

            if (flags.WarMECHMode == WarMECHMode.Unleashed)
            {
                UnleashWarMECH();
            }

            if ((bool)flags.ClassAsNpcFiends || (bool)flags.ClassAsNpcKeyNPC)
            {
                ClassAsNPC(flags, talkroutines, npcdata, flippedMaps, rng);
            }

            if ((bool)flags.FiendShuffle)
            {
                FiendShuffle(rng);
            }

            if (flags.FormationShuffleMode != FormationShuffleMode.None && !flags.EnemizerEnabled)
            {
                ShuffleEnemyFormations(rng, flags.FormationShuffleMode);
            }

            if ((bool)flags.RemoveTrapTiles)
            {
                RemoveTrapTiles(flags.EnemizerEnabled);
            }

            if (((bool)flags.EnemyTrapTiles) && !flags.EnemizerEnabled)
            {
                ShuffleTrapTiles(rng, ((bool)flags.RandomTrapFormations));
            }

            if ((bool)flags.OrdealsPillars)
            {
                ShuffleOrdeals(rng, maps);
            }

            if (flags.SkyCastle4FMazeMode == SkyCastle4FMazeMode.Maze)
            {
                DoSkyCastle4FMaze(rng, maps);
            }
            else if (flags.SkyCastle4FMazeMode == SkyCastle4FMazeMode.Teleporters)
            {
                ShuffleSkyCastle4F(rng, maps);
            }

            if ((bool)flags.ConfusedOldMen)
            {
                EnableConfusedOldMen(rng);
            }

            if ((bool)flags.EarlyOrdeals)
            {
                EnableEarlyOrdeals();
            }

            if (flags.ChaosRush)
            {
                EnableChaosRush();
            }
            if ((bool)flags.EarlyKing)
            {
                EnableEarlyKing(npcdata);
            }

            if ((bool)flags.EarlySarda)
            {
                EnableEarlySarda(npcdata);
            }

            if ((bool)flags.EarlySage)
            {
                EnableEarlySage(npcdata);
            }

            if ((bool)flags.FreeBridge)
            {
                EnableFreeBridge();
            }

            if ((bool)flags.FreeAirship)
            {
                EnableFreeAirship();
            }

            if ((bool)flags.FreeShip)
            {
                EnableFreeShip();
            }

            if (flags.FreeOrbs)
            {
                EnableFreeOrbs();
            }

            if ((bool)flags.FreeCanal)
            {
                EnableFreeCanal((bool)flags.NPCItems, npcdata);
            }

            if ((bool)flags.FreeCanoe)
            {
                EnableFreeCanoe();
            }

            if ((bool)flags.FreeLute)
            {
                EnableFreeLute();
            }

            if ((bool)flags.FreeTail && !(bool)flags.NoTail)
            {
                EnableFreeTail();
            }

            if (flags.NoPartyShuffle)
            {
                DisablePartyShuffle();
            }

            if (flags.SpeedHacks)
            {
                EnableSpeedHacks();
            }

            if (flags.IdentifyTreasures)
            {
                EnableIdentifyTreasures();
            }

            if (flags.Dash)
            {
                EnableDash();
            }

            if (flags.BuyTen)
            {
                EnableBuyQuantity();
            }
            else if (flags.BuyTenOld)
            {
                EnableBuyTen();
            }

            if (flags.WaitWhenUnrunnable)
            {
                ChangeUnrunnableRunToWait();
            }

            if (flags.SpeedHacks && flags.EnableCritNumberDisplay)
            {
                EnableCritNumberDisplay();
            }

            if (flags.BattleMagicMenuWrapAround)
            {
                BattleMagicMenuWrapAround();
            }

            if (flags.NPCSwatter)
            {
                EnableNPCSwatter(npcdata);
            }

            if (flags.EasyMode)
            {
                EnableEasyMode();
            }

            if ((bool)flags.TrappedChests || (bool)flags.TCMasaGuardian || (bool)flags.TrappedShards)
            {
                MonsterInABox(rng, flags);
            }

            if (flags.HouseMPRestoration || flags.HousesFillHp)
            {
                FixHouse(flags.HouseMPRestoration, flags.HousesFillHp);
            }

            if (flags.BBCritRate)
            {
                DontDoubleBBCritRates();
            }

            if (flags.WeaponCritRate)
            {
                DoubleWeaponCritRates();
            }

            //needs to go after item magic, moved after double weapon crit to have more control over the actual number of crit gained.
            if ((bool)flags.RandomWeaponBonus)
            {
                RandomWeaponBonus(rng, flags.RandomWeaponBonusLow, flags.RandomWeaponBonusHigh, (bool)flags.RandomWeaponBonusExcludeMasa);
            }

            if ((bool)flags.RandomArmorBonus)
            {
                RandomArmorBonus(rng, flags.RandomArmorBonusLow, flags.RandomArmorBonusHigh);
            }

            if (flags.WeaponBonuses)
            {
                IncreaseWeaponBonus(flags.WeaponTypeBonusValue);
            }

            if (flags.WeaponStats)
            {
                FixWeaponStats();
            }

            if (flags.ChanceToRun)
            {
                FixChanceToRun();
            }

            if (flags.EnemyStatusAttackBug)
            {
                FixEnemyStatusAttackBug();
            }

            if (flags.BlackBeltAbsorb)
            {
                FixBBAbsorbBug();
            }

            if (flags.MDefMode != MDEFGrowthMode.None)
            {
                MDefChanges(flags.MDefMode);
            }

            if (flags.ThiefHitRate)
            {
                ThiefHitRate();
            }

            if (flags.ImproveTurnOrderRandomization)
            {
                ImproveTurnOrderRandomization(rng);
            }

            if (flags.FixHitChanceCap)
            {
                FixHitChanceCap();
            }

            if (flags.EnemyElementalResistancesBug)
            {
                FixEnemyElementalResistances();
            }

            if (preferences.FunEnemyNames && !flags.EnemizerEnabled)
            {
                FunEnemyNames(preferences.TeamSteak);
            }

            var itemText = ReadText(ItemTextPointerOffset, ItemTextPointerBase, ItemTextPointerCount);

            itemText[(int)Item.Ribbon] = itemText[(int)Item.Ribbon].Remove(7);

            if ((bool)flags.HintsVillage || (bool)flags.HintsDungeon)
            {
                if ((bool)flags.HintsDungeon)
                {
                    SetDungeonNPC(flippedMaps, rng);
                }

                NPCHints(rng, npcdata, flags, overworldMap);
            }

            if (flags.Etherizer && !flags.HouseMPRestoration && !flags.HousesFillHp)
            {
                Etherizer();
                itemText[(int)Item.Tent]  = "ETHR@p";
                itemText[(int)Item.Cabin] = "DRY@p ";
                itemText[(int)Item.House] = "XETH@p";
            }

            ExpGoldBoost(flags.ExpBonus, flags.ExpMultiplier);
            ScalePrices(flags, itemText, rng, ((bool)flags.ClampMinimumPriceScale), shopItemLocation);
            ScaleEncounterRate(flags.EncounterRate / 30.0, flags.DungeonEncounterRate / 30.0);

            overworldMap.ApplyMapEdits();
            WriteMaps(maps);

            WriteText(itemText, ItemTextPointerOffset, ItemTextPointerBase, ItemTextOffset, UnusedGoldItems);

            if ((bool)flags.SwolePirates)
            {
                EnableSwolePirates();
            }

            if (flags.EnemyScaleStatsHigh != 100 || flags.EnemyScaleStatsLow != 100 || ((bool)flags.SeparateEnemyHPScaling && (flags.EnemyScaleHpLow != 100 || flags.EnemyScaleHpHigh != 100)))
            {
                ScaleEnemyStats(rng, flags);
            }

            if (flags.BossScaleStatsHigh != 100 || flags.BossScaleStatsLow != 100 || ((bool)flags.SeparateBossHPScaling && (flags.BossScaleHpLow != 100 || flags.BossScaleHpHigh != 100)))
            {
                ScaleBossStats(rng, flags);
            }

            PartyComposition(rng, flags, preferences);

            if (((bool)flags.RecruitmentMode))
            {
                PubReplaceClinic(rng, flags);
            }

            if ((bool)flags.ChangeMaxMP)
            {
                SetMPMax(flags.RedMageMaxMP, flags.WhiteMageMaxMP, flags.BlackMageMaxMP, flags.KnightMaxMP, flags.NinjaMaxMP);
            }

            if ((bool)flags.ShuffleAstos)
            {
                ShuffleAstos(flags, npcdata, talkroutines, rng);
            }

            if ((bool)flags.EnablePoolParty)
            {
                EnablePoolParty(flags, rng);
            }

            if ((bool)flags.MapCanalBridge)
            {
                EnableCanalBridge();
            }

            if (flags.NoDanMode)
            {
                NoDanMode();
            }

            SetProgressiveScaleMode(flags);

            if ((bool)flags.RandomizeClass)
            {
                RandomizeClass(rng, flags, oldItemNames);
            }

            if ((bool)flags.EnableRandomPromotions)
            {
                EnableRandomPromotions(flags, rng);
            }

            if (flags.DisableTentSaving)
            {
                CannotSaveOnOverworld();
            }

            if (flags.DisableInnSaving)
            {
                CannotSaveAtInns();
            }

            if (flags.PacifistMode && !flags.SpookyFlag)
            {
                PacifistEnd(talkroutines, npcdata, (bool)flags.EnemyTrapTiles || flags.EnemizerEnabled);
            }

            if (flags.ShopInfo)
            {
                ShopUpgrade();
            }

            if (flags.SpookyFlag)
            {
                Spooky(talkroutines, npcdata, rng, flags);
            }

            if (flags.InventoryAutosort && !(preferences.RenounceAutosort))
            {
                EnableInventoryAutosort();
            }

            // We have to do "fun" stuff last because it alters the RNG state.
            // Back up Rng so that fun flags are uniform when different ones are selected
            uint funRngSeed = rng.Next();

            RollCredits(rng);

            if (preferences.DisableDamageTileFlicker)
            {
                DisableDamageTileFlicker();
            }

            if (preferences.ThirdBattlePalette)
            {
                UseVariablePaletteForCursorAndStone();
            }

            if (preferences.PaletteSwap && !flags.EnemizerEnabled)
            {
                rng = new MT19337(funRngSeed);
                PaletteSwap(rng);
            }

            if (preferences.TeamSteak && !(bool)flags.RandomizeEnemizer)
            {
                TeamSteak();
            }

            if (preferences.ChangeLute)
            {
                rng = new MT19337(funRngSeed);
                ChangeLute(rng);
            }

            rng = new MT19337(funRngSeed);

            HurrayDwarfFate(preferences.HurrayDwarfFate, npcdata, rng);

            if (preferences.Music != MusicShuffle.None)
            {
                rng = new MT19337(funRngSeed);
                ShuffleMusic(preferences.Music, rng);
            }

            if (preferences.DisableSpellCastFlash)
            {
                DisableSpellCastScreenFlash();
            }

            npcdata.WriteNPCdata(this);
            talkroutines.WriteRoutines(this);
            talkroutines.UpdateNPCRoutines(this, npcdata);

            WriteSeedAndFlags(seed.ToHex(), Flags.EncodeFlagsText(flags));
            ExtraTrackingAndInitCode(flags);
        }
예제 #31
0
        public IncentiveData(MT19337 rng, IIncentiveFlags flags, OverworldMap map)
        {
            Dictionary <MapLocation, Tuple <List <MapChange>, AccessRequirement> > fullLocationRequirements = map.FullLocationRequirements;
            var forcedItemPlacements = ItemLocations.AllOtherItemLocations.ToList();

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

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

            if (flags.IncentivizeXcalber)
            {
                incentivePool.Add(Item.Xcalber);
            }
            if (flags.IncentivizeMasamune)
            {
                incentivePool.Add(Item.Masamune);
            }
            if (flags.IncentivizeRibbon)
            {
                incentivePool.Add(Item.Ribbon);
            }
            if (flags.IncentivizeRibbon2)
            {
                incentivePool.Add(Item.Ribbon);
            }
            if (flags.IncentivizeOpal)
            {
                incentivePool.Add(Item.Opal);
            }
            if (flags.Incentivize65K)
            {
                incentivePool.Add(Item.Gold65000);
            }
            if (flags.IncentivizeBad)
            {
                incentivePool.Add(Item.Cloth);
            }
            if (flags.IncentivizeDefCastArmor)
            {
                incentivePool.Add(Item.WhiteShirt);
            }
            if (flags.IncentivizeOffCastArmor)
            {
                incentivePool.Add(Item.BlackShirt);
            }
            if (flags.IncentivizeOtherCastArmor)
            {
                incentivePool.Add(Item.PowerGauntlets);
            }
            if (flags.IncentivizeDefCastWeapon)
            {
                incentivePool.Add(Item.Defense);
            }
            if (flags.IncentivizeOffCastWeapon)
            {
                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)
            {
                incentiveLocationPool.Add(ItemLocations.VolcanoMajor);
            }
            if (flags.IncentivizeEarth)
            {
                incentiveLocationPool.Add(ItemLocations.EarthCaveMajor);
            }
            if (flags.IncentivizeMarsh)
            {
                incentiveLocationPool.Add(ItemLocations.MarshCaveMajor);
            }
            if (flags.IncentivizeMarshKeyLocked)
            {
                incentiveLocationPool.Add(ItemLocations.MarshCave13);
            }
            if (flags.IncentivizeSkyPalace)
            {
                incentiveLocationPool.Add(ItemLocations.SkyPalaceMajor);
            }
            if (flags.IncentivizeSeaShrine)
            {
                incentiveLocationPool.Add(ItemLocations.SeaShrineMajor);
            }
            if (flags.IncentivizeConeria)
            {
                incentiveLocationPool.Add(ItemLocations.ConeriaMajor);
            }
            if (flags.IncentivizeIceCave)
            {
                incentiveLocationPool.Add(ItemLocations.IceCaveMajor);
            }
            if (flags.IncentivizeOrdeals)
            {
                incentiveLocationPool.Add(ItemLocations.OrdealsMajor);
            }
            if (flags.IncentivizeCaravan)
            {
                incentiveLocationPool.Add(ItemLocations.CaravanItemShop1);
            }
            var itemLocationPool =
                ItemLocations.AllTreasures.Concat(ItemLocations.AllNPCItemLocations)
                .Where(x => !x.IsUnused && !forcedItemPlacements.Any(y => y.Address == x.Address))
                .ToList();

            if (flags.CrownlessOrdeals)
            {
                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 (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 (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
            };

            if (flags.NPCFetchItems)
            {
                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 && incentivePool.Count >= incentiveLocationPool.Count)
                {
                    validKeyLocations = validKeyLocations.Where(x => !incentiveLocationPool.Any(y => y.Address == x.Address)).ToList();
                }
            }

            if (flags.NPCItems)
            {
                var everythingButCanoe      = ~MapChange.Canoe;
                var everythingButOrbs       = ~AccessRequirement.BlackOrb;
                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();
                }
            }

            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();
        }
예제 #32
0
        public CompleteMap Generate(MT19337 rng, MapRequirements reqs)
        {
            //Constants! Get yer constants here!
            int iteration_count = 15;

            //((List<RoomSpec>)reqs.Rooms)[0].Tiledata = ((List<RoomSpec>)reqs.Rooms)[0].Tiledata;

            CompleteMap complete = new CompleteMap
            {
                Map = new Map((byte)Tile.WaterfallInside)
            };
            //(57,56)
            var startLoc = (x : 0x39, y : 0x38);

            var startingX = rng.Between(-3, 0) + startLoc.x;
            var startingY = rng.Between(-4, -1) + startLoc.y;

            var startRegion = new Region(startingX, startingY, 4, 5, Tile.WaterfallRandomEncounters);

            List <Region> regionList = new List <Region>();

            regionList.Add(startRegion);

            List <Region> endingRegions = new List <Region>();

            for (var i = 0; i < iteration_count; i++)
            {
                var startPoint = regionList[rng.Between(0, regionList.Count - 1)];
                var newRegions = RegionChain(rng, startPoint, regionList, 30 - i);
                regionList.AddRange(newRegions);
                if (newRegions.Count > 0)
                {
                    endingRegions.Add(newRegions[newRegions.Count - 1]);
                }
            }

            var foundRoomPlace = false;
            var room           = ((List <RoomSpec>)reqs.Rooms)[0];
            var room_x         = -1;
            var room_y         = -1;

            while (!foundRoomPlace && endingRegions.Count > 0)
            {
                var borderRegion = endingRegions.PickRandom(rng);

                var base_x = (borderRegion.x - (room.Width - 1) + 64) % 64;
                room_y = (borderRegion.y - room.Height + 64) % 64;
                var        x_offset      = 1;
                List <int> valid_offsets = new List <int>();

                while (x_offset < room.Width)
                {
                    room_x = (base_x + x_offset) % 64;
                    var validRoomPlace = true;
                    foreach (Region r in regionList)
                    {
                        var testVal = validRoomPlace && !r.IntersectsRoom(room, room_x, room_y);
                        if (!testVal && validRoomPlace)
                        {
                            //Console.WriteLine(room_x);
                        }
                        validRoomPlace = testVal;
                    }
                    if (validRoomPlace)
                    {
                        valid_offsets.Add(x_offset);
                    }
                    x_offset++;
                }

                if (valid_offsets.Count != 0)
                {
                    foundRoomPlace = true;
                    room_x         = (base_x + valid_offsets[rng.Between(0, valid_offsets.Count - 1)]);
                }

                endingRegions.Remove(borderRegion);
            }

            if (!foundRoomPlace)
            {
                List <int> idxs = Enumerable.Range(0, regionList.Count).ToList();
                while (!foundRoomPlace && idxs.Count > 0)
                {
                    int regionIdx = idxs.PickRandom(rng);

                    var borderRegion = regionList[regionIdx];
                    var base_x       = (borderRegion.x - (room.Width - 1) + 64) % 64;
                    room_y = (borderRegion.y - (room.Height - 1) + 64) % 64;
                    var        x_offset      = 1;
                    List <int> valid_offsets = new List <int>();

                    while (x_offset < room.Width)
                    {
                        room_x = (base_x + x_offset) % 64;
                        var validRoomPlace = true;
                        foreach (Region r in regionList)
                        {
                            var testVal = validRoomPlace && !r.IntersectsRoom(room, room_x, room_y);
                            if (!testVal && validRoomPlace)
                            {
                                //Console.WriteLine(room_x);
                            }
                            validRoomPlace = testVal;
                        }
                        if (validRoomPlace)
                        {
                            valid_offsets.Add(x_offset);
                        }
                        x_offset++;
                    }

                    if (valid_offsets.Count != 0)
                    {
                        foundRoomPlace = true;
                        room_x         = (base_x + valid_offsets[rng.Between(0, valid_offsets.Count - 1)]);
                    }

                    idxs.Remove(regionIdx);
                }
            }

            if (!foundRoomPlace)
            {
                Console.WriteLine("No room found :o");
                return(null);
            }

            //Draw every room in regionList to complete
            foreach (Region r in regionList)
            {
                r.DrawRegion(complete);
            }

            Region waterfallRoom = new Region(room_x, room_y, room);

            waterfallRoom.DrawRegion(complete);


            int doorYPos = (room_y + room.Height) % 64;

            List <int> possibleDoors = new List <int>();

            for (var i = 0; i < room.Width; i++)
            {
                if (complete.Map[((room_x + i) % 64, doorYPos)].Tile == Tile.WaterfallRandomEncounters)