// Private members

        public async Task _plusZone(Species species, string zoneList, string notes, bool onlyShowErrors = false)
        {
            // Get the zones from user input.
            ZoneListResult zones = await ZoneUtils.GetZonesByZoneListAsync(zoneList);

            // Add the zones to the species.
            await SpeciesUtils.AddZonesAsync(species, zones.Zones, notes);

            if (zones.InvalidZones.Count() > 0)
            {
                // Show a warning if the user provided any invalid zones.

                await BotUtils.ReplyAsync_Warning(Context, string.Format("{0} {1} not exist.",
                                                                         StringUtils.ConjunctiveJoin(", ", zones.InvalidZones.Select(x => string.Format("**{0}**", ZoneUtils.FormatZoneName(x))).ToArray()),
                                                                         zones.InvalidZones.Count() == 1 ? "does" : "do"));
            }

            if (zones.Zones.Count() > 0 && !onlyShowErrors)
            {
                // Show a confirmation of all valid zones.

                await BotUtils.ReplyAsync_Success(Context, string.Format("**{0}** now inhabits {1}.",
                                                                         species.ShortName,
                                                                         StringUtils.ConjunctiveJoin(", ", zones.Zones.Select(x => string.Format("**{0}**", x.GetFullName())).ToArray())));
            }
        }
Beispiel #2
0
        private Model.Infrastructure.Service CheckJob(Job job, string zoneId = null)
        {
            if (job == null)
            {
                throw new ArgumentException("Job cannot be null.");
            }

            if (StringUtils.IsEmpty(job.Name))
            {
                throw new ArgumentException("Job name must be specified.");
            }

            Model.Infrastructure.Service service = ZoneUtils.GetService(
                EnvironmentUtils.GetTargetZone(registrationService.CurrentEnvironment, zoneId),
                job.Name + "s",
                ServiceType.FUNCTIONAL);

            if (service == null)
            {
                throw new ArgumentException(
                          $"A FUNCTIONAL service with the name {job.Name}s cannot be found in the current environment");
            }

            return(service);
        }
        public async Task SetZonePic(string zone, string imageUrl)
        {
            // Make sure that the given zone exists.

            Zone z = await ZoneUtils.GetZoneAsync(zone);

            if (!await BotUtils.ReplyValidateZoneAsync(Context, z))
            {
                return;
            }

            // Make sure the image URL is valid.

            if (!await BotUtils.ReplyIsImageUrlValidAsync(Context, imageUrl))
            {
                return;
            }

            // Update the zone.

            using (SQLiteCommand cmd = new SQLiteCommand("UPDATE Zones SET pics=$pics WHERE id=$id;")) {
                cmd.Parameters.AddWithValue("$pics", imageUrl);
                cmd.Parameters.AddWithValue("$id", z.Id);

                await Database.ExecuteNonQuery(cmd);
            }

            await BotUtils.ReplyAsync_Success(Context, string.Format("Successfully updated the picture for **{0}**.", z.GetFullName()));
        }
        public async Task SetParentZone(string zoneName, string parentZoneName)
        {
            Zone zone = await ZoneUtils.GetZoneAsync(zoneName);

            Zone parent = await ZoneUtils.GetZoneAsync(parentZoneName);

            if (await BotUtils.ReplyValidateZoneAsync(Context, zone) && await BotUtils.ReplyValidateZoneAsync(Context, parent))
            {
                if (zone.Id == parent.Id)
                {
                    await BotUtils.ReplyAsync_Error(Context, "A zone cannot be its own parent.");
                }
                else if (parent.ParentId == zone.Id)
                {
                    await BotUtils.ReplyAsync_Error(Context, "A zone cannot have its child as its parent.");
                }
                else if (zone.ParentId == parent.Id)
                {
                    await BotUtils.ReplyAsync_Warning(Context, string.Format("The parent zone of **{0}** is already **{1}**.",
                                                                             zone.FullName,
                                                                             parent.FullName));
                }
                else
                {
                    zone.ParentId = parent.Id;

                    await ZoneUtils.UpdateZoneAsync(zone);

                    await BotUtils.ReplyAsync_Success(Context, string.Format("Successfully set the parent zone of **{0}** to **{1}**.",
                                                                             zone.FullName,
                                                                             parent.FullName));
                }
            }
        }
Beispiel #5
0
        private async Task _generateOpponentAsync()
        {
            // Pick a random species from the same zone as the player's gotchi.

            List <Species> species_list = new List <Species>();

            foreach (SpeciesZone zone in await SpeciesUtils.GetZonesAsync(await SpeciesUtils.GetSpeciesAsync(player1.Gotchi.Gotchi.SpeciesId)))
            {
                species_list.AddRange((await ZoneUtils.GetSpeciesAsync(zone.Zone)).Where(x => !x.IsExtinct));
            }

            player2 = new PlayerState();

            if (species_list.Count() > 0)
            {
                player2.Gotchi = await GotchiUtils.GenerateGotchiAsync(new GotchiGenerationParameters {
                    Base            = player1.Gotchi.Gotchi,
                    Species         = species_list[BotUtils.RandomInteger(species_list.Count())],
                    MinLevel        = player1.Gotchi.Stats.Level - 3,
                    MaxLevel        = player1.Gotchi.Stats.Level + 3,
                    GenerateMoveset = true,
                    GenerateStats   = true
                });
            }

            // Set the opponent.

            if (player2.Gotchi != null)
            {
                player2.Gotchi.Gotchi.OwnerId = WildGotchiUserId;
                player2.Gotchi.Gotchi.Id      = WildGotchiId;
            }
        }
        public async Task MinusZone(string genus, string species, string zoneList)
        {
            // Ensure that the user has necessary privileges to use this command.
            if (!await BotUtils.ReplyHasPrivilegeAsync(Context, PrivilegeLevel.ServerModerator))
            {
                return;
            }

            // Get the specified species.

            Species sp = await BotUtils.ReplyFindSpeciesAsync(Context, genus, species);

            if (sp is null)
            {
                return;
            }

            // Get the zones that the species currently resides in.
            // These will be used to show warning messages (e.g., doesn't exist in the given zone).

            long[] current_zone_ids = (await BotUtils.GetZonesFromDb(sp.Id)).Select(x => x.Id).ToArray();

            // Get the zones from user input.
            ZoneListResult zones = await ZoneUtils.GetZonesByZoneListAsync(zoneList);

            // Remove the zones from the species.
            await SpeciesUtils.RemoveZonesAsync(sp, zones.Zones);

            if (zones.InvalidZones.Count() > 0)
            {
                // Show a warning if the user provided any invalid zones.

                await BotUtils.ReplyAsync_Warning(Context, string.Format("{0} {1} not exist.",
                                                                         StringUtils.ConjunctiveJoin(", ", zones.InvalidZones.Select(x => string.Format("**{0}**", ZoneUtils.FormatZoneName(x))).ToArray()),
                                                                         zones.InvalidZones.Count() == 1 ? "does" : "do"));
            }

            if (zones.Zones.Any(x => !current_zone_ids.Contains(x.Id)))
            {
                // Show a warning if the species wasn't in one or more of the zones provided.

                await BotUtils.ReplyAsync_Warning(Context, string.Format("**{0}** is already absent from {1}.",
                                                                         sp.ShortName,
                                                                         StringUtils.ConjunctiveJoin(", ", zones.Zones.Where(x => !current_zone_ids.Contains(x.Id)).Select(x => string.Format("**{0}**", x.GetFullName())).ToArray())));
            }

            if (zones.Zones.Any(x => current_zone_ids.Contains(x.Id)))
            {
                // Show a confirmation of all valid zones.

                await BotUtils.ReplyAsync_Success(Context, string.Format("**{0}** no longer inhabits {1}.",
                                                                         sp.ShortName,
                                                                         StringUtils.DisjunctiveJoin(", ", zones.Zones.Where(x => current_zone_ids.Contains(x.Id)).Select(x => string.Format("**{0}**", x.GetFullName())).ToArray())));
            }
        }
        public async Task AddZone(string name, string type = "", string description = "")
        {
            if (string.IsNullOrEmpty(name))
            {
                await BotUtils.ReplyAsync_Error(Context, "Zone name cannot be empty.");
            }
            else
            {
                // Allow the user to specify zones with numbers (e.g., "1") or single letters (e.g., "A").
                // Otherwise, the name is taken as-is.
                name = ZoneUtils.FormatZoneName(name).ToLower();

                // If an invalid type was provided, assume the user meant it as a description instead.
                // i.e., "addzone <name> <description>"

                ZoneType zone_type = await ZoneUtils.GetZoneTypeAsync(type);

                if (zone_type is null || zone_type.Id == ZoneType.NullZoneTypeId)
                {
                    description = type;

                    // Attempt to determine the zone type automatically if one wasn't provided.
                    // Currently, this is only possible if users are using the default zone types (i.e. "aquatic" and "terrestrial").

                    zone_type = await ZoneUtils.GetDefaultZoneTypeAsync(name);
                }

                if (await ZoneUtils.GetZoneAsync(name) != null)
                {
                    // Don't attempt to create the zone if it already exists.

                    await BotUtils.ReplyAsync_Warning(Context, string.Format("A zone named \"{0}\" already exists.", ZoneUtils.FormatZoneName(name)));
                }
                else
                {
                    await ZoneUtils.AddZoneAsync(new Zone {
                        Name        = name,
                        Description = description,
                        ZoneTypeId  = zone_type.Id
                    });

                    await BotUtils.ReplyAsync_Success(Context, string.Format("Successfully created new {0} zone, **{1}**.",
                                                                             zone_type.Name.ToLower(),
                                                                             ZoneUtils.FormatZoneName(name)));
                }
            }
        }
Beispiel #8
0
        private static async Task <WikiLinkList> _generateLinkifyListAsync()
        {
            // Returns a dictionary of substrings that should be turned into page links in page content.

            WikiLinkList list = new WikiLinkList();

            // Add species names to the dictionary.

            Species[] species_list = await SpeciesUtils.GetSpeciesAsync();

            foreach (Species species in species_list)
            {
                list.Add(species.ShortName.ToLower(), species.FullName);
                list.Add(species.FullName.ToLower(), species.FullName);
                list.Add(species.Name.ToLower(), species.FullName);

                if (!string.IsNullOrEmpty(species.CommonName))
                {
                    list.Add(species.CommonName.ToLower(), species.FullName);
                }
            }

            foreach (Species species in species_list)
            {
                // Also linkify binomial names that might be using outdated genera (e.g. Species moved to a new genus since the description was written).
                // Only do this for species that have a unique name-- otherwise, there's no way to know for sure which species to link to!
                // This might create some false-positives, so it could be a good idea to limit matches only to known genera (at the expense of a significantly longer regex).

                if (list.Count(x => x.Value == species.Name.ToLower()) == 1)
                {
                    list.Add(string.Format(WikiPageUtils.UnlinkedWikiTextPatternFormat, @"[A-Z](?:[a-z]+|\.)\s" + Regex.Escape(species.Name.ToLower())), species.FullName, WikiLinkListDataType.Regex);
                }
            }

            // Add zone names to the dictionary.

            Zone[] zones_list = await ZoneUtils.GetZonesAsync();

            foreach (Zone zone in zones_list)
            {
                list.Add(zone.FullName.ToLower(), zone.FullName);
            }

            return(list);
        }
        public async Task SetZoneType(string zoneName, string zoneType)
        {
            Zone zone = await ZoneUtils.GetZoneAsync(zoneName);

            ZoneType type = await ZoneUtils.GetZoneTypeAsync(zoneType);

            if (await BotUtils.ReplyValidateZoneAsync(Context, zone) && await BotUtils.ReplyValidateZoneTypeAsync(Context, type))
            {
                zone.ZoneTypeId = type.Id;

                await ZoneUtils.UpdateZoneAsync(zone);

                await BotUtils.ReplyAsync_Success(Context, string.Format("Successfully set the type of {0}**{1}** to **{2}**.",
                                                                         zone.FullName.StartsWith("Zone") ? string.Empty : "zone ",
                                                                         zone.FullName,
                                                                         type.Name));
            }
        }
        public async Task GetZoneType(string arg0)
        {
            // If the given argument is a zone type, display information for that type.
            // If the given argument is a zone name, display information for the type corresponding to that zone.

            ZoneType type = await ZoneUtils.GetZoneTypeAsync(arg0);

            if (!ZoneUtils.ZoneTypeIsValid(type))
            {
                // If no zone type exists with this name, attempt to get the type of the zone with this name.

                Zone zone = await ZoneUtils.GetZoneAsync(arg0);

                if (zone != null)
                {
                    type = await ZoneUtils.GetZoneTypeAsync(zone.ZoneTypeId);
                }
            }

            if (ZoneUtils.ZoneTypeIsValid(type))
            {
                // We got a valid zone type, so show information about the zone type.

                Zone[] zones = await ZoneUtils.GetZonesAsync(type);

                Bot.PaginatedMessageBuilder embed = new Bot.PaginatedMessageBuilder {
                    Title       = string.Format("{0} {1} Zones ({2})", type.Icon, type.Name, zones.Count()),
                    Description = type.Description + "\n\n",
                    Color       = Bot.DiscordUtils.ConvertColor(type.Color)
                };

                await BotUtils.ZonesToEmbedPagesAsync(embed, zones, showIcon : false);

                embed.AddPageNumbers();

                await Bot.DiscordUtils.SendMessageAsync(Context, embed.Build());
            }
            else
            {
                await BotUtils.ReplyAsync_Error(Context, "No such zone type exists.");
            }
        }
        // Checks for roles that are unfulfilled for a given zone
        private async Task <string[]> _getMissingRolesInZoneIdeasAsync()
        {
            List <string> ideas = new List <string>();

            string query = @"SELECT Zones.id AS zone_id1, Zones.name AS zone_name, Roles.id AS role_id1, Roles.name AS role_name FROM Zones, Roles WHERE
	            NOT EXISTS(SELECT * FROM SpeciesRoles WHERE role_id = role_id1 AND species_id IN (SELECT species_id FROM SpeciesZones WHERE zone_id = zone_id1));"    ;

            using (SQLiteCommand cmd = new SQLiteCommand(query))
                using (DataTable table = await Database.GetRowsAsync(cmd))
                    foreach (DataRow row in table.Rows)
                    {
                        string zone_name = row.Field <string>("zone_name");
                        string role_name = row.Field <string>("role_name");

                        ideas.Add(string.Format("**{0}** does not have any **{1}s**. Why not fill this role?",
                                                ZoneUtils.FormatZoneName(zone_name),
                                                StringUtils.ToTitleCase(role_name)));
                    }

            return(ideas.ToArray());
        }
        public async Task SetZoneDescription(string zoneName, string description)
        {
            // Get the zone from the database.

            Zone zone = await ZoneUtils.GetZoneAsync(zoneName);

            if (!await BotUtils.ReplyValidateZoneAsync(Context, zone))
            {
                return;
            }

            // Update the description for the zone.

            using (SQLiteCommand cmd = new SQLiteCommand("UPDATE Zones SET description=$description WHERE id=$id;")) {
                cmd.Parameters.AddWithValue("$description", description);
                cmd.Parameters.AddWithValue("$id", zone.Id);

                await Database.ExecuteNonQuery(cmd);
            }

            await BotUtils.ReplyAsync_Success(Context, string.Format("Successfully updated the description for **{0}**.", zone.GetFullName()));
        }
Beispiel #13
0
        private async Task <bool> _checkTrophy_helper_hasSpeciesWithZoneTypeMatch(TrophyScanner.ScannerQueueItem item, string[] zoneTypeNames)
        {
            if (zoneTypeNames.Count() <= 0)
            {
                return(false);
            }

            StringBuilder query_builder = new StringBuilder();

            query_builder.Append("SELECT COUNT(*) FROM Species WHERE owner=$owner");

            foreach (string zone_type_name in zoneTypeNames)
            {
                ZoneType zone_type = await ZoneUtils.GetZoneTypeAsync(zone_type_name);

                // If any of the zone types are invalid, the trophy is automatically invalidated.

                if (zone_type is null || zone_type.Id == ZoneType.NullZoneTypeId)
                {
                    return(false);
                }

                query_builder.Append(string.Format(" AND id IN (SELECT species_id FROM SpeciesZones WHERE zone_id IN (SELECT id FROM Zones WHERE type_id = {0}))", zone_type.Id));
            }

            string username = (await item.Context.Guild.GetUserAsync(item.UserId)).Username;
            bool   unlocked = false;

            using (SQLiteCommand cmd = new SQLiteCommand(query_builder.ToString())) {
                cmd.Parameters.AddWithValue("$owner", username);

                if (await Database.GetScalar <long>(cmd) > 0)
                {
                    return(true);
                }
            }

            return(unlocked);
        }
Beispiel #14
0
    protected virtual void Initialize()
    {
        if (initialized)
        {
            return;
        }

        (MinX, MaxX) = GetRange((int)Center.x, Range);
        (MinZ, MaxZ) = GetRange((int)Center.z, Range);

        // Get zones to check
        ZoneIds = ZoneUtils.GetZonesInSquare(MinX, MinZ, MaxX, MaxZ);

        // Get zdo's
        Zdos = new List <ZDO>();

        foreach (var zone in ZoneIds)
        {
            ZDOMan.instance.FindObjects(zone, Zdos);
        }

        initialized = true;
    }
        public async Task Zone(string arg0 = "")
        {
            ZoneType zone_type = await ZoneUtils.GetZoneTypeAsync(arg0);

            if (string.IsNullOrEmpty(arg0) || ZoneUtils.ZoneTypeIsValid(zone_type))
            {
                // Display all zones, or, if the user passed in a valid zone type, all zones of that type.

                Zone[] zones = await ZoneUtils.GetZonesAsync(zone_type);

                if (zones.Count() > 0)
                {
                    // We need to make sure that even if the "short" description is actually long, we can show n zones per page.

                    Bot.PaginatedMessageBuilder embed = new Bot.PaginatedMessageBuilder {
                        Title       = StringUtils.ToTitleCase(string.Format("{0} zones ({1})", string.IsNullOrEmpty(arg0) ? "All" : arg0, zones.Count())),
                        Description = string.Format("For detailed zone information, use `{0}zone <zone>` (e.g. `{0}zone {1}`).\n\n",
                                                    OurFoodChainBot.Instance.Config.Prefix,
                                                    zones[0].ShortName.Contains(" ") ? string.Format("\"{0}\"", zones[0].ShortName.ToLower()) : zones[0].ShortName.ToLower())
                    };

                    // Build paginated message.

                    await BotUtils.ZonesToEmbedPagesAsync(embed, zones);

                    embed.AddPageNumbers();

                    if (ZoneUtils.ZoneTypeIsValid(zone_type))
                    {
                        embed.SetColor(Bot.DiscordUtils.ConvertColor(zone_type.Color));
                    }

                    await Bot.DiscordUtils.SendMessageAsync(Context, embed.Build());
                }
                else
                {
                    await BotUtils.ReplyAsync_Info(Context, "No zones have been added yet.");
                }

                return;
            }
            else
            {
                Zone zone = await ZoneUtils.GetZoneAsync(arg0);

                if (await BotUtils.ReplyValidateZoneAsync(Context, zone))
                {
                    List <Embed> pages = new List <Embed>();

                    ZoneType type = await ZoneUtils.GetZoneTypeAsync(zone.ZoneTypeId) ?? new ZoneType();

                    string title       = string.Format("{0} {1}", type.Icon, zone.FullName);
                    string description = zone.GetDescriptionOrDefault();
                    Color  color       = Bot.DiscordUtils.ConvertColor(type.Color);

                    // Get all species living in this zone.

                    List <Species> species_list = new List <Species>(await BotUtils.GetSpeciesFromDbByZone(zone));

                    species_list.Sort((lhs, rhs) => lhs.ShortName.CompareTo(rhs.ShortName));

                    // Starting building a paginated message.
                    // The message will have a paginated species list, and a toggle button to display the species sorted by role.

                    List <EmbedBuilder>         embed_pages = EmbedUtils.SpeciesListToEmbedPages(species_list, fieldName: (string.Format("Extant species in this zone ({0}):", species_list.Count())));
                    Bot.PaginatedMessageBuilder paginated   = new Bot.PaginatedMessageBuilder(embed_pages);

                    if (embed_pages.Count() <= 0)
                    {
                        embed_pages.Add(new EmbedBuilder());
                    }

                    // Add title, decription, etc., to all pages.

                    paginated.SetTitle(title);
                    paginated.SetDescription(description);
                    paginated.SetThumbnailUrl(zone.Pics);
                    paginated.SetColor(color);

                    // This page will have species organized by role.
                    // Only bother with the role page if species actually exist in this zone.

                    if (species_list.Count() > 0)
                    {
                        EmbedBuilder role_page = new EmbedBuilder();

                        role_page.WithTitle(title);
                        role_page.WithDescription(description);
                        //role_page.WithThumbnailUrl(zone.pics);
                        role_page.WithColor(color);

                        Dictionary <string, List <Species> > roles_map = new Dictionary <string, List <Species> >();

                        foreach (Species sp in species_list)
                        {
                            Role[] roles_list = await SpeciesUtils.GetRolesAsync(sp);

                            if (roles_list.Count() <= 0)
                            {
                                if (!roles_map.ContainsKey("no role"))
                                {
                                    roles_map["no role"] = new List <Species>();
                                }

                                roles_map["no role"].Add(sp);

                                continue;
                            }

                            foreach (Role role in roles_list)
                            {
                                if (!roles_map.ContainsKey(role.name))
                                {
                                    roles_map[role.name] = new List <Species>();
                                }

                                roles_map[role.name].Add(sp);
                            }
                        }

                        // Sort the list of species belonging to each role.

                        foreach (List <Species> i in roles_map.Values)
                        {
                            i.Sort((lhs, rhs) => lhs.ShortName.CompareTo(rhs.ShortName));
                        }

                        // Create a sorted list of keys so that the roles are in order.

                        List <string> sorted_keys = new List <string>(roles_map.Keys);
                        sorted_keys.Sort();

                        foreach (string i in sorted_keys)
                        {
                            StringBuilder lines = new StringBuilder();

                            foreach (Species j in roles_map[i])
                            {
                                lines.AppendLine(j.ShortName);
                            }

                            role_page.AddField(string.Format("{0}s ({1})", StringUtils.ToTitleCase(i), roles_map[i].Count()), lines.ToString(), inline: true);
                        }

                        // Add the page to the builder.

                        paginated.AddReaction("🇷");
                        paginated.SetCallback(async(args) => {
                            if (args.Reaction != "🇷")
                            {
                                return;
                            }

                            args.PaginatedMessage.PaginationEnabled = !args.ReactionAdded;

                            if (args.ReactionAdded)
                            {
                                await args.DiscordMessage.ModifyAsync(msg => msg.Embed = role_page.Build());
                            }
                            else
                            {
                                await args.DiscordMessage.ModifyAsync(msg => msg.Embed = args.PaginatedMessage.Pages[args.PaginatedMessage.PageIndex]);
                            }
                        });
                    }

                    await Bot.DiscordUtils.SendMessageAsync(Context, paginated.Build());
                }
            }
        }
Beispiel #16
0
 public static Vector2i GetZoneId(this Vector3 position)
 {
     return(ZoneUtils.GetZone((int)position.x, (int)position.z));
 }
        public async Task AddZoneType(params string[] args)
        {
            if (args.Count() <= 0)
            {
                await BotUtils.ReplyAsync_Error(Context, "You must specify a name for the zone type.");
            }
            else if (args.Count() > 4)
            {
                await BotUtils.ReplyAsync_Error(Context, "Too many arguments have been provided.");
            }
            else
            {
                string name = args[0];
                string icon = ZoneType.DefaultIcon;
                System.Drawing.Color color = ZoneType.DefaultColor;
                string description         = "";

                if (await ZoneUtils.GetZoneTypeAsync(name) != null)
                {
                    // If a zone type with this name already exists, do not create a new one.
                    await BotUtils.ReplyAsync_Warning(Context, string.Format("A zone type named \"{0}\" already exists.", name));
                }
                else
                {
                    // Read the rest of the arguments.

                    for (int i = 1; i < args.Count(); ++i)
                    {
                        if (Bot.DiscordUtils.IsEmoji(args[i]))
                        {
                            icon = args[i];
                        }
                        else if (StringUtils.TryParseColor(args[i], out System.Drawing.Color result))
                        {
                            color = result;
                        }
                        else if (string.IsNullOrEmpty(description))
                        {
                            description = args[i];
                        }
                        else
                        {
                            await BotUtils.ReplyAsync_Warning(Context, string.Format("Invalid argument provided: {0}", args[i]));
                        }
                    }

                    ZoneType type = new ZoneType {
                        Name        = name,
                        Icon        = icon,
                        Description = description,
                        Color       = color
                    };

                    // Add the zone type to the database.

                    await ZoneUtils.AddZoneTypeAsync(type);

                    await BotUtils.ReplyAsync_Success(Context, string.Format("Successfully created new zone type **{0}**.", type.Name));
                }
            }
        }
        public static async Task ShowSpeciesInfoAsync(ICommandContext context, Species species)
        {
            if (await BotUtils.ReplyValidateSpeciesAsync(context, species))
            {
                EmbedBuilder  embed = new EmbedBuilder();
                StringBuilder descriptionBuilder = new StringBuilder();

                string embed_title = species.FullName;
                Color  embed_color = Color.Blue;

                CommonName[] common_names = await SpeciesUtils.GetCommonNamesAsync(species);

                if (common_names.Count() > 0)
                {
                    embed_title += string.Format(" ({0})", string.Join(", ", (object[])common_names));
                }

                // Show generation only if generations are enabled.

                if (OurFoodChainBot.Instance.Config.GenerationsEnabled)
                {
                    Generation gen = await GenerationUtils.GetGenerationByTimestampAsync(species.Timestamp);

                    embed.AddField("Gen", gen is null ? "???" : gen.Number.ToString(), inline: true);
                }

                embed.AddField("Owner", await SpeciesUtils.GetOwnerOrDefaultAsync(species, context), inline: true);

                SpeciesZone[] zone_list = await SpeciesUtils.GetZonesAsync(species);

                if (zone_list.Count() > 0)
                {
                    embed_color = Bot.DiscordUtils.ConvertColor((await ZoneUtils.GetZoneTypeAsync(zone_list
                                                                                                  .GroupBy(x => x.Zone.ZoneTypeId)
                                                                                                  .OrderBy(x => x.Count())
                                                                                                  .Last()
                                                                                                  .Key)).Color);
                }

                string zones_value = new SpeciesZoneCollection(zone_list).ToString(SpeciesZoneCollectionToStringOptions.Default, Bot.DiscordUtils.MaxFieldLength);

                embed.AddField("Zone(s)", string.IsNullOrEmpty(zones_value) ? "None" : zones_value, inline: true);

                // Check if the species is extinct.
                using (SQLiteCommand cmd = new SQLiteCommand("SELECT * FROM Extinctions WHERE species_id=$species_id;")) {
                    cmd.Parameters.AddWithValue("$species_id", species.Id);

                    DataRow row = await Database.GetRowAsync(cmd);

                    if (!(row is null))
                    {
                        embed_title = "[EXTINCT] " + embed_title;
                        embed_color = Color.Red;

                        string reason    = row.Field <string>("reason");
                        long   timestamp = (long)row.Field <decimal>("timestamp");

                        if (!string.IsNullOrEmpty(reason))
                        {
                            descriptionBuilder.AppendLine(string.Format("**Extinct ({0}):** _{1}_\n", await BotUtils.TimestampToDateStringAsync(timestamp), reason));
                        }
                    }
                }

                descriptionBuilder.Append(species.GetDescriptionOrDefault());

                embed.WithTitle(embed_title);
                embed.WithThumbnailUrl(species.Picture);
                embed.WithColor(embed_color);

                if (!string.IsNullOrEmpty(OurFoodChainBot.Instance.Config.WikiUrlFormat))
                {
                    // Discord automatically encodes certain characters in URIs, which doesn't allow us to update the config via Discord when we have "{0}" in the URL.
                    // Replace this with the proper string before attempting to call string.Format.
                    string format = OurFoodChainBot.Instance.Config.WikiUrlFormat.Replace("%7B0%7D", "{0}");

                    embed.WithUrl(string.Format(format, Uri.EscapeUriString(GetWikiPageTitleForSpecies(species, common_names))));
                }

                if (embed.Length + descriptionBuilder.Length > DiscordUtils.MaxEmbedLength)
                {
                    // If the description puts us over the character limit, we'll paginate.

                    int pageLength = DiscordUtils.MaxEmbedLength - embed.Length;

                    List <EmbedBuilder> pages = new List <EmbedBuilder>();

                    foreach (string pageText in new StringPaginator(descriptionBuilder.ToString())
                    {
                        MaxPageLength = pageLength
                    })
                    {
                        EmbedBuilder page = new EmbedBuilder();

                        page.WithTitle(embed.Title);
                        page.WithThumbnailUrl(embed.ThumbnailUrl);
                        page.WithFields(embed.Fields);
                        page.WithDescription(pageText);

                        pages.Add(page);
                    }

                    PaginatedMessageBuilder builder = new Bot.PaginatedMessageBuilder(pages);
                    builder.AddPageNumbers();
                    builder.SetColor(embed_color);

                    await DiscordUtils.SendMessageAsync(context, builder.Build());
                }
                else
                {
                    embed.WithDescription(descriptionBuilder.ToString());

                    await context.Channel.SendMessageAsync("", false, embed.Build());
                }
            }
        }
        // Checks for empty zones
        private async Task <string[]> _getEmptyZoneIdeasAsync()
        {
            List <string> ideas = new List <string>();

            using (SQLiteCommand cmd = new SQLiteCommand("SELECT * FROM Zones WHERE id NOT IN (SELECT zone_id FROM SpeciesZones);"))
                using (DataTable table = await Database.GetRowsAsync(cmd))
                    foreach (DataRow row in table.Rows)
                    {
                        ideas.Add(string.Format("**{0}** does not contain any species yet. Why not make one?", ZoneUtils.ZoneFromDataRow(row).GetFullName()));
                    }

            return(ideas.ToArray());
        }