/// <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 } } bool seesPlayers = false; //whether the monster automatically "sees" players at spawn (handled differently by some monster types) if (monsterType.Settings != null) //if settings were provided { if (monsterType.Settings.ContainsKey("SeesPlayersAtSpawn")) //if this setting was provided { seesPlayers = (bool)monsterType.Settings["SeesPlayersAtSpawn"]; //use the provided setting } } //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 "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) //if the "SeesPlayersAtSpawn" setting is true { 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) //if the "SeesPlayersAtSpawn" setting is true { 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) //if the "SeesPlayersAtSpawn" setting is true { 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 "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); break; case "duggy": monster = new DuggyFTM(tile); break; case "dust": case "sprite": case "dustsprite": case "dust sprite": case "spirit": case "dustspirit": case "dust spirit": monster = new DustSpirit(tile); break; case "ghost": monster = new GhostFTM(tile); break; case "carbonghost": case "carbon ghost": monster = new GhostFTM(tile, "Carbon 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 "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 "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 "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 "brute": case "shadowbrute": case "shadow brute": monster = new ShadowBrute(tile); break; case "shaman": case "shadowshaman": case "shadow shaman": monster = new ShadowShaman(tile); break; case "skeleton": monster = new Skeleton(tile); if (seesPlayers) //if the "SeesPlayersAtSpawn" setting is true { IReflectedField <bool> spottedPlayer = Helper.Reflection.GetField <bool>(monster, "spottedPlayer"); //try to access this skeleton's private "spottedPlayer" field spottedPlayer.SetValue(true); monster.IsWalkingTowardPlayer = true; } break; case "squid": case "kid": case "squidkid": case "squid kid": monster = new SquidKidFTM(tile); break; default: //if the name doesn't match any directly known monster types 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); }
/********* ** Private methods *********/ private void SpawnEntity(string command, string[] args) { //We need a world to spawn monsters in, duh if (Context.IsWorldReady) { // Determine if we have arguments if (args.Length > 0) { //Set defaults NPC entity = null; int xTile = Game1.player.getTileX(); int yTile = Game1.player.getTileY(); int amount = 1; //Ensure provided coordinatees are actually coordinates try { //Determine X tile if (args.Length >= 2) { if (!args[1].Equals("~")) { xTile = int.Parse(args[1]); } } //Determine Y tile if (args.Length >= 3) { if (!args[1].Equals("~")) { yTile = int.Parse(args[2]); } } } catch (Exception) { Monitor.Log("Arguments 1 and 2 must be coordinates or '~' to use the Farmer's coordinates! Make sure you don't add any brackets!"); return; } try { if (args.Length >= 4) { amount = int.Parse(args[3]); if (amount < 1) { throw new Exception(); } } } catch (Exception) { Monitor.Log("Argument 3 must be an amount larger than 0!"); return; } Vector2 pos = new Vector2(xTile, yTile); for (int i = 0; i < amount; i++) { // Determine the monster to spawn switch (args[0]) { case "greenSlime": entity = new GreenSlime(pos, 0); break; case "blueSlime": entity = new GreenSlime(pos, 40); break; case "redSlime": entity = new GreenSlime(pos, 80); break; case "purpleSlime": entity = new GreenSlime(pos, 121); break; case "yellowSlime": entity = new GreenSlime(pos, new Color(255, 255, 50)); break; case "blackSlime": Random r = new Random(); entity = new GreenSlime(pos, new Color(40 + r.Next(10), 40 + r.Next(10), 40 + r.Next(10))); break; case "bat": entity = new Bat(pos); break; //minelevel: 0 - 40 - 80 - 171 -> type case "frostBat": entity = new Bat(pos, 40); break; case "lavaBat": entity = new Bat(pos, 80); break; case "iridiumBat": entity = new Bat(pos, 171); break; case "bug": entity = new Bug(pos, 0); break; //available areatypes: 121 -> armored case "armoredBug": entity = new Bug(pos, 121); break; case "fly": entity = new Fly(pos); break; //hard -> mutant case "mutantFly": entity = new Fly(pos, true); break; case "ghost": entity = new Ghost(pos); break; //name -> carbon ghost case "carbonGhost": entity = new Ghost(pos, "Carbon Ghost"); break; case "grub": entity = new Grub(pos); break; //hard -> mutant case "mutantGrub": entity = new Grub(pos, true); break; case "rockCrab": entity = new RockCrab(pos); break; //name -> iridium crab case "lavaCrab": entity = new LavaCrab(pos); break; case "iridiumCrab": entity = new RockCrab(pos, "Iridium Crab"); break; case "metalHead": entity = new MetalHead(pos, 80); break; //mineareas: 0, 40, 80 - seems to only spawn at 80+ case "rockGolem": entity = new RockGolem(pos); break; //mineareas: 0, 40, 80 - changes health and damage; difficultymod: case "wildernessGolem": entity = new RockGolem(pos, 5); break; case "mummy": entity = new Mummy(pos); break; case "serpent": entity = new Serpent(pos); break; case "shadowBrute": entity = new ShadowBrute(pos); break; case "shadowShaman": entity = new ShadowShaman(pos); break; case "skeleton": entity = new Skeleton(pos); break; case "squidKid": entity = new SquidKid(pos); break; case "duggy": entity = new Duggy(pos); break; case "dustSpirit": entity = new DustSpirit(pos); break; } if (entity != null) { entity.currentLocation = Game1.currentLocation; entity.setTileLocation(new Vector2(xTile, yTile)); Game1.currentLocation.addCharacter(entity); } else { Monitor.Log($"{args[0]} not found! Type monster_list to view a list of available monsters to spawn!"); return; } } Monitor.Log($"{amount} {entity.Name} added at {entity.currentLocation.Name} {entity.getTileX()},{entity.getTileY()}", LogLevel.Info); } else { Monitor.Log("Usage: monster_spawn <name> [posX] [posY] [amount]\n\nUses Farmer's coordinates if none or '~' was given."); } } else { Monitor.Log("Load a save first!"); } }
private Monster GetMonster(int x, Vector2 loc) { Monster m; switch (x) { case 0: m = new DustSpirit(loc); break; case 1: m = new Grub(loc); break; case 2: m = new Skeleton(loc); break; case 3: m = new RockCrab(loc); break; case 4: m = new Ghost(loc); break; case 5: m = new GreenSlime(loc); break; case 6: m = new RockGolem(loc); break; case 7: m = new ShadowBrute(loc); break; case 8: int y = rand.Next(1, 6); //m = new Monster(); if (y == 1) { m = new RockCrab(loc, "Iridium Crab"); } else if (y == 2) { m = new Ghost(loc, "Carbon Ghost"); } else if (y == 3) { m = new LavaCrab(loc); } //else if (y == 4) //m = new Bat(loc, Math.Max(Game1.player.CombatLevel * 5, 50)); else if (y == 4) { m = new GreenSlime(loc, Math.Max(Game1.player.CombatLevel * 5, 50)); } else if (y == 5) { m = new BigSlime(loc, Math.Max(Game1.player.CombatLevel * 5, 50)); } else { m = new Mummy(loc); } break; default: m = new Monster(); break; } return(m); }
/// <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); }