public int CalculateDamage(int power) { double damage = (((2.0 * User.Stats.Level / 5.0) + 2.0) * power * User.Stats.Atk / Target.Stats.Def / 50.0) + 2.0; // Critical hits add 1.5x damage if (IsCritical) { damage *= 1.5; } // Randomize the damage between 0.85x and 100x damage *= NumberUtilities.GetRandomInteger(85, 100) / 100.0; // Apply STAB damage *= StabMultiplier; // Apply type matchup bonus damage *= MatchupMultiplier; return((int)damage); }
private async Task _generateOpponentAsync() { // Pick a random species from the same zone as the player's gotchi. List <ISpecies> species_list = new List <ISpecies>(); foreach (ISpeciesZoneInfo zone in await database.GetZonesAsync(await database.GetSpeciesAsync(player1.Gotchi.Gotchi.SpeciesId))) { species_list.AddRange((await database.GetSpeciesAsync(zone.Zone)).Where(x => !x.Status.IsExinct)); } player2 = new PlayerState(); if (species_list.Count() > 0) { player2.Gotchi = await database.GenerateGotchiAsync(new GotchiGenerationParameters { Base = player1.Gotchi.Gotchi, Species = species_list[NumberUtilities.GetRandomInteger(species_list.Count())], MinLevel = player1.Gotchi.Stats.Level - 3, MaxLevel = player1.Gotchi.Stats.Level + 3, GenerateMoveset = true, GenerateStats = true }); } // Set the opponent. if (player2.Gotchi != null) { player2.Gotchi.Gotchi.OwnerId = WildGotchiUserId; player2.Gotchi.Gotchi.Id = WildGotchiId; } }
public async Task Roll(int min, int max) { if (min < 0 || max < 0) { await ReplyErrorAsync("Values must be greater than 1."); } else if (min > max + 1) { await ReplyErrorAsync("Minimum value must be less than or equal to the maximum value."); } else { int result = NumberUtilities.GetRandomInteger(min, max + 1); await ReplyAsync(result.ToString()); } }
public static void InitializeLuaContext(Script script) { if (!_registered_assembly) { UserData.RegisterAssembly(); _registered_assembly = true; } script.Globals["Console"] = (Action <string>)((string x) => Console.WriteLine(x)); script.Globals["Rand"] = (Func <int, int, int>)((int min, int max) => NumberUtilities.GetRandomInteger(min, max)); script.Globals["Chance"] = (Func <int, bool>)((int chance) => NumberUtilities.GetRandomInteger(0, chance) == 0); script.Globals["Min"] = (Func <int, int, int>)((int a, int b) => Math.Min(a, b)); script.Globals["Max"] = (Func <int, int, int>)((int a, int b) => Math.Max(a, b)); script.Globals["Swap"] = (Action <object, object>)((object a, object b) => GeneralUtilities.Swap(ref a, ref b)); script.Globals["NewRequirements"] = (Func <GotchiRequirements>)(() => LuaFunctions.NewRequirements()); }
private Tuple <PlayerState, PlayerState> _getTurnOrder() { /* Determine which player gets to move first at the start of the turn. * * Move priority is considered first, followed by speed, and then random selection. * * */ PlayerState first, second; if (player1.SelectedMove.Priority > player2.SelectedMove.Priority) { first = player1; second = player2; } else if (player2.SelectedMove.Priority > player1.SelectedMove.Priority) { first = player2; second = player1; } else if (player1.Gotchi.Stats.Spd > player2.Gotchi.Stats.Spd) { first = player1; second = player2; } else if (player2.Gotchi.Stats.Spd > player1.Gotchi.Stats.Spd) { first = player2; second = player1; } else { if (NumberUtilities.GetRandomInteger(0, 2) == 0) { first = player1; second = player2; } else { first = player2; second = player1; } } return(new Tuple <PlayerState, PlayerState>(first, second)); }
public async override Task ApplyAsync(ISearchContext context, ISearchResult result) { if (int.TryParse(Value, out int count) && count > 0) { IEnumerable <ISpecies> results = await result.GetResultsAsync(); // Take N random IDs from the results. IEnumerable <long> randomIds = results .Where(species => species.Id.HasValue) .OrderBy(species => NumberUtilities.GetRandomInteger(int.MaxValue)) .Take(count) .Select(species => (long)species.Id) .ToArray(); // Filter all but those results. await result.FilterByAsync(async (species) => await Task.FromResult(!randomIds.Any(id => id == species.Id)), Invert); } }
public GotchiMove GetRandomMove() { // Select randomly from all moves that currently have PP. List <GotchiMove> options = new List <GotchiMove>(); foreach (GotchiMove move in Moves) { if (move.PP > 0) { options.Add(move); } } if (options.Count() > 0) { return(options[NumberUtilities.GetRandomInteger(options.Count())]); } else { return(null); } }
public static async Task <BattleGotchi> GenerateGotchiAsync(this SQLiteDatabase database, 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. ISpecies species = parameters.Species; if (species is null) { IEnumerable <ISpecies> base_species_list = await database.GetBaseSpeciesAsync(); if (base_species_list.Count() > 0) { species = base_species_list.ElementAt(NumberUtilities.GetRandomInteger(0, base_species_list.Count())); } } if (species != null) { result.Gotchi.SpeciesId = (long)species.Id; } // Evolve it the given number of times. for (int i = 0; i < parameters.MaxEvolutions; ++i) { if (!await database.EvolveAndUpdateGotchiAsync(result.Gotchi)) { break; } } // Generate stats (if applicable). if (parameters.GenerateStats) { result.Gotchi.Experience = GotchiExperienceCalculator.ExperienceToLevel(result.Stats.ExperienceGroup, NumberUtilities.GetRandomInteger(parameters.MinLevel, parameters.MaxLevel + 1)); result.Stats = await new GotchiStatsCalculator(database, Global.GotchiContext).GetStatsAsync(result.Gotchi); } // Generate moveset (if applicable). if (parameters.GenerateMoveset) { result.Moves = await Global.GotchiContext.MoveRegistry.GetMoveSetAsync(database, result.Gotchi); } // Generate types. result.Types = await Global.GotchiContext.TypeRegistry.GetTypesAsync(database, result.Gotchi); // Generate a name for the gotchi. result.Gotchi.Name = (species is null ? "Wild Gotchi" : species.GetShortName()) + string.Format(" (Lv. {0})", result.Stats is null ? 1 : result.Stats.Level); return(result); }
public static async Task <bool> EvolveAndUpdateGotchiAsync(this SQLiteDatabase database, Gotchi gotchi, string desiredEvo) { bool evolved = false; if (string.IsNullOrEmpty(desiredEvo)) { // Find all descendatants of this species. using (SQLiteCommand cmd = new SQLiteCommand("SELECT species_id FROM Ancestors WHERE ancestor_id=$ancestor_id;")) { List <long> descendant_ids = new List <long>(); cmd.Parameters.AddWithValue("$ancestor_id", gotchi.SpeciesId); foreach (DataRow row in await database.GetRowsAsync(cmd)) { descendant_ids.Add(row.Field <long>("species_id")); } // Pick an ID at random. if (descendant_ids.Count > 0) { gotchi.SpeciesId = descendant_ids[NumberUtilities.GetRandomInteger(descendant_ids.Count)]; evolved = true; } } } else { // Get the desired evo. IEnumerable <ISpecies> sp = await database.GetSpeciesAsync(desiredEvo); if (sp is null || sp.Count() != 1) { return(false); } // Ensure that the species evolves into the desired evo. using (SQLiteCommand cmd = new SQLiteCommand("SELECT COUNT(*) FROM Ancestors WHERE ancestor_id = $ancestor_id AND species_id = $species_id")) { cmd.Parameters.AddWithValue("$ancestor_id", gotchi.SpeciesId); cmd.Parameters.AddWithValue("$species_id", sp.First().Id); if (await database.GetScalarAsync <long>(cmd) <= 0) { return(false); } gotchi.SpeciesId = (long)sp.First().Id; evolved = true; } } // Update the gotchi in the database. if (evolved) { using (SQLiteCommand cmd = new SQLiteCommand("UPDATE Gotchi SET species_id=$species_id, evolved_ts=$evolved_ts WHERE id=$id;")) { cmd.Parameters.AddWithValue("$species_id", gotchi.SpeciesId); // The "last evolved" timestamp is now only updated in the event the gotchi evolves (in order to make the "IsEvolved" check work correctly). // Note that this means that the background service will attempt to evolve the gotchi at every iteration (unless it evolves by leveling). cmd.Parameters.AddWithValue("$evolved_ts", DateTimeOffset.UtcNow.ToUnixTimeSeconds()); cmd.Parameters.AddWithValue("$id", gotchi.Id); await database.ExecuteNonQueryAsync(cmd); } } return(evolved); }
private async Task _endBattle(ICommandContext context) { PlayerState winner = player1.Gotchi.Stats.Hp <= 0.0 ? player2 : player1; PlayerState loser = player1.Gotchi.Stats.Hp <= 0.0 ? player1 : player2; // Calculate the amount of EXP awarded to the winner. // The loser will get 50% of the winner's EXP. double exp = _getExpEarned(winner.Gotchi.Gotchi, loser.Gotchi.Gotchi, won: true); double exp1 = player2.Gotchi.Stats.Hp <= 0.0 ? exp : exp * .5; double exp2 = player1.Gotchi.Stats.Hp <= 0.0 ? exp : exp * .5; long levels1 = player1.Gotchi.Stats.AddExperience((int)exp1); long levels2 = player2.Gotchi.Stats.AddExperience((int)exp2); StringBuilder sb = new StringBuilder(); sb.AppendLine(battleText); // Show the winner's accomplishments, then the loser's. if (!IsCpuGotchi(winner.Gotchi.Gotchi)) { double winner_exp = winner.Gotchi.Gotchi.Id == player1.Gotchi.Gotchi.Id ? exp1 : exp2; long winner_levels = winner.Gotchi.Gotchi.Id == player1.Gotchi.Gotchi.Id ? levels1 : levels2; long winner_g = (long)Math.Round(loser.Gotchi.Stats.Level * (NumberUtilities.GetRandomInteger(150, 200) / 100.0)); sb.AppendLine(string.Format("🏆 **{0}** won the battle! Earned **{1} EXP** and **{2}G**.", winner.Gotchi.Gotchi.Name, winner_exp, winner_g)); if (winner_levels > 0) { sb.AppendLine(string.Format("🆙 **{0}** leveled up to level **{1}**!", winner.Gotchi.Gotchi.Name, winner.Gotchi.Stats.Level)); } if (((winner.Gotchi.Stats.Level - winner_levels) / 10) < (winner.Gotchi.Stats.Level / 10)) { if (await database.EvolveAndUpdateGotchiAsync(winner.Gotchi.Gotchi)) { ISpecies sp = await database.GetSpeciesAsync(winner.Gotchi.Gotchi.SpeciesId); sb.AppendLine(string.Format("🚩 Congratulations, **{0}** evolved into **{1}**!", winner.Gotchi.Gotchi.Name, sp.GetShortName())); } } // Update the winner's G. GotchiUserInfo user_data = await database.GetUserInfoAsync(winner.Gotchi.Gotchi.OwnerId); user_data.G += winner_g; await database.UpdateUserInfoAsync(user_data); sb.AppendLine(); } if (!IsCpuGotchi(loser.Gotchi.Gotchi)) { double loser_exp = loser.Gotchi.Gotchi.Id == player1.Gotchi.Gotchi.Id ? exp1 : exp2; long loser_levels = loser.Gotchi.Gotchi.Id == player1.Gotchi.Gotchi.Id ? levels1 : levels2; sb.AppendLine(string.Format("💀 **{0}** lost the battle... Earned **{1} EXP**.", loser.Gotchi.Gotchi.Name, loser_exp)); if (loser_levels > 0) { sb.AppendLine(string.Format("🆙 **{0}** leveled up to level **{1}**!", loser.Gotchi.Gotchi.Name, loser.Gotchi.Stats.Level)); } if (((loser.Gotchi.Stats.Level - loser_levels) / 10) < (loser.Gotchi.Stats.Level / 10)) { if (await database.EvolveAndUpdateGotchiAsync(loser.Gotchi.Gotchi)) { ISpecies sp = await database.GetSpeciesAsync(loser.Gotchi.Gotchi.SpeciesId); sb.AppendLine(string.Format("🚩 Congratulations, **{0}** evolved into **{1}**!", loser.Gotchi.Gotchi.Name, sp.GetShortName())); } } } // Update exp in the database. using (SQLiteCommand cmd = new SQLiteCommand("UPDATE Gotchi SET level=$level, exp=$exp WHERE id=$id;")) { cmd.Parameters.AddWithValue("$id", player1.Gotchi.Gotchi.Id); cmd.Parameters.AddWithValue("$level", DBNull.Value); cmd.Parameters.AddWithValue("$exp", player1.Gotchi.Stats.Experience); await database.ExecuteNonQueryAsync(cmd); } if (!IsCpuGotchi(player2.Gotchi.Gotchi)) { using (SQLiteCommand cmd = new SQLiteCommand("UPDATE Gotchi SET level=$level, exp=$exp WHERE id=$id;")) { cmd.Parameters.AddWithValue("$id", player2.Gotchi.Gotchi.Id); cmd.Parameters.AddWithValue("$level", DBNull.Value); cmd.Parameters.AddWithValue("$exp", player2.Gotchi.Stats.Experience); await database.ExecuteNonQueryAsync(cmd); } } // Deregister the battle state. DeregisterBattle(context.User.Id); battleText = sb.ToString(); }
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 || (NumberUtilities.GetRandomInteger(0, 20 + 1) < 20 * user.SelectedMove.Accuracy * Math.Max(0.1, user.Gotchi.Stats.Acc - target.Gotchi.Stats.Eva)); args.IsCritical = !user.SelectedMove.IgnoreCritical && (NumberUtilities.GetRandomInteger(0, (int)(10 / user.SelectedMove.CriticalRate)) == 0 || (await database.GetPreyAsync(user.Gotchi.Gotchi.SpeciesId)).Any(x => x.Species.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 Idea() { List <Func <Task <IEnumerable <string> > > > ideaGenerators = new List <Func <Task <IEnumerable <string> > > > { async() => await GetEmptyZoneIdeasAsync(), async() => await GetSmallGenusIdeasAsync(), async() => await GetEmptyLineageIdeasAsync(), async() => await GetNoPredatorIdeasAsync(), async() => await GetMissingRolesInZoneIdeasAsync() }; List <string> ideas = new List <string>(); foreach (int index in Enumerable.Range(0, ideaGenerators.Count()).OrderBy(i => NumberUtilities.GetRandomInteger())) { ideas.AddRange(await ideaGenerators[index]()); if (ideas.Any()) { break; } } if (ideas.Count() > 0) { await ReplyInfoAsync(string.Format("💡 {0}", ideas.Random())); } else { await ReplyInfoAsync("I don't have any good ideas right now."); } }