Esempio n. 1
0
            /// <summary>Generates a monster and places it on the specified map and tile.</summary>
            /// <param name="monsterType">The monster type's name and an optional dictionary of monster-specific settings.</param>
            /// <param name="location">The GameLocation where the monster should be spawned.</param>
            /// <param name="tile">The x/y coordinates of the tile where the monster should be spawned.</param>
            /// <param name="areaID">The UniqueAreaID of the related SpawnArea. Required for log messages.</param>
            /// <returns>Returns the monster's ID value, or null if the spawn process failed.</returns>
            public static int?SpawnMonster(MonsterType monsterType, GameLocation location, Vector2 tile, string areaID = "")
            {
                Monster monster = null;                                                                      //an instatiated monster, to be spawned into the world later

                Color?color = null;                                                                          //the monster's color (used by specific monster types)

                if (monsterType.Settings != null)                                                            //if settings were provided
                {
                    if (monsterType.Settings.ContainsKey("Color"))                                           //if this setting was provided
                    {
                        string[]   colorText    = ((string)monsterType.Settings["Color"]).Trim().Split(' '); //split the setting string into strings for each number
                        List <int> colorNumbers = new List <int>();
                        foreach (string text in colorText)                                                   //for each string
                        {
                            int num = Convert.ToInt32(text);                                                 //convert it to a number
                            if (num < 0)
                            {
                                num = 0;
                            }                         //minimum 0
                            else if (num > 255)
                            {
                                num = 255;
                            }                      //maximum 255
                            colorNumbers.Add(num); //add it to the list
                        }

                        //convert strings into RGBA values
                        int r = Convert.ToInt32(colorNumbers[0]);
                        int g = Convert.ToInt32(colorNumbers[1]);
                        int b = Convert.ToInt32(colorNumbers[2]);
                        int a;
                        if (colorNumbers.Count > 3) //if the setting included an "A" value
                        {
                            a = Convert.ToInt32(colorNumbers[3]);
                        }
                        else //if the setting did not include an "A" value
                        {
                            a = 255; //default to no transparency
                        }

                        color = new Color(r, g, b, a);                                                                     //set the color
                    }
                    else if (monsterType.Settings.ContainsKey("MinColor") && monsterType.Settings.ContainsKey("MaxColor")) //if color wasn't provided, but mincolor & maxcolor were
                    {
                        string[]   minColorText    = ((string)monsterType.Settings["MinColor"]).Trim().Split(' ');         //split the setting string into strings for each number
                        List <int> minColorNumbers = new List <int>();
                        foreach (string text in minColorText)                                                              //for each string
                        {
                            int num = Convert.ToInt32(text);                                                               //convert it to a number
                            if (num < 0)
                            {
                                num = 0;
                            }                         //minimum 0
                            else if (num > 255)
                            {
                                num = 255;
                            }                         //maximum 255
                            minColorNumbers.Add(num); //add it to the list
                        }

                        string[]   maxColorText    = ((string)monsterType.Settings["MaxColor"]).Trim().Split(' '); //split the setting string into strings for each number
                        List <int> maxColorNumbers = new List <int>();
                        foreach (string text in maxColorText)                                                      //for each string
                        {
                            int num = Convert.ToInt32(text);                                                       //convert it to a number
                            if (num < 0)
                            {
                                num = 0;
                            }                         //minimum 0
                            else if (num > 255)
                            {
                                num = 255;
                            }                         //maximum 255
                            maxColorNumbers.Add(num); //convert to number
                        }

                        for (int x = 0; x < minColorNumbers.Count && x < maxColorNumbers.Count; x++) //for each pair of values
                        {
                            if (minColorNumbers[x] > maxColorNumbers[x])                             //if min > max
                            {
                                //swap min and max
                                int temp = minColorNumbers[x];
                                minColorNumbers[x] = maxColorNumbers[x];
                                maxColorNumbers[x] = temp;
                            }
                        }

                        //pick random RGBA values between min and max
                        int r = RNG.Next(minColorNumbers[0], maxColorNumbers[0] + 1);
                        int g = RNG.Next(minColorNumbers[1], maxColorNumbers[1] + 1);
                        int b = RNG.Next(minColorNumbers[2], maxColorNumbers[2] + 1);
                        int a;
                        if (minColorNumbers.Count > 3 && maxColorNumbers.Count > 3) //if both settings included an "A" value
                        {
                            a = RNG.Next(minColorNumbers[3], maxColorNumbers[3] + 1);
                        }
                        else //if one/both of the settings did not include an "A" value
                        {
                            a = 255; //default to no transparency
                        }

                        color = new Color(r, g, b, a); //set the color
                    }
                }

                //set fields that affect some monster types in different ways
                bool seesPlayers     = false;                                           //whether the monster automatically "sees" players at spawn
                int  facingDirection = 2;                                               //the direction the monster should be facing at spawn
                bool rangedAttacks   = true;                                            //whether the monster is allowed to use its ranged attacks (if any)

                if (monsterType.Settings != null)                                       //if settings were provided
                {
                    if (monsterType.Settings.ContainsKey("SeesPlayersAtSpawn"))         //if this setting was provided
                    {
                        seesPlayers = (bool)monsterType.Settings["SeesPlayersAtSpawn"]; //use it
                    }

                    if (monsterType.Settings.ContainsKey("FacingDirection"))                      //if this setting was provided
                    {
                        string directionString = (string)monsterType.Settings["FacingDirection"]; //get it
                        switch (directionString.Trim().ToLower())
                        {
                        //get an integer representing the direction
                        case "up":
                            facingDirection = 0;
                            break;

                        case "right":
                            facingDirection = 1;
                            break;

                        case "down":
                            facingDirection = 2;
                            break;

                        case "left":
                            facingDirection = 3;
                            break;
                        }
                    }

                    if (monsterType.Settings.ContainsKey("RangedAttacks"))           //if this setting was provided
                    {
                        rangedAttacks = (bool)monsterType.Settings["RangedAttacks"]; //use it
                    }
                }

                //create a new monster based on the provided name & apply type-specific settings
                switch (monsterType.MonsterName.ToLower()) //avoid most casing issues by making this lower-case
                {
                case "bat":
                    monster = new BatFTM(tile, 0);
                    break;

                case "frostbat":
                case "frost bat":
                    monster = new BatFTM(tile, 40);
                    break;

                case "lavabat":
                case "lava bat":
                    monster = new BatFTM(tile, 80);
                    break;

                case "iridiumbat":
                case "iridium bat":
                    monster = new BatFTM(tile, 171);
                    break;

                case "doll":
                case "curseddoll":
                case "cursed doll":
                    monster = new BatFTM(tile, -666);
                    break;

                case "skull":
                case "hauntedskull":
                case "haunted skull":
                    monster = new BatFTM(tile, 77377);
                    break;

                case "magmasprite":
                case "magma sprite":
                    monster = new BatFTM(tile, -555);
                    break;

                case "magmasparker":
                case "magma sparker":
                    monster = new BatFTM(tile, -556);
                    break;

                case "bigslime":
                case "big slime":
                case "biggreenslime":
                case "big green slime":
                    monster = new BigSlimeFTM(tile, 0);
                    if (color.HasValue)                               //if color was provided
                    {
                        ((BigSlimeFTM)monster).c.Value = color.Value; //set its color after creation
                    }
                    if (seesPlayers)
                    {
                        monster.IsWalkingTowardPlayer = true;
                    }
                    break;

                case "bigblueslime":
                case "big blue slime":
                case "bigfrostjelly":
                case "big frost jelly":
                    monster = new BigSlimeFTM(tile, 40);
                    if (color.HasValue)                               //if color was provided
                    {
                        ((BigSlimeFTM)monster).c.Value = color.Value; //set its color after creation
                    }
                    if (seesPlayers)
                    {
                        monster.IsWalkingTowardPlayer = true;
                    }
                    break;

                case "bigredslime":
                case "big red slime":
                case "bigredsludge":
                case "big red sludge":
                    monster = new BigSlimeFTM(tile, 80);
                    if (color.HasValue)                               //if color was provided
                    {
                        ((BigSlimeFTM)monster).c.Value = color.Value; //set its color after creation
                    }
                    if (seesPlayers)
                    {
                        monster.IsWalkingTowardPlayer = true;
                    }
                    break;

                case "bigpurpleslime":
                case "big purple slime":
                case "bigpurplesludge":
                case "big purple sludge":
                    monster = new BigSlimeFTM(tile, 121);
                    if (color.HasValue)                               //if color was provided
                    {
                        ((BigSlimeFTM)monster).c.Value = color.Value; //set its color after creation
                    }
                    if (seesPlayers)                                  //if the "SeesPlayersAtSpawn" setting is true
                    {
                        monster.IsWalkingTowardPlayer = true;
                    }
                    break;

                case "bluesquid":
                case "blue squid":
                    monster = new BlueSquid(tile);
                    break;

                case "bug":
                    monster = new Bug(tile, 0);
                    break;

                case "armoredbug":
                case "armored bug":
                    monster = new Bug(tile, 121);
                    break;

                case "dino":
                case "dinomonster":
                case "dino monster":
                case "pepper":
                case "pepperrex":
                case "pepper rex":
                case "rex":
                    monster = new DinoMonster(tile);
                    if (!rangedAttacks)
                    {
                        DinoMonster dino = monster as DinoMonster;
                        dino.timeUntilNextAttack = int.MaxValue;
                        dino.nextFireTime        = int.MaxValue;
                    }
                    break;

                case "duggy":
                    monster = new DuggyFTM(tile);
                    break;

                case "magmaduggy":
                case "magma duggy":
                    monster = new DuggyFTM(tile, true);
                    break;

                case "dust":
                case "sprite":
                case "dustsprite":
                case "dust sprite":
                case "spirit":
                case "dustspirit":
                case "dust spirit":
                    monster = new DustSpiritFTM(tile);
                    break;

                case "dwarvishsentry":
                case "dwarvish sentry":
                case "dwarvish":
                case "sentry":
                    monster = new DwarvishSentry(tile);
                    for (int x = Game1.delayedActions.Count - 1; x >= 0; x--)       //check each existing DelayedAction (from last to first)
                    {
                        if (Game1.delayedActions[x].stringData == "DwarvishSentry") //if this action seems to be playing this monster's sound effect
                        {
                            Game1.delayedActions.Remove(Game1.delayedActions[x]);   //remove the action (preventing this monster's global sound effect after creation)
                            break;                                                  //skip the rest of the actions
                        }
                    }
                    break;

                case "ghost":
                    monster = new GhostFTM(tile);
                    break;

                case "carbonghost":
                case "carbon ghost":
                    monster = new GhostFTM(tile, "Carbon Ghost");
                    break;

                case "putridghost":
                case "putrid ghost":
                    monster = new GhostFTM(tile, "Putrid Ghost");
                    break;

                case "slime":
                case "greenslime":
                case "green slime":
                    monster = new GreenSlime(tile, 0);
                    if (color.HasValue)                                  //if color was also provided
                    {
                        ((GreenSlime)monster).color.Value = color.Value; //set its color after creation
                    }
                    break;

                case "blueslime":
                case "blue slime":
                case "frostjelly":
                case "frost jelly":
                    monster = new GreenSlime(tile, 40);
                    if (color.HasValue)                                  //if color was also provided
                    {
                        ((GreenSlime)monster).color.Value = color.Value; //set its color after creation
                    }
                    break;

                case "redslime":
                case "red slime":
                case "redsludge":
                case "red sludge":
                    monster = new GreenSlime(tile, 80);
                    if (color.HasValue)                                  //if color was also provided
                    {
                        ((GreenSlime)monster).color.Value = color.Value; //set its color after creation
                    }
                    break;

                case "purpleslime":
                case "purple slime":
                case "purplesludge":
                case "purple sludge":
                    monster = new GreenSlime(tile, 121);
                    if (color.HasValue)                                  //if color was also provided
                    {
                        ((GreenSlime)monster).color.Value = color.Value; //set its color after creation
                    }
                    break;

                case "tigerslime":
                case "tiger slime":
                    monster = new GreenSlime(tile, 0);                   //create any "normal" slime
                    ((GreenSlime)monster).makeTigerSlime();              //convert it into a tiger slime
                    if (color.HasValue)                                  //if color was also provided
                    {
                        ((GreenSlime)monster).color.Value = color.Value; //set its color after creation
                    }
                    break;

                case "prismaticslime":
                case "prismatic slime":
                    monster = new GreenSlime(tile, 0);                   //create any "normal" slime
                    ((GreenSlime)monster).makePrismatic();               //convert it into a prismatic slime
                    if (color.HasValue)                                  //if color was also provided
                    {
                        ((GreenSlime)monster).color.Value = color.Value; //set its color after creation
                    }
                    break;

                case "grub":
                case "cavegrub":
                case "cave grub":
                    monster = new GrubFTM(tile, false);
                    break;

                case "fly":
                case "cavefly":
                case "cave fly":
                    monster = new FlyFTM(tile, false);
                    break;

                case "mutantgrub":
                case "mutant grub":
                    monster = new GrubFTM(tile, true);
                    break;

                case "mutantfly":
                case "mutant fly":
                    monster = new FlyFTM(tile, true);
                    break;

                case "metalhead":
                case "metal head":
                    monster = new MetalHead(tile, 0);
                    if (color.HasValue)                             //if color was provided
                    {
                        ((MetalHead)monster).c.Value = color.Value; //set its color after creation
                    }
                    break;

                case "hothead":
                case "hot head":
                    monster = new HotHead(tile);
                    if (color.HasValue)                           //if color was provided
                    {
                        ((HotHead)monster).c.Value = color.Value; //set its color after creation
                    }
                    break;

                case "lavalurk":
                case "lava lurk":
                    monster = new LavaLurkFTM(tile, rangedAttacks);
                    break;

                case "leaper":
                    monster = new Leaper(tile);
                    break;

                case "mummy":
                    monster = new MummyFTM(tile);
                    break;

                case "rockcrab":
                case "rock crab":
                    monster = new RockCrab(tile);
                    break;

                case "lavacrab":
                case "lava crab":
                    monster = new LavaCrab(tile);
                    break;

                case "iridiumcrab":
                case "iridium crab":
                    monster = new RockCrab(tile, "Iridium Crab");
                    break;

                case "falsemagmacap":
                case "false magma cap":
                case "magmacap":
                case "magma cap":
                    monster            = new RockCrab(tile, "False Magma Cap");
                    monster.HideShadow = true;     //hide shadow, making them look more like "real" magma caps
                    break;

                case "stickbug":
                case "stick bug":
                    monster = new RockCrab(tile);
                    (monster as RockCrab).makeStickBug();
                    break;

                case "rockgolem":
                case "rock golem":
                case "stonegolem":
                case "stone golem":
                    monster = new RockGolemFTM(tile);
                    break;

                case "wildernessgolem":
                case "wilderness golem":
                    monster = new RockGolemFTM(tile, Game1.player.CombatLevel);
                    break;

                case "serpent":
                    monster = new SerpentFTM(tile);
                    break;

                case "royalserpent":
                case "royal serpent":
                    monster = new SerpentFTM(tile, "Royal Serpent");
                    break;

                case "brute":
                case "shadowbrute":
                case "shadow brute":
                    monster = new ShadowBrute(tile);
                    break;

                case "shaman":
                case "shadowshaman":
                case "shadow shaman":
                    monster = new ShadowShaman(tile);
                    if (!rangedAttacks)
                    {
                        Helper.Reflection.GetField <int>(monster, "coolDown", false)?.SetValue(int.MaxValue);    //set spell cooldown to max
                    }
                    break;

                case "sniper":
                case "shadowsniper":
                case "shadow sniper":
                    monster = new Shooter(tile);
                    if (!rangedAttacks)
                    {
                        (monster as Shooter).nextShot = float.MaxValue;     //set shot cooldown to max
                    }
                    break;

                case "skeleton":
                    monster = new SkeletonFTM(tile, false, rangedAttacks);
                    if (seesPlayers)
                    {
                        Helper.Reflection.GetField <bool>(monster, "spottedPlayer", false)?.SetValue(true);    //set "spotted player" field to true
                        monster.IsWalkingTowardPlayer = true;
                    }
                    break;

                case "skeletonmage":
                case "skeleton mage":
                    monster = new SkeletonFTM(tile, true, rangedAttacks);
                    if (seesPlayers)
                    {
                        Helper.Reflection.GetField <bool>(monster, "spottedPlayer", false)?.SetValue(true);    //set "spotted player" field to true
                        monster.IsWalkingTowardPlayer = true;
                    }
                    break;

                case "spiker":
                    monster = new Spiker(tile, facingDirection);
                    break;

                case "squidkid":
                case "squid kid":
                    monster = new SquidKidFTM(tile);
                    if (!rangedAttacks)
                    {
                        Helper.Reflection.GetField <int>(monster, "lastFireball", false)?.SetValue(int.MaxValue);    //set fireball cooldown to max
                    }
                    break;

                default:                                                                           //if the name doesn't match any directly known monster types
                    //check MTF monster types
                    if (MonstersTheFrameworkAPI.IsKnownMonsterType(monsterType.MonsterName, true)) //if this is a known (and previously validated) monster type from MTF
                    {
                        monster = MonstersTheFrameworkAPI.CreateMonster(monsterType.MonsterName);  //create it through the MTF interface
                        break;
                    }
                    //handle the name as a custom Type
                    Type externalType = GetTypeFromName(monsterType.MonsterName, typeof(Monster)); //find a monster subclass with a matching name
                    monster = (Monster)Activator.CreateInstance(externalType, tile);               //create a monster with the Vector2 constructor
                    break;
                }

                if (monster == null)
                {
                    Monitor.Log($"The monster to be spawned (\"{monsterType.MonsterName}\") doesn't match any known monster types. Make sure that name isn't misspelled in your config file.", LogLevel.Info);
                    return(null);
                }

                int?ID = MonsterTracker.AddMonster(monster);  //generate an ID for this monster

                if (!ID.HasValue)
                {
                    Monitor.Log("A new monster ID could not be generated. This is may be due to coding issue; please report it to this mod's developer. This monster won't be spawned.", LogLevel.Warn);
                    return(null);
                }
                monster.id = ID.Value;                                       //assign the ID to this monster

                monster.MaxHealth = monster.Health;                          //some monster types set Health on creation and expect MaxHealth to be updated like this

                ApplyMonsterSettings(monster, monsterType.Settings, areaID); //adjust the monster based on any other provided optional settings

                //spawn the completed monster at the target location
                Monitor.VerboseLog($"Spawning monster. Type: {monsterType.MonsterName}. Location: {tile.X},{tile.Y} ({location.Name}).");
                monster.currentLocation = location;
                monster.setTileLocation(tile);
                location.addCharacter(monster);
                return(monster.id);
            }