/// <summary> /// Customize NPC to Customized NPC specifications /// </summary> /// <param name="index"></param> /// <param name="obj"></param> /// <param name="netDef">If false, this method doesn't set netDefaults on the npc in question.</param> public void ConvertNPCToCustom(int index, CustomNPCDefinition obj, bool netDef = true) { NPC npc = Main.npc[index]; if (netDef) { npc.SetDefaults(obj.customBase.netID); } npc._givenName = obj.customName; npc.lifeMax = obj.customHealth; npc.life = obj.customHealth + 1; npc.aiStyle = obj.customAI; npc.lavaImmune = obj.lavaImmune; npc.noGravity = obj.noGravity; npc.noTileCollide = obj.noTileCollide; npc.defense = obj.customDefense; npc.defDefense = obj.customDefense; npc.boss = obj.isBoss; if (!string.IsNullOrEmpty(obj.customSpawnMessage)) { TSPlayer.All.SendMessage(obj.customSpawnMessage, obj.customSpawnMessageColor); } }
private static int SpawnCustomNPC(int x, int y, CustomNPCDefinition definition) { //DEBUG TShock.Log.ConsoleInfo("DEBUG Spawning Custom NPC at {0}, {1} with customID {2}", x, y, definition.customID); //DEBUG int npcid = NPC.NewNPC(x, y, definition.customBase.type); if (npcid == 200) { //DEBUG TShock.Log.ConsoleInfo("DEBUG Spawning FAILED (mobcap) at {0}, {1} for customID {2}", x, y, definition.customID); //DEBUG return(-1); } Data.ConvertNPCToCustom(npcid, definition); DateTime[] dt = null; if (definition.customProjectiles != null) { dt = Enumerable.Repeat(DateTime.Now, definition.customProjectiles.Count).ToArray(); } NPCs[npcid] = new CustomNPCVars(definition, dt, Main.npc[npcid]); TSPlayer.All.SendData(PacketTypes.NpcUpdate, "", npcid); return(npcid); }
public void Add(CustomNPCDefinition definition) { if (definition == null) { throw new ArgumentNullException("definition"); } Definitions.Add(definition.customID, definition); }
/// <summary> /// List custom npcs using /csmlist <page> [onlyalive] /// </summary> /// <param name="args"></param> private void CommandListNPCS(CommandArgs args) { //Error if too many or too few params specified if (args.Parameters.Count == 0 || args.Parameters.Count > 2) { args.Player.SendInfoMessage("Usage: /csmlist <page> [onlyalive]"); return; } int page; if (!int.TryParse(args.Parameters[0], out page)) { args.Player.SendErrorMessage("Error: Invalid page defined! Please give a number."); return; } bool onlyalive = false; if (args.Parameters.Count == 2 && !bool.TryParse(args.Parameters[1], out onlyalive)) { args.Player.SendErrorMessage("Error: Invalid onlyalive flag! onlyalive can be true or false."); return; } //Check if page is valid if (page < 1) { args.Player.SendErrorMessage("Error: Invalid page defined! Page has to be 1 or bigger."); return; } var vals = NPCManager.Data.CustomNPCs.Values.Where(cd => cd != null && (!onlyalive || cd.currSpawnsVar > 0)); int start = (page - 1) * 6; int end = start + 6; int total = vals.Count(); int totalpages = (total + 5) / 6; if (start > total) { args.Player.SendErrorMessage("Error: Page too large! The last page is \"{0}\".", totalpages); return; } args.Player.SendInfoMessage("Page {0} / {1}. Only Alive = {2}", page, totalpages, onlyalive); for (int i = start; i < end && i < total; i++) { CustomNPCDefinition obj = vals.ElementAt(i); args.Player.SendInfoMessage("[{0}]: {1}. Spawned: {2}", obj.customID, obj.customName, obj.currSpawnsVar); } }
public CustomNPCVars(CustomNPCDefinition customnpc, DateTime[] lastattemptedprojectile, NPC mainnpc, bool isclone = false) { lastAttemptedProjectile = lastattemptedprojectile; isDead = false; isUncounted = false; isClone = isclone; customNPC = customnpc; mainNPC = mainnpc; droppedLoot = false; //Update if this mob is using custom ai. updateCustomAI(); }
/// <summary> /// Transforms a NPC to another Custom NPC /// </summary> /// <param name="id">CustomNPC that will be replacing it</param> /// <param name="addhealth">Increase monsters Health</param> /// <param name="additionalhealth">Amount to Increase by, if 0 - get new monsters health and add that to NPC</param> /// <param name="fullTransform">When false, only the skin of the NPC changes. When true, also applies some of the given NPC's stats</param> public bool Transform(string id, bool addhealth = false, int additionalhealth = 0) { CustomNPCDefinition search = NPCManager.Data.GetNPCbyID(id); //If not found, return false if (search == null) { return(false); } Transform(search, addhealth, additionalhealth); return(true); }
public static int SpawnNPCAroundNPC(int npcindex, ShotTile shottile, CustomNPCDefinition customnpc) { NPC npc = Main.npc[npcindex]; if (npc == null) { return(-1); } int x = (int)(npc.position.X + shottile.X); int y = (int)(npc.position.Y + shottile.Y); return(SpawnCustomNPC(x, y, customnpc)); }
/// <summary> /// (Attempts to) Spawn monsters randomly around the current x y position. /// </summary> /// <param name="npcvar">The Custom NPC to use to spawn children</param> /// <param name="amount">The amount to spawn</param> /// <param name="sethealth"></param> /// <param name="health"></param> public void Multiply(CustomNPCDefinition type, int amount, bool sethealth = false, int health = 0) { //DEBUG TShock.Log.ConsoleInfo("DEBUG [Multiply] entry. amount={0}, sethealth={1}, health={2}", amount, sethealth, health); //DEBUG // MainNPC is gone. if (mainNPC == null) { return; } if (type == null) { return; } for (int i = 0; i < amount; i++) { int x = (int)mainNPC.position.X + rand.Next(-8, 9); int y = (int)mainNPC.position.Y + rand.Next(-8, 9); int npc = NPCManager.SpawnNPCAtLocation(x, y, type); if (npc == -1) { //DEBUG TShock.Log.ConsoleInfo("DEBUG [Multiply] spawn failed. location={0}, {1}", x, y); //DEBUG continue; } var spawned = NPCManager.GetCustomNPCByIndex(npc); if (spawned == null) { continue; } if (sethealth) { spawned.mainNPC.life = health; } spawned.isClone = true; } }
/// <summary> /// Returns a list of all Custom NPC that are spawned of the given type. /// </summary> /// <param name="customnpc">The type to look for</param> /// <returns></returns> public static List <CustomNPCVars> GetAllOfType(CustomNPCDefinition customnpc) { List <CustomNPCVars> tbr = new List <CustomNPCVars>(); foreach (CustomNPCVars v in NPCs) { if (v == null) { continue; } if (v.customNPC == customnpc) { tbr.Add(v); } } return(tbr); }
/// <summary> /// Transforms a NPC to another Custom NPC /// </summary> /// <param name="npcdef">CustomNPC that will be replacing it</param> /// <param name="addhealth">Increase monsters Health</param> /// <param name="additionalhealth">Amount to Increase by, if 0 - get new monsters health and add that to NPC</param> public void Transform(CustomNPCDefinition npcdef, bool addhealth = false, int additionalhealth = 0) { //DEBUG TShock.Log.ConsoleInfo("DEBUG [TransformToCustom] addHealth={0} additionalHealth={1}", addhealth, additionalhealth); //DEBUG customNPC = npcdef; CustomTransform(); //mainNPC.type = npcdef.customBase.type; if (addhealth) { if (additionalhealth == 0) { mainNPC.life += npcdef.customHealth; } else { mainNPC.life += additionalhealth; } } postTransform(); //#Issue Number 7: Github issue //Improve this! //Temp Fix for projectiles not attaching to transformations, some one else should redo this in a better way.. DateTime[] dt = null; if (npcdef.customProjectiles != null) { dt = Enumerable.Repeat(DateTime.Now, npcdef.customProjectiles.Count).ToArray(); } NPC spawned = Main.npc[mainNPC.whoAmI]; NPCManager.NPCs[mainNPC.whoAmI] = new CustomNPCVars(npcdef, dt, spawned); NPCManager.Data.ConvertNPCToCustom(mainNPC.whoAmI, npcdef); }
/// <summary> /// This is called once the CustomNPCDefinitions have been loaded into the DefinitionManager. /// </summary> /// <param name="definitions"></param> internal void LoadFrom(DefinitionManager definitions) { foreach (var pair in definitions.Definitions) { string id = pair.Key; CustomNPCDefinition definition = pair.Value; CustomNPCs.Add(id, definition); foreach (var spawning in definition.customNPCSpawning) { if (spawning.spawnBiome != BiomeTypes.None) { AddCustomNPCToBiome(spawning.spawnBiome, id, spawning); } if (!string.IsNullOrEmpty(spawning.spawnRegion)) { AddCustomNPCToRegion(spawning.spawnRegion, id, spawning); } } } }
public bool TryGetDefinition(string id, out CustomNPCDefinition definition) { return(Definitions.TryGetValue(id, out definition)); }
/// <summary> /// Attempts to spawn the given minion for every player online, where applicable. /// </summary> /// <param name="spawnRegion"></param> /// <param name="minion"></param> /// <param name="npcdef"></param> /// <param name="attempts"></param> private static void SpawnMinion(Rectangle spawnRegion, SpawnMinion minion, CustomNPCDefinition npcdef, int attempts) { //Loop through players foreach (TSPlayer player in TShock.Players) { if (player == null || player.Dead || !player.Active || !NPCManager.Chance(minion.Chance)) { continue; } //Check if the minions can spawn anywhere, or if we need to check if players see them. if (!CurrentWave.SpawnAnywhere) { Rectangle playerFrame = new Rectangle((int)player.TPlayer.position.X, (int)player.TPlayer.position.Y, player.TPlayer.width, player.TPlayer.height); if (!playerFrame.Intersects(spawnRegion)) { continue; } } //Check biomes if (minion.BiomeConditions != BiomeTypes.None) { BiomeTypes biomes = player.GetCurrentBiomes(); if ((minion.BiomeConditions & biomes) == 0) { continue; } } int mobid = -1; //Try max attempts times. This gives attempts*50 spawn attempts at random positions. for (int i = 0; mobid == -1 && i < attempts; i++) { mobid = NPCManager.SpawnMobAroundPlayer(player, npcdef); } //Spawning failed :( if (mobid == -1) { continue; } NPCManager.GetCustomNPCByIndex(mobid).isInvasion = true; } }
public static int SpawnMobAroundPlayer(TSPlayer player, CustomNPCDefinition definition) { const int SpawnSpaceX = 3; const int SpawnSpaceY = 3; //Search for a location int screenTilesX = (int)(NPC.sWidth / 16f); int screenTilesY = (int)(NPC.sHeight / 16f); int spawnRangeX = (int)(screenTilesX * 0.7); int spawnRangeY = (int)(screenTilesY * 0.7); int safeRangeX = (int)(screenTilesX * 0.52); int safeRangeY = (int)(screenTilesY * 0.52); Vector2 position = player.TPlayer.position; int playerTileX = (int)(position.X / 16f); int playerTileY = (int)(position.Y / 16f); int spawnRangeMinX = Math.Max(0, Math.Min(Main.maxTilesX, playerTileX - spawnRangeX)); int spawnRangeMaxX = Math.Max(0, Math.Min(Main.maxTilesX, playerTileX + spawnRangeX)); int spawnRangeMinY = Math.Max(0, Math.Min(Main.maxTilesY, playerTileY - spawnRangeY)); int spawnRangeMaxY = Math.Max(0, Math.Min(Main.maxTilesY, playerTileY + spawnRangeY)); int safeRangeMinX = Math.Max(0, Math.Min(Main.maxTilesX, playerTileX - safeRangeX)); int safeRangeMaxX = Math.Max(0, Math.Min(Main.maxTilesX, playerTileX + safeRangeX)); int safeRangeMinY = Math.Max(0, Math.Min(Main.maxTilesY, playerTileY - safeRangeY)); int safeRangeMaxY = Math.Max(0, Math.Min(Main.maxTilesY, playerTileY + safeRangeY)); int spawnX = 0; int spawnY = 0; bool found = false; int attempts = 0; while (attempts < 50) { int testX = rand.Next(spawnRangeMinX, spawnRangeMaxX); int testY = rand.Next(spawnRangeMinY, spawnRangeMaxY); OTAPI.Tile.ITile testTile = Main.tile[testX, testY]; if (testTile.nactive() && Main.tileSolid[testTile.type]) { attempts++; continue; } if (!Main.wallHouse[testTile.wall]) { for (int y = testY; y < Main.maxTilesY; y++) { OTAPI.Tile.ITile test = Main.tile[testX, y]; if (test.nactive() && Main.tileSolid[test.type]) { if (testX < safeRangeMinX || testX > safeRangeMaxX || y < safeRangeMinY || y > safeRangeMaxY) { spawnX = testX; spawnY = y; found = true; break; } } } if (!found) { attempts++; } int spaceMinX = spawnX - (SpawnSpaceX / 2); int spaceMaxX = spawnX + (SpawnSpaceX / 2); int spaceMinY = spawnY - SpawnSpaceY; int spaceMaxY = spawnY; if (spaceMinX < 0 || spaceMaxX > Main.maxTilesX) { attempts++; continue; } if (spaceMinY < 0 || spaceMaxY > Main.maxTilesY) { attempts++; continue; } if (found) { for (int x = spaceMinX; x < spaceMaxX; x++) { for (int y = spaceMinY; y < spaceMaxY; y++) { if (Main.tile[x, y].nactive() && Main.tileSolid[Main.tile[x, y].type]) { found = false; break; } if (Main.tile[x, y].lava()) { found = false; break; } } } if (!found) { attempts++; continue; } } if (spawnX >= safeRangeMinX && spawnX <= safeRangeMaxX) { if (!found) { attempts++; continue; } } } } if (found) { return(SpawnCustomNPC((spawnX * 16) + 8, spawnY * 16, definition)); } return(-1); }
public static int SpawnNPCAtLocation(int x, int y, CustomNPCDefinition customnpc) { return(SpawnCustomNPC(x, y, customnpc)); }
internal static void SpawnMobsInBiomeAndRegion() { //loop through all players foreach (TSPlayer player in TShock.Players) { //Check if player exist and is connected if (player == null || !player.ConnectionAlive) { continue; } //Log.ConsoleInfo("{0} - Checking spawn for player", player.Name); //Check all biome spawns BiomeTypes biomes = player.GetCurrentBiomes(); foreach (BiomeTypes biome in availableBiomes.Where(x => biomes.HasFlag(x))) { //Log.ConsoleInfo("{0} - Checking biome for player", biome.ToString()); //Get list of mobs that can be spawned in that biome List <Tuple <string, CustomNPCSpawning> > biomeSpawns; if (!Data.BiomeSpawns.TryGetValue(biome, out biomeSpawns)) { continue; } foreach (Tuple <string, CustomNPCSpawning> obj in biomeSpawns) { //Log.ConsoleInfo("{0} - Checking mob spawn", obj.Item1); //Check spawn conditions if (!CheckSpawnConditions(obj.Item2.spawnConditions)) { //Log.ConsoleInfo("False Conditions"); continue; } CustomNPCDefinition customnpc = Data.GetNPCbyID(obj.Item1); if (customnpc.maxSpawns <= -1) { customnpc.currSpawnsVar++; } //Make sure not spawning more then maxSpawns if (customnpc.maxSpawns != -1 && customnpc.currSpawnsVar >= customnpc.maxSpawns) { continue; } //Get the last spawn attempt DateTime lastSpawnAttempt; if (!Data.LastSpawnAttempt.TryGetValue(customnpc.customID, out lastSpawnAttempt)) { lastSpawnAttempt = default(DateTime); Data.LastSpawnAttempt[customnpc.customID] = lastSpawnAttempt; } //If not enough time has passed, we skip and go to the next NPC. if ((DateTime.Now - lastSpawnAttempt).TotalSeconds < obj.Item2.spawnRate) { continue; } //Check spawn chance if (!NPCManager.Chance(obj.Item2.spawnChance)) { continue; } // Check Player Depth if (player.TileY > obj.Item2.minDepth && player.TileY < obj.Item2.maxDepth || obj.Item2.minDepth == -1 || obj.Item2.maxDepth == -1) { //Check spawn method if (obj.Item2.useTerrariaSpawn) { //All checks completed --> spawn mob int npcid = SpawnMobAroundPlayer(player, customnpc); if (npcid != -1) { Main.npc[npcid].target = player.Index; Data.LastSpawnAttempt[customnpc.customID] = DateTime.Now; customnpc.currSpawnsVar++; } } else { //All checks completed --> spawn mob int spawnX; int spawnY; TShock.Utils.GetRandomClearTileWithInRange(player.TileX, player.TileY, 50, 50, out spawnX, out spawnY); int npcid = SpawnNPCAtLocation((spawnX * 16) + 8, spawnY * 16, customnpc); if (npcid == -1) { continue; } Data.LastSpawnAttempt[customnpc.customID] = DateTime.Now; Main.npc[npcid].target = player.Index; customnpc.currSpawnsVar++; } } } } //Then check regions as well Rectangle playerRectangle = new Rectangle(player.TileX, player.TileY, player.TPlayer.width, player.TPlayer.height); foreach (Region obj in Data.RegionSpawns.Keys.Select(name => TShock.Regions.GetRegionByName(name)).Where(region => region != null && region.InArea(playerRectangle))) { List <Tuple <string, CustomNPCSpawning> > regionSpawns; if (!Data.RegionSpawns.TryGetValue(obj.Name, out regionSpawns)) { continue; } foreach (Tuple <string, CustomNPCSpawning> obj2 in regionSpawns) { //Invalid spawn conditions if (!CheckSpawnConditions(obj2.Item2.spawnConditions)) { continue; } CustomNPCDefinition customnpc = Data.GetNPCbyID(obj2.Item1); //Make sure not spawning more then maxSpawns if (customnpc.maxSpawns != -1 && customnpc.currSpawnsVar >= customnpc.maxSpawns) { continue; } //Get the last spawn attempt DateTime lastSpawnAttempt; if (!Data.LastSpawnAttempt.TryGetValue(customnpc.customID, out lastSpawnAttempt)) { lastSpawnAttempt = default(DateTime); Data.LastSpawnAttempt[customnpc.customID] = lastSpawnAttempt; } //If not enough time passed, we skip and go to the next NPC. if ((DateTime.Now - lastSpawnAttempt).TotalSeconds < obj2.Item2.spawnRate) { continue; } Data.LastSpawnAttempt[customnpc.customID] = DateTime.Now; if (!NPCManager.Chance(obj2.Item2.spawnChance)) { continue; } // Check Player Depth if (player.TileY > obj2.Item2.minDepth && player.TileY < obj2.Item2.maxDepth || obj2.Item2.minDepth == -1 || obj2.Item2.maxDepth == -1) { var region = TShock.Regions.GetRegionByName(obj2.Item2.spawnRegion); ActiveArenas.Add(region); ActiveArenas.Count(); var arenaX = region.Area.X + (region.Area.Width / 2); var arenaY = region.Area.Y + (region.Area.Height / 2); var rangeX = region.Area.Width / 2; var rangeY = region.Area.Height / 2; int spawnX; int spawnY; TShock.Utils.GetRandomClearTileWithInRange(arenaX, arenaY, rangeX, rangeY, out spawnX, out spawnY); int npcid = SpawnNPCAtLocation((spawnX * 16) + 8, spawnY * 16, customnpc); if (npcid == -1) { continue; } Main.npc[npcid].target = player.Index; customnpc.currSpawnsVar++; ActiveArenas.Remove(region); continue; } } } } }