Esempio n. 1
0
        private async Task _setTaxonDescriptionAsync(Taxon taxon)
        {
            if (taxon.type == TaxonRank.Species)
            {
                await _setSpeciesDescriptionAsync(await SpeciesUtils.GetSpeciesAsync(taxon.id));
            }

            else if (await BotUtils.ReplyHasPrivilegeAsync(Context, PrivilegeLevel.ServerModerator))   // moderator use only

            {
                Bot.MultiPartMessage p = new Bot.MultiPartMessage(Context)
                {
                    UserData = new string[] { taxon.name },
                    Callback = async(args) => {
                        await BotUtils.Command_SetTaxonDescription(args.Message.Context, taxon, args.ResponseContent);
                    }
                };

                await Bot.DiscordUtils.SendMessageAsync(Context, p,
                                                        string.Format("Reply with the description for {0} **{1}**.\nTo cancel the update, reply with \"cancel\".", taxon.GetTypeName(), taxon.GetName()));
            }
        }
        public async Task ListSpecies()
        {
            // Get all species.

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

            using (SQLiteCommand cmd = new SQLiteCommand("SELECT * FROM Species;"))
                using (DataTable table = await Database.GetRowsAsync(cmd))
                    foreach (DataRow row in table.Rows)
                    {
                        species.Add(await SpeciesUtils.SpeciesFromDataRow(row));
                    }

            // If there are no species, state so.

            if (species.Count <= 0)
            {
                await BotUtils.ReplyAsync_Info(Context, "No species have been added yet.");

                return;
            }

            // Create embed pages.

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

            List <EmbedBuilder> pages = EmbedUtils.SpeciesListToEmbedPages(species, fieldName: string.Format("All species ({0}):", species.Count()));

            // Send the result.

            Bot.PaginatedMessage reply = new Bot.PaginatedMessage();

            foreach (EmbedBuilder page in pages)
            {
                reply.Pages.Add(page.Build());
            }

            await Bot.DiscordUtils.SendMessageAsync(Context, reply);
        }
Esempio n. 3
0
        public async Task AddPrey(string genusName, string speciesName, string preyGenusName, string preySpeciesName, string notes = "")
        {
            Species[] species_list = await SpeciesUtils.GetSpeciesAsync(genusName, speciesName);

            Species[] prey_list = await SpeciesUtils.GetSpeciesAsync(preyGenusName, preySpeciesName);

            if (species_list.Count() <= 0)
            {
                await BotUtils.ReplyAsync_SpeciesSuggestions(Context, genusName, speciesName);
            }
            else if (prey_list.Count() <= 0)
            {
                await BotUtils.ReplyAsync_SpeciesSuggestions(Context, preyGenusName, preySpeciesName);
            }
            else if (!await BotUtils.ReplyValidateSpeciesAsync(Context, species_list) || !await BotUtils.ReplyValidateSpeciesAsync(Context, prey_list))
            {
                return;
            }
            else
            {
                await _addPrey(species_list[0], prey_list, notes);
            }
        }
Esempio n. 4
0
        private async Task _removePrey(Species predatorSpecies, Species preySpecies)
        {
            if (predatorSpecies is null || preySpecies is null)
            {
                return;
            }

            // Remove the relationship.

            // Ensure that the user has necessary privileges to use this command.
            if (!await BotUtils.ReplyHasPrivilegeOrOwnershipAsync(Context, PrivilegeLevel.ServerModerator, predatorSpecies))
            {
                return;
            }

            PreyInfo[] existing_prey = await SpeciesUtils.GetPreyAsync(predatorSpecies);

            if (existing_prey.Any(x => x.Prey.Id == preySpecies.Id))
            {
                using (SQLiteCommand cmd = new SQLiteCommand("DELETE FROM Predates WHERE species_id = $species_id AND eats_id = $eats_id")) {
                    cmd.Parameters.AddWithValue("$species_id", predatorSpecies.Id);
                    cmd.Parameters.AddWithValue("$eats_id", preySpecies.Id);

                    await Database.ExecuteNonQuery(cmd);
                }

                await BotUtils.ReplyAsync_Success(Context, string.Format("**{0}** no longer preys upon **{1}**.",
                                                                         predatorSpecies.ShortName,
                                                                         preySpecies.ShortName));
            }
            else
            {
                await BotUtils.ReplyAsync_Warning(Context, string.Format("**{0}** does not prey upon **{1}**.",
                                                                         predatorSpecies.ShortName,
                                                                         preySpecies.ShortName));
            }
        }
Esempio n. 5
0
        private async Task SetSpeciesCommonName(string genus, string species, string commonName)
        {
            Species species_info = await BotUtils.ReplyFindSpeciesAsync(Context, genus, species);

            if (species_info is null)
            {
                return;
            }

            // Ensure that the user has necessary privileges to use this command.
            if (!await BotUtils.ReplyHasPrivilegeOrOwnershipAsync(Context, PrivilegeLevel.ServerModerator, species_info))
            {
                return;
            }

            if (string.IsNullOrWhiteSpace(commonName))
            {
                // If the given common name is empty, erase all common names associated with this species.
                await SpeciesUtils.RemoveCommonNamesAsync(species_info);

                await BotUtils.ReplyAsync_Success(Context, string.Format("Successfully removed all common names from **{0}**.",
                                                                         species_info.ShortName));
            }
            else
            {
                // Otherwise, add the common name to the database.

                // The difference between this and the "+common" command is that this one overwrites the value stored in the "Species" table.
                // This field is pretty much deprected at this point, but it is still accessed through some generic taxon commands.

                await SpeciesUtils.AddCommonNameAsync(species_info, commonName, overwriteSpeciesTable : true);

                await BotUtils.ReplyAsync_Success(Context, string.Format("**{0}** is now commonly known as the **{1}**.",
                                                                         species_info.ShortName,
                                                                         StringUtils.ToTitleCase(commonName)));
            }
        }
        public async Task Gallery(string speciesOrTaxon)
        {
            // Prioritize species galleries first.

            Species[] species = await BotUtils.GetSpeciesFromDb("", speciesOrTaxon);

            if (species is null || species.Count() <= 0)
            {
                // No such species exists, so check if a taxon exists.

                Taxon taxon = await BotUtils.GetTaxonFromDb(speciesOrTaxon);

                if (taxon is null)
                {
                    // If no such taxon exists, show species recommendations to the user.
                    await BotUtils.ReplyAsync_SpeciesSuggestions(Context, "", speciesOrTaxon);
                }
                else
                {
                    // The taxon does exist, so we'll generate a gallery from this taxon.
                    // First, images for this taxon will be added, followed by the galleries for all species under it.

                    List <Picture> pictures = new List <Picture>();

                    if (!string.IsNullOrEmpty(taxon.pics))
                    {
                        pictures.Add(new Picture(taxon.pics));
                    }

                    foreach (Species sp in await BotUtils.GetSpeciesInTaxonFromDb(taxon))
                    {
                        pictures.AddRange(await SpeciesUtils.GetPicturesAsync(sp));
                    }

                    await ShowGalleryAsync(Context, taxon.GetName(), pictures.ToArray());
                }
            }
        public async Task SetOwner(string genusName, string speciesName, string ownerName)
        {
            Species species = await BotUtils.ReplyFindSpeciesAsync(Context, genusName, speciesName);

            if (species != null)
            {
                // If we've seen this user before, get their user ID from the database.

                UserInfo userInfo = await UserUtils.GetUserInfoAsync(ownerName);

                if (userInfo != null)
                {
                    ownerName = userInfo.Username;

                    await SpeciesUtils.SetOwnerAsync(species, userInfo.Username, userInfo.Id);
                }
                else
                {
                    await SpeciesUtils.SetOwnerAsync(species, ownerName);
                }

                await BotUtils.ReplyAsync_Success(Context, string.Format("**{0}** is now owned by **{1}**.", species.ShortName, ownerName));
            }
        }
        public async Task Roles(Species species)
        {
            // Get the role(s) assigned to this species.

            Role[] roles = await SpeciesUtils.GetRolesAsync(species);

            if (roles.Count() <= 0)
            {
                await BotUtils.ReplyAsync_Info(Context, string.Format("**{0}** has not been assigned any roles.", species.ShortName));

                return;
            }

            // Display the role(s) to the user.

            StringBuilder lines = new StringBuilder();

            foreach (Role i in roles)
            {
                lines.Append(StringUtils.ToTitleCase(i.name));

                if (!string.IsNullOrEmpty(i.notes))
                {
                    lines.Append(string.Format(" ({0})", i.notes));
                }

                lines.AppendLine();
            }

            EmbedBuilder embed = new EmbedBuilder();

            embed.WithTitle(string.Format("{0}'s role(s) ({1})", species.ShortName, roles.Count()));
            embed.WithDescription(lines.ToString());

            await ReplyAsync("", false, embed.Build());
        }
Esempio n. 9
0
        public async Task <bool> CheckAsync(Gotchi gotchi, GotchiRequirements requirements)
        {
            if (requirements is null)
            {
                return(true);
            }

            if (requirements.AlwaysFailValue)
            {
                return(false);
            }

            if (!_checkLevels(gotchi, requirements))
            {
                return(false);
            }

            if (!string.IsNullOrEmpty(requirements.RolePattern) && !await _checkRolesAsync(gotchi, requirements))
            {
                return(false);
            }

            if (!string.IsNullOrEmpty(requirements.TypePattern) && !await _checkTypesAsync(gotchi, requirements))
            {
                return(false);
            }

            Species species = await SpeciesUtils.GetSpeciesAsync(gotchi.SpeciesId);

            if (!string.IsNullOrEmpty(requirements.DescriptionPattern) && !_checkDescription(species, requirements))
            {
                return(false);
            }

            return(true);
        }
Esempio n. 10
0
        public async Task MinusExtinct(string genus, string species)
        {
            Species sp = await BotUtils.ReplyFindSpeciesAsync(Context, genus, species);

            if (sp is null)
            {
                return;
            }

            // If the species is not extinct, don't do anything.

            if (!sp.IsExtinct)
            {
                await BotUtils.ReplyAsync_Warning(Context, string.Format("**{0}** is not extinct.", sp.ShortName));

                return;
            }

            // Delete the extinction from the database.

            await SpeciesUtils.SetExtinctionInfoAsync(sp, new ExtinctionInfo { IsExtinct = false });

            await BotUtils.ReplyAsync_Success(Context, string.Format("A population of **{0}** has been discovered! The species is no longer considered extinct.", sp.ShortName));
        }
        public static async Task <BattleGotchi> GenerateGotchiAsync(GotchiGenerationParameters parameters)
        {
            BattleGotchi result = new BattleGotchi();

            if (!(parameters.Base is null))
            {
                // If a base gotchi was provided, copy over some of its characteristics.

                result.Gotchi.BornTimestamp    = parameters.Base.BornTimestamp;
                result.Gotchi.DiedTimestamp    = parameters.Base.DiedTimestamp;
                result.Gotchi.EvolvedTimestamp = parameters.Base.EvolvedTimestamp;
                result.Gotchi.FedTimestamp     = parameters.Base.FedTimestamp;
            }

            // Select a random base species to start with.

            Species species = parameters.Species;

            if (species is null)
            {
                Species[] base_species_list = await SpeciesUtils.GetBaseSpeciesAsync();


                if (base_species_list.Count() > 0)
                {
                    species = base_species_list.ElementAt(BotUtils.RandomInteger(0, base_species_list.Count()));
                }
            }

            if (!(species is null))
            {
                result.Gotchi.SpeciesId = species.Id;
            }

            // Evolve it the given number of times.

            for (int i = 0; i < parameters.MaxEvolutions; ++i)
            {
                if (!await EvolveAndUpdateGotchiAsync(result.Gotchi))
                {
                    break;
                }
            }

            // Generate stats (if applicable).

            if (parameters.GenerateStats)
            {
                result.Gotchi.Experience = GotchiExperienceCalculator.ExperienceToLevel(result.Stats.ExperienceGroup, BotUtils.RandomInteger(parameters.MinLevel, parameters.MaxLevel + 1));

                result.Stats = await new GotchiStatsCalculator(Global.GotchiContext).GetStatsAsync(result.Gotchi);
            }

            // Generate moveset (if applicable).

            if (parameters.GenerateMoveset)
            {
                result.Moves = await Global.GotchiContext.MoveRegistry.GetMoveSetAsync(result.Gotchi);
            }

            // Generate types.
            result.Types = await Global.GotchiContext.TypeRegistry.GetTypesAsync(result.Gotchi);

            // Generate a name for the gotchi.

            result.Gotchi.Name = (species is null ? "Wild Gotchi" : species.ShortName) + string.Format(" (Lv. {0})", result.Stats is null ? 1 : result.Stats.Level);

            return(result);
        }
Esempio n. 12
0
        public async Task Search([Remainder] string queryString)
        {
            // Create and execute the search query.

            Taxa.SearchQuery       query  = new Taxa.SearchQuery(Context, queryString);
            Taxa.SearchQueryResult result = await query.GetResultAsync();

            // Build the embed.

            if (result.Count() <= 0)
            {
                await BotUtils.ReplyAsync_Info(Context, "No species matching this query could be found.");
            }
            else
            {
                if (result.DisplayFormat == Taxa.DisplayFormat.Gallery)
                {
                    List <Picture> pictures = new List <Picture>();

                    foreach (Species species in result.ToArray())
                    {
                        pictures.AddRange(await SpeciesUtils.GetPicturesAsync(species));
                    }

                    await GalleryCommands.ShowGalleryAsync(Context, string.Format("search results ({0})", result.Count()), pictures.ToArray());
                }
                else if (result.DisplayFormat == Taxa.DisplayFormat.Leaderboard)
                {
                    // Match each group to a rank depending on how many results it contains.

                    Dictionary <Taxa.SearchQueryResult.Group, long> groupRanks = new Dictionary <Taxa.SearchQueryResult.Group, long>();

                    long rank      = 0;
                    int  lastCount = -1;

                    foreach (Taxa.SearchQueryResult.Group group in result.Groups.OrderByDescending(x => x.Count()))
                    {
                        groupRanks[group] = (lastCount >= 0 && group.Count() == lastCount) ? rank : ++rank;

                        lastCount = group.Count();
                    }

                    // Create a list of groups that will be displayed to the user.

                    List <string> lines = new List <string>();

                    foreach (Taxa.SearchQueryResult.Group group in result.Groups)
                    {
                        lines.Add(string.Format("**`{0}.`**{1}`{2}` {3}",
                                                groupRanks[group].ToString("000"),
                                                UserRank.GetRankIcon(groupRanks[group]),
                                                group.Count().ToString("000"),
                                                string.Format(groupRanks[group] <= 3 ? "**{0}**" : "{0}", string.IsNullOrEmpty(group.Name) ? "Results" : StringUtils.ToTitleCase(group.Name))
                                                ));
                    }

                    Bot.PaginatedMessageBuilder embed = new Bot.PaginatedMessageBuilder {
                        Title = string.Format("Search results ({0})", result.Groups.Count())
                    };

                    embed.AddPages(EmbedUtils.LinesToEmbedPages(lines));
                    embed.AddPageNumbers();

                    await Bot.DiscordUtils.SendMessageAsync(Context, embed.Build());
                }
                else
                {
                    if (result.Count() == 1)
                    {
                        // If there's only one result, just show that species.
                        await SpeciesCommands.ShowSpeciesInfoAsync(Context, result.ToArray()[0]);
                    }
                    else
                    {
                        Bot.PaginatedMessageBuilder embed;

                        if (result.HasGroup(Taxa.SearchQuery.DefaultGroupName))
                        {
                            // If there's only one group, just list the species without creating separate fields.
                            embed = new Bot.PaginatedMessageBuilder(EmbedUtils.ListToEmbedPages(result.DefaultGroup.ToStringArray().ToList(), fieldName: string.Format("Search results ({0})", result.Count())));
                        }
                        else
                        {
                            embed = new Bot.PaginatedMessageBuilder();
                            embed.AddPages(EmbedUtils.SearchQueryResultToEmbedPages(result));
                        }

                        embed.SetFooter("");
                        embed.AddPageNumbers();

                        await Bot.DiscordUtils.SendMessageAsync(Context, embed.Build());
                    }
                }
            }
        }
Esempio n. 13
0
        public async Task Profile(IUser user)
        {
            // Begin building the embed (add default parameters).

            EmbedBuilder embed = new EmbedBuilder();

            embed.WithTitle(string.Format("{0}'s profile", user.Username));
            embed.WithThumbnailUrl(user.GetAvatarUrl(size: 64));

            // Get basic information about the user.
            // This will return null if the user hasn't been seen before.

            UserInfo userInfo = await UserUtils.GetUserInfoAsync(user.Username, user.Id, UserInfoQueryFlags.MatchEither);

            if (userInfo is null)
            {
                embed.WithDescription(string.Format("{0} has not submitted any species.", user.Username));
            }
            else
            {
                long     daysSinceFirstSubmission = (DateUtils.GetCurrentTimestamp() - userInfo.FirstSubmissionTimestamp) / 60 / 60 / 24;
                UserRank userRank = await UserUtils.GetRankAsync(userInfo, UserInfoQueryFlags.MatchEither);

                // Get the user's most active genus.

                Species[] userSpecies = await UserUtils.GetSpeciesAsync(userInfo, UserInfoQueryFlags.MatchEither);

                IGrouping <string, string> favoriteGenusGrouping = userSpecies
                                                                   .Select(x => x.GenusName)
                                                                   .GroupBy(x => x)
                                                                   .OrderByDescending(x => x.Count())
                                                                   .FirstOrDefault();

                string favoriteGenus      = favoriteGenusGrouping is null ? "N/A" : favoriteGenusGrouping.First();
                int    favoriteGenusCount = favoriteGenusGrouping is null ? 0 : favoriteGenusGrouping.Count();

                int userSpeciesCount = userSpecies.Count();
                int speciesCount     = await SpeciesUtils.GetSpeciesCount();

                // Get the user's rarest trophy.

                string rarest_trophy = "N/A";

                Trophies.UnlockedTrophyInfo[] unlocked = await Global.TrophyRegistry.GetUnlockedTrophiesAsync(user.Id);

                if (unlocked.Count() > 0)
                {
                    Array.Sort(unlocked, (lhs, rhs) => lhs.timesUnlocked.CompareTo(rhs.timesUnlocked));

                    Trophies.Trophy trophy = await Global.TrophyRegistry.GetTrophyByIdentifierAsync(unlocked[0].identifier);

                    rarest_trophy = trophy.GetName();
                }

                // Put together the user's profile.

                if (OurFoodChainBot.Instance.Config.GenerationsEnabled)
                {
                    int    generationsSinceFirstSubmission = (await GenerationUtils.GetGenerationsAsync()).Where(x => x.EndTimestamp > userInfo.FirstSubmissionTimestamp).Count();
                    double speciesPerGeneration            = generationsSinceFirstSubmission <= 0 ? userSpeciesCount : (double)userSpeciesCount / generationsSinceFirstSubmission;

                    embed.WithDescription(string.Format("{0} made their first species during **{1}**.\nSince then, they have submitted **{2:0.0}** species per generation.\n\nTheir submissions make up **{3:0.0}%** of all species.",
                                                        user.Username,
                                                        await BotUtils.TimestampToDateStringAsync(userInfo.FirstSubmissionTimestamp),
                                                        speciesPerGeneration,
                                                        (double)userSpeciesCount / speciesCount * 100.0));
                }
                else
                {
                    embed.WithDescription(string.Format("{0} made their first species on **{1}**.\nSince then, they have submitted **{2:0.0}** species per day.\n\nTheir submissions make up **{3:0.0}%** of all species.",
                                                        user.Username,
                                                        await BotUtils.TimestampToDateStringAsync(userInfo.FirstSubmissionTimestamp),
                                                        daysSinceFirstSubmission == 0 ? userSpeciesCount : (double)userSpeciesCount / daysSinceFirstSubmission,
                                                        (double)userSpeciesCount / speciesCount * 100.0));
                }

                embed.AddField("Species", string.Format("{0} (Rank **#{1}**)", userSpeciesCount, userRank.Rank), inline: true);

                embed.AddField("Favorite genus", string.Format("{0} ({1} spp.)", StringUtils.ToTitleCase(favoriteGenus), favoriteGenusCount), inline: true);

                if (OurFoodChainBot.Instance.Config.TrophiesEnabled)
                {
                    embed.AddField("Trophies", string.Format("{0} ({1:0.0}%)",
                                                             (await Global.TrophyRegistry.GetUnlockedTrophiesAsync(user.Id)).Count(),
                                                             await Global.TrophyRegistry.GetUserCompletionRateAsync(user.Id)), inline: true);

                    embed.AddField("Rarest trophy", rarest_trophy, inline: true);
                }
            }

            await ReplyAsync("", false, embed.Build());
        }
Esempio n. 14
0
        private async Task _useMoveOnAsync(ICommandContext context, PlayerState user, PlayerState target)
        {
            // Execute the selected move.

            StringBuilder battle_text = new StringBuilder();

            battle_text.AppendLine(battleText);

            if (!string.IsNullOrEmpty(user.SelectedMove.LuaScriptFilePath))
            {
                // Create, initialize, and execute the script associated with this move.

                GotchiMoveLuaScript moveScript = new GotchiMoveLuaScript(user.SelectedMove.LuaScriptFilePath);

                GotchiMoveCallbackArgs args = await _createCallbackArgsAsync(user, target);

                // Call the move's "OnInit" callback (only applicable for some moves).
                await moveScript.OnInitAsync(args);

                // It's possible for a move to be used more than once in a turn (e.g., multi-hit moves).
                // Each time will trigger the callback to be called and display a new message.

                for (int i = 0; i < Math.Max(1, args.Times); ++i)
                {
                    // Check if this was a critical hit, or if the move missed.

                    bool is_hit = user.SelectedMove.IgnoreAccuracy || (BotUtils.RandomInteger(0, 20 + 1) < 20 * user.SelectedMove.Accuracy * Math.Max(0.1, user.Gotchi.Stats.Acc - target.Gotchi.Stats.Eva));
                    args.IsCritical =
                        !user.SelectedMove.IgnoreCritical &&
                        (BotUtils.RandomInteger(0, (int)(10 / user.SelectedMove.CriticalRate)) == 0 ||
                         (await SpeciesUtils.GetPreyAsync(user.Gotchi.Gotchi.SpeciesId)).Any(x => x.Prey.Id == target.Gotchi.Gotchi.Id));

                    if (is_hit)
                    {
                        // Clone each user's stats before triggering the callback, so we can compare them before and after.

                        GotchiStats user_before   = user.Gotchi.Stats.Clone();
                        GotchiStats target_before = target.Gotchi.Stats.Clone();

                        // Invoke the callback.

                        try {
                            if (!await moveScript.OnMoveAsync(args))
                            {
                                args.DealDamage(user.SelectedMove.Power);
                            }
                        }
                        catch (Exception) {
                            args.Text = "but something went wrong";
                        }

                        // Apply the target's status if a new status was acquired.

                        if (target.Gotchi.StatusChanged && target.Gotchi.HasStatus)
                        {
                            await new GotchiStatusLuaScript(target.Gotchi.Status.LuaScriptFilePath).OnAcquireAsync(args);

                            target.Gotchi.StatusChanged = false;
                        }

                        // Prevent the target from fainting if they're able to endure the hit.

                        if (target.Gotchi.HasStatus && target.Gotchi.Status.Endure)
                        {
                            target.Gotchi.Stats.Hp = Math.Max(1, target.Gotchi.Stats.Hp);
                        }

                        // If the user is heal-blocked, prevent recovery by resetting the HP back to what it was before recovery.

                        if (user.Gotchi.HasStatus && !user.Gotchi.Status.AllowRecovery && user.Gotchi.Stats.Hp > user_before.Hp)
                        {
                            user.Gotchi.Stats.Hp = user_before.Hp;
                        }

                        // Show the battle text.
                        // If the move doesn't specify a text, choose one automatically (where possible).

                        string text = args.Text;

                        if (string.IsNullOrEmpty(text))
                        {
                            if (target.Gotchi.Stats.Hp < target_before.Hp)
                            {
                                text = "dealing {target:damage} damage";
                                //user.SelectedMove.info.Type = GotchiMoveType.Offensive;
                            }

                            else if (target.Gotchi.Stats.Atk < target_before.Atk)
                            {
                                text = "lowering its opponent's ATK by {target:atk%}";
                                //user.SelectedMove.info.Type = GotchiMoveType.Buff;
                            }
                            else if (target.Gotchi.Stats.Def < target_before.Def)
                            {
                                text = "lowering its opponent's DEF by {target:def%}";
                                //user.SelectedMove.info.Type = GotchiMoveType.Buff;
                            }
                            else if (target.Gotchi.Stats.Spd < target_before.Spd)
                            {
                                text = "lowering its opponent's SPD by {target:spd%}";
                                //user.SelectedMove.info.Type = GotchiMoveType.Buff;
                            }
                            else if (target.Gotchi.Stats.Acc < target_before.Acc)
                            {
                                text = "lowering its opponent's accuracy by {target:acc%}";
                                //user.SelectedMove.info.Type = GotchiMoveType.Buff;
                            }
                            else if (target.Gotchi.Stats.Eva < target_before.Eva)
                            {
                                text = "lowering its opponent's evasion by {target:eva%}";
                                //user.SelectedMove.info.Type = GotchiMoveType.Buff;
                            }

                            else if (user.Gotchi.Stats.Hp > user_before.Hp)
                            {
                                text = "recovering {user:recovered} HP";
                                //user.SelectedMove.info.Type = GotchiMoveType.Recovery;
                            }
                            else if (user.Gotchi.Stats.Atk > user_before.Atk)
                            {
                                text = "boosting its ATK by {user:atk%}";
                                //user.SelectedMove.info.Type = GotchiMoveType.Buff;
                            }
                            else if (user.Gotchi.Stats.Def > user_before.Def)
                            {
                                text = "boosting its DEF by {user:def%}";
                                //user.SelectedMove.info.Type = GotchiMoveType.Buff;
                            }
                            else if (user.Gotchi.Stats.Spd > user_before.Spd)
                            {
                                text = "boosting its SPD by {user:spd%}";
                                //user.SelectedMove.info.Type = GotchiMoveType.Buff;
                            }
                            else if (user.Gotchi.Stats.Acc > user_before.Acc)
                            {
                                text = "boosting its accuracy by {user:acc%}";
                                //user.SelectedMove.info.Type = GotchiMoveType.Buff;
                            }
                            else if (user.Gotchi.Stats.Eva > user_before.Eva)
                            {
                                text = "boosting its evasion by {user:eva%}";
                                //user.SelectedMove.info.Type = GotchiMoveType.Buff;
                            }

                            else
                            {
                                text = "but nothing happened?";
                            }
                        }

                        // Various replacements are allowed, which the user can specify in the move's battle text.

                        text = Regex.Replace(text, @"\{([^\}]+)\}", m => {
                            switch (m.Groups[1].Value.ToLower())
                            {
                            case "damage":
                            case "target:damage":
                                return(string.Format("{0:0.#}", target_before.Hp - target.Gotchi.Stats.Hp));

                            case "target:atk%":
                                return(string.Format("{0:0.#}%", Math.Abs(target_before.Atk - target.Gotchi.Stats.Atk) / (double)target_before.Atk * 100.0));

                            case "target:def%":
                                return(string.Format("{0:0.#}%", Math.Abs(target_before.Def - target.Gotchi.Stats.Def) / (double)target_before.Def * 100.0));

                            case "target:spd%":
                                return(string.Format("{0:0.#}%", Math.Abs(target_before.Spd - target.Gotchi.Stats.Spd) / (double)target_before.Spd * 100.0));

                            case "target:acc%":
                                return(string.Format("{0:0.#}%", Math.Abs(target_before.Acc - target.Gotchi.Stats.Acc) / target_before.Acc * 100.0));

                            case "target:eva%":
                                return(string.Format("{0:0.#}%", Math.Abs(target_before.Eva - target.Gotchi.Stats.Eva) / target_before.Eva * 100.0));

                            case "user:atk%":
                                return(string.Format("{0:0.#}%", Math.Abs(user_before.Atk - user.Gotchi.Stats.Atk) / (double)user_before.Atk * 100.0));

                            case "user:def%":
                                return(string.Format("{0:0.#}%", Math.Abs(user_before.Def - user.Gotchi.Stats.Def) / (double)user_before.Def * 100.0));

                            case "user:spd%":
                                return(string.Format("{0:0.#}%", Math.Abs(user_before.Spd - user.Gotchi.Stats.Spd) / (double)user_before.Spd * 100.0));

                            case "user:acc%":
                                return(string.Format("{0:0.#}%", Math.Abs(user_before.Acc - user.Gotchi.Stats.Acc) / user_before.Acc * 100.0));

                            case "user:eva%":
                                return(string.Format("{0:0.#}%", (user_before.Eva == 0.0 ? user.Gotchi.Stats.Eva : (Math.Abs(user_before.Eva - user.Gotchi.Stats.Eva) / user_before.Eva)) * 100.0));

                            case "user:recovered":
                                return(string.Format("{0:0.#}", user.Gotchi.Stats.Hp - user_before.Hp));

                            default:
                                return("???");
                            }
                        });

                        battle_text.Append(string.Format("{0} **{1}** used **{2}**, {3}!",
                                                         "💥", //user.SelectedMove.info.Icon(),
                                                         user.Gotchi.Gotchi.Name,
                                                         user.SelectedMove.Name,
                                                         text));

                        if (args.IsCritical && target.Gotchi.Stats.Hp < target_before.Hp)
                        {
                            battle_text.Append(" Critical hit!");
                        }

                        if (args.MatchupMultiplier > 1.0)
                        {
                            battle_text.Append(" It's super effective!");
                        }
                        else if (args.MatchupMultiplier < 1.0)
                        {
                            battle_text.Append(" It's not very effective...");
                        }

                        battle_text.AppendLine();
                    }
                    else
                    {
                        // If the move missed, so display a failure message.
                        battle_text.AppendLine(string.Format("{0} **{1}** used **{2}**, but it missed!",
                                                             "💥", //user.SelectedMove.info.Icon(),
                                                             user.Gotchi.Gotchi.Name,
                                                             user.SelectedMove.Name));
                    }
                }

                if (args.Times > 1)
                {
                    battle_text.Append(string.Format(" Hit {0} times!", args.Times));
                }
            }
            else
            {
                // If there is no Lua script associated with the given move, display a failure message.
                battle_text.Append(string.Format("{0} **{1}** used **{2}**, but it forgot how!",
                                                 "💥", //user.SelectedMove.info.Icon(),
                                                 user.Gotchi.Gotchi.Name,
                                                 user.SelectedMove.Name));
            }

            battleText = battle_text.ToString();
        }
Esempio n. 15
0
        public async Task Migration(string genusName, string speciesName)
        {
            Species species = await BotUtils.ReplyFindSpeciesAsync(Context, genusName, speciesName);

            if (species is null)
            {
                return;
            }

            SpeciesZone[] zones = (await SpeciesUtils.GetZonesAsync(species)).OrderBy(x => x.Timestamp).ToArray();

            if (zones.Count() <= 0)
            {
                await BotUtils.ReplyAsync_Info(Context, string.Format("**{0}** is not present in any zones.", species.ShortName));
            }
            else
            {
                // Group zones changes that happened closely together (12 hours).

                List <List <SpeciesZone> > zone_groups = new List <List <SpeciesZone> >();

                long last_timestamp = zones.Count() > 0 ? zones.First().Timestamp : 0;

                foreach (SpeciesZone zone in zones)
                {
                    if (zone_groups.Count() <= 0)
                    {
                        zone_groups.Add(new List <SpeciesZone>());
                    }

                    if (zone_groups.Last().Count() <= 0 || Math.Abs(zone_groups.Last().Last().Timestamp - zone.Timestamp) < 60 * 60 * 12)
                    {
                        zone_groups.Last().Add(zone);
                    }
                    else
                    {
                        last_timestamp = zone.Timestamp;
                        zone_groups.Add(new List <SpeciesZone> {
                            zone
                        });
                    }
                }


                StringBuilder result = new StringBuilder();

                for (int i = 0; i < zone_groups.Count(); ++i)
                {
                    if (zone_groups[i].Count() <= 0)
                    {
                        continue;
                    }

                    long ts = i == 0 ? species.Timestamp : zone_groups[i].First().Timestamp;

                    if (ts <= 0)
                    {
                        ts = species.Timestamp;
                    }

                    result.Append(string.Format("{0} - ", await BotUtils.TimestampToDateStringAsync(ts, TimestampToDateStringFormat.Short)));
                    result.Append(i == 0 ? "Started in " : "Spread to ");
                    result.Append(zone_groups[i].Count() == 1 ? "Zone " : "Zones ");
                    result.Append(StringUtils.ConjunctiveJoin(", ", zone_groups[i].Select(x => x.Zone.ShortName)));

                    result.AppendLine();
                }


                await ReplyAsync(string.Format("```{0}```", result.ToString()));
            }
        }
        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());
                }
            }
        }
Esempio n. 17
0
        public static async Task <Bot.PaginatedMessageBuilder> BuildRecentEventsEmbedAsync(long startTimestamp, long endTimestamp, TimeUnits timeUnit = 0)
        {
            // Get all species created within the given timespan.

            List <Species> new_species = new List <Species>();
            TimeAmount     time_amount = new TimeAmount(endTimestamp - startTimestamp, TimeUnits.Seconds);

            if (timeUnit != 0)
            {
                time_amount = time_amount.ConvertTo(timeUnit);
            }
            else
            {
                time_amount = time_amount.Reduce();
            }

            using (SQLiteCommand cmd = new SQLiteCommand("SELECT * FROM Species WHERE timestamp >= $start_ts AND timestamp < $end_ts")) {
                cmd.Parameters.AddWithValue("$start_ts", startTimestamp);
                cmd.Parameters.AddWithValue("$end_ts", endTimestamp);

                using (DataTable table = await Database.GetRowsAsync(cmd))
                    foreach (DataRow row in table.Rows)
                    {
                        new_species.Add(await SpeciesUtils.SpeciesFromDataRow(row));
                    }
            }

            new_species.Sort();

            // Get all extinctions that occurred recently.

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

            using (SQLiteCommand cmd = new SQLiteCommand("SELECT * FROM Extinctions WHERE timestamp >= $start_ts AND timestamp < $end_ts")) {
                cmd.Parameters.AddWithValue("$start_ts", startTimestamp);
                cmd.Parameters.AddWithValue("$end_ts", endTimestamp);

                using (DataTable table = await Database.GetRowsAsync(cmd))
                    foreach (DataRow row in table.Rows)
                    {
                        extinct_species.Add(await BotUtils.GetSpeciesFromDb(row.Field <long>("species_id")));
                    }
            }

            extinct_species.Sort();

            // Build embed.

            Bot.PaginatedMessageBuilder embed = new Bot.PaginatedMessageBuilder();
            List <EmbedBuilder>         pages = new List <EmbedBuilder>();
            List <string> field_lines         = new List <string>();

            if (new_species.Count() > 0)
            {
                foreach (Species sp in new_species)
                {
                    field_lines.Add(sp.FullName);
                }

                EmbedUtils.AddLongFieldToEmbedPages(pages, field_lines, fieldName: string.Format("New species ({0})", new_species.Count()));

                field_lines.Clear();
            }

            if (extinct_species.Count() > 0)
            {
                foreach (Species sp in extinct_species)
                {
                    field_lines.Add(sp.FullName);
                }

                EmbedUtils.AddLongFieldToEmbedPages(pages, field_lines, fieldName: string.Format("Extinctions ({0})", extinct_species.Count()));

                field_lines.Clear();
            }

            embed.AddPages(pages);

            embed.SetTitle(string.Format("Recent events ({0})", time_amount.ToString()));
            embed.SetFooter(string.Empty); // remove page numbers added automatically
            embed.AddPageNumbers();

            if (embed.FieldCount <= 0)
            {
                embed.SetDescription("No events");
            }

            return(embed);
        }
 private async Task <string> GetCommonNamesTokenValueAsync()
 {
     return(await Task.FromResult(string.Join(", ", SpeciesUtils.GetCommonNamesAsync(Species).Result.Select(x => x.Value))));
 }
Esempio n. 19
0
        public async Task AddPrey(string arg0, string arg1, string arg2)
        {
            // We have the following possibilities, which we will check for in-order:
            // <genusName> <speciesName> <preySpeciesName>
            // <speciesName> <preyGenusName> <preySpeciesName>
            // <speciesName> <preySpecieName> <Notes>

            // If the user provided a prey list, it's easier to determine what they meant-- Check for that first.

            if (_isSpeciesList(arg1))
            {
                // <speciesName> <preyList> <notes>
                await _addPrey(string.Empty, arg0, _splitSpeciesList(arg1), arg2);
            }
            else
            {
                Species species = null, preySpecies = null;
                string  notes = string.Empty;

                // <genusName> <speciesName> <preyList>

                species = await SpeciesUtils.GetUniqueSpeciesAsync(arg0, arg1);

                if (species != null && _isSpeciesList(arg2))
                {
                    await _addPrey(arg0, arg1, _splitSpeciesList(arg2), string.Empty);
                }
                else
                {
                    // <genusName> <speciesName> <preySpeciesName>

                    preySpecies = species is null ? null : await SpeciesUtils.GetUniqueSpeciesAsync(arg2);

                    notes = string.Empty;

                    if (species is null || preySpecies is null)
                    {
                        // <speciesName> <preyGenusName> <preySpeciesName>

                        species = await SpeciesUtils.GetUniqueSpeciesAsync(arg0);

                        preySpecies = species is null ? null : await SpeciesUtils.GetUniqueSpeciesAsync(arg1, arg2);

                        notes = string.Empty;
                    }

                    if (species is null || preySpecies is null)
                    {
                        // <speciesName> <preySpeciesName> <Notes>

                        species = await SpeciesUtils.GetUniqueSpeciesAsync(arg0);

                        preySpecies = species is null ? null : await SpeciesUtils.GetUniqueSpeciesAsync(arg1);

                        notes = arg2;
                    }

                    if (species is null)
                    {
                        await BotUtils.ReplyAsync_Error(Context, "The given species does not exist.");
                    }
                    else if (preySpecies is null)
                    {
                        await BotUtils.ReplyAsync_Error(Context, "The given prey species does not exist.");
                    }
                    else
                    {
                        await _addPrey(species, new Species[] { preySpecies }, notes);
                    }
                }
            }
        }
        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());
                }
            }
        }
Esempio n. 21
0
        public async Task MainAsync(string[] args)
        {
            _log("loading configuration");

            Config config = JsonConvert.DeserializeObject <Config>(System.IO.File.ReadAllText("wikibot-config.json"));

            _log("initializing mediawiki client");

            MediaWikiClient client = new MediaWikiClient {
                Protocol  = config.Protocol,
                Server    = config.Server,
                ApiPath   = config.ApiPath,
                UserAgent = config.UserAgent
            };

            client.Log += _log;

            EditHistory history = new EditHistory();

            if (client.Login(config.Username, config.Password).Success)
            {
                _log("generating link dictionary");

                WikiLinkList LinkifyList = await _generateLinkifyListAsync();

                _log("synchronizing species");
                _log("getting species from database");

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

                _log(string.Format("got {0} results", speciesList.Count()));

                foreach (Species species in speciesList)
                {
                    _log(string.Format("synchronizing species {0}", species.ShortName));

                    // Create the page builder.

                    SpeciesPageBuilder pageBuilder = new SpeciesPageBuilder(species, WikiPageTemplate.Open(SpeciesTemplateFilePath))
                    {
                        AllSpecies = speciesList,
                        LinkList   = LinkifyList
                    };

                    // Attempt to upload the species' picture.

                    pageBuilder.PictureFilenames.Add(await UploadSpeciesPictureAsync(client, history, species));
                    pageBuilder.PictureFilenames.AddRange(await UploadSpeciesGalleryAsync(client, history, species));
                    pageBuilder.PictureFilenames.RemoveAll(x => string.IsNullOrWhiteSpace(x));

                    // Generate page content.

                    WikiPage wikiPage = await pageBuilder.BuildAsync();

                    string pageTitle      = wikiPage.Title;
                    bool   createRedirect = pageTitle != species.FullName;

                    // Upload page content.

                    await _editSpeciesPageAsync(client, history, species, pageTitle, wikiPage.Body);

                    // Attempt to create the redirect page for the species (if applicable).

                    if (createRedirect)
                    {
                        string redirect_page_title = species.FullName;

                        if (await _editPageAsync(client, history, redirect_page_title, string.Format("#REDIRECT [[{0}]]", pageTitle) + "\n" + BotFlag))
                        {
                            await history.AddRedirectRecordAsync(redirect_page_title, pageTitle);
                        }
                    }

                    _log(string.Format("finished synchronizing species {0}", species.ShortName));
                }
            }
            else
            {
                _log("mediawiki login failed");
            }

            _log("synchronizing complete");

            await Task.Delay(-1);
        }
        // Checks for species with empty lineage
        private async Task <string[]> _getEmptyLineageIdeasAsync()
        {
            List <string> ideas = new List <string>();

            using (SQLiteCommand cmd = new SQLiteCommand("SELECT * FROM Species WHERE id NOT IN (SELECT species_id FROM Ancestors) AND id NOT IN (SELECT ancestor_id FROM Ancestors) AND id NOT IN (SELECT species_id FROM Extinctions);"))
                using (DataTable table = await Database.GetRowsAsync(cmd))
                    foreach (DataRow row in table.Rows)
                    {
                        ideas.Add(string.Format("Species **{0}** does not have any descendants. Why not derive one?", (await SpeciesUtils.SpeciesFromDataRow(row)).ShortName));
                    }

            return(ideas.ToArray());
        }
        public async Task <GotchiStats> GetBaseStatsAsync(Gotchi gotchi)
        {
            GotchiStats result = new GotchiStats();

            int denominator = 0;

            GotchiType[] gotchiTypes = await Context.TypeRegistry.GetTypesAsync(gotchi);

            if (gotchiTypes.Count() > 0)
            {
                // Include the average of all types of this species.

                result.MaxHp = gotchiTypes.Sum(x => x.BaseHp);
                result.Atk   = gotchiTypes.Sum(x => x.BaseAtk);
                result.Def   = gotchiTypes.Sum(x => x.BaseDef);
                result.Spd   = gotchiTypes.Sum(x => x.BaseSpd);

                denominator += gotchiTypes.Count();
            }

            long[] ancestor_ids = await SpeciesUtils.GetAncestorIdsAsync(gotchi.SpeciesId);

            // Factor in the base stats of this species' ancestor (which will, in turn, factor in all other ancestors).

            if (ancestor_ids.Count() > 0)
            {
                GotchiStats ancestor_stats = await GetBaseStatsAsync(new Gotchi { SpeciesId = ancestor_ids.Last() });

                result.MaxHp += ancestor_stats.MaxHp;
                result.Atk   += ancestor_stats.Atk;
                result.Def   += ancestor_stats.Def;
                result.Spd   += ancestor_stats.Spd;

                denominator += 1;
            }

            // Add 20 points if this species has an ancestor (this effect will be compounded by the previous loop).

            if (ancestor_ids.Count() > 0)
            {
                result.MaxHp += 20;
                result.Atk   += 20;
                result.Def   += 20;
                result.Spd   += 20;
            }

            // Get the average of each base stat.

            denominator = Math.Max(denominator, 1);

            result.MaxHp /= denominator;
            result.Atk   /= denominator;
            result.Def   /= denominator;
            result.Spd   /= denominator;

            // Add 0.5 points for every week the gotchi has been alive.

            int age_bonus = (int)(0.5 * (gotchi.Age / 7));

            result.MaxHp += age_bonus;
            result.Atk   += age_bonus;
            result.Def   += age_bonus;
            result.Spd   += age_bonus;

            // Add or remove stats based on the species' description.
            await _calculateDescriptionBasedBaseStats(gotchi, result);

            return(result);
        }
 private async Task <string> GetRolesTokenValueAsync()
 {
     return(string.Join(", ", (await SpeciesUtils.GetRolesAsync(Species)).Select(x => x.Name)));
 }