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); }
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); } }
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)); } }
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()); }
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); }
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); }
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()); } } } }
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()); }
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(); }
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()); } } }
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)))); }
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()); } } }
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))); }