public static bool IsOfferValid(ArcadeUser author, ArcadeUser target, TradeOffer offer) { if (DateTime.UtcNow - offer.CreatedAt >= TimeSpan.FromHours(24)) { return(false); } foreach ((string itemId, int amount) in offer.ItemIds) { if (ItemHelper.GetOwnedAmount(author, itemId) < amount) { return(false); } } foreach ((string itemId, int amount) in offer.RequestedItemIds) { if (ItemHelper.GetOwnedAmount(target, itemId) < amount) { return(false); } } return(true); }
public void Apply(ArcadeUser user) { if (Money > 0) { user.Give(Money); } ulong oldExp = user.Exp; if (Exp > 0) { user.Exp += Exp; } if (user.Config.Notifier.HasFlag(NotifyAllow.Level)) { int oldLevel = ExpConvert.AsLevel(oldExp, user.Ascent); int level = user.Level; if (level > oldLevel) { user.Notifier.Append($"Level up! ({LevelViewer.GetLevel(oldLevel, user.Ascent)} to {LevelViewer.GetLevel(level, user.Ascent)})"); } } if (!Check.NotNullOrEmpty(ItemIds)) { return; } foreach ((string itemId, int amount) in ItemIds) { ItemHelper.GiveItem(user, itemId, amount); } }
private static bool MatchesAny(Item item, string input) { if (item.Id == input) { return(true); } ItemGroup group = ItemHelper.GetGroup(item.GroupId); if (group?.Id == input || (group?.Icon?.Equals(input) ?? false)) { return(true); } if (item.Rarity.ToString().Equals(input, StringComparison.OrdinalIgnoreCase)) { return(true); } if (item.Tag.GetFlags().Any(x => x.ToString().Equals(input, StringComparison.OrdinalIgnoreCase))) { return(true); } if (Enum.TryParse(input, true, out ItemFilter filter)) { return(MeetsFilter(item, filter)); } return(item.Name.Contains(input, StringComparison.OrdinalIgnoreCase) || (group?.Name?.Contains(input, StringComparison.OrdinalIgnoreCase) ?? group?.Prefix?.Contains(input, StringComparison.OrdinalIgnoreCase) ?? false)); }
public static bool Craft(ArcadeUser user, Recipe recipe) { if (GetRecipeStatus(user, recipe) == RecipeStatus.Unknown) { return(false); } if (!CanCraft(user, recipe)) { return(false); } if (recipe.Result == null) { throw new Exception("Expected recipe result but returned null"); } foreach ((string itemId, int amount) in recipe.Components) { ItemHelper.TakeItem(user, itemId, amount); } ItemHelper.GiveItem(user, recipe.Result.ItemId, recipe.Result.Amount); user.AddToVar(Stats.ItemsCrafted); return(true); }
public Item Next() { int totalWeight = Loot.Entries.Sum(x => x.Weight); int marker = RandomProvider.Instance.Next(0, totalWeight); int weightSum = 0; if (totalWeight == 0) { return(null); } string choice = Loot.Entries.First().ItemId; for (int i = 0; i < Loot.Entries.Count; i++) { weightSum += Loot.Entries[i].Weight; if (marker <= weightSum) { choice = Loot.Entries[i].ItemId; break; } } return(ItemHelper.GetItem(choice)); }
public static string WriteReward(Reward reward) { var info = new StringBuilder(); if (reward.Money > 0) { info.AppendLine($"> • {Icons.Balance} **{reward.Money:##,0}**"); } if (reward.Exp > 0) { info.AppendLine($"> • {Icons.Exp} **{reward.Exp:##,0}**"); } if (!Check.NotNullOrEmpty(reward.ItemIds)) { return(info.ToString()); } foreach ((string itemId, int amount) in reward.ItemIds) { string counter = amount > 1 ? $" (x**{amount:##,0}**)" : ""; info.AppendLine($"> • {ItemHelper.IconOf(itemId)}{ItemHelper.NameOf(itemId)}{counter}"); } return(info.ToString()); }
private static IEnumerable <KeyValuePair <string, long> > GetVisibleStats(ArcadeUser user) => user.Stats.Where(x => x.Value != 0 && Var.TypeOf(x.Key) == VarType.Stat && !ItemHelper.Exists(Var.GetGroup(x.Key)) && !ShopHelper.Exists(Var.GetGroup(x.Key)) ).OrderBy(x => x.Key);
// 35 / 25 => 1.23 public static string GetRandomStat(ArcadeUser user, IEnumerable <string> chosen) => Randomizer.ChooseOrDefault(user.Stats.Keys.Where(x => user.GetVar(x) != 0 && !x.EqualsAny(Vars.Balance, Vars.Debt, Vars.Chips, Vars.Tokens) && Var.TypeOf(x) == VarType.Stat && !ItemHelper.Exists(Var.GetGroup(x)) && !ShopHelper.Exists(Var.GetGroup(x)) && (Check.NotNullOrEmpty(chosen) ? !chosen.Contains(x) : true)));
private static string WriteItem(string itemId, int amount) { Item item = ItemHelper.GetItem(itemId); string icon = ItemHelper.IconOf(itemId); string name = Check.NotNull(icon) ? item.Name : item.GetName(); return($"{(Check.NotNull(icon) ? $"{icon} " : "• ")}{name}{(amount > 1 ? $" (x**{amount:##,0}**)" : "")}"); }
public override Task <TypeReaderResult> ReadAsync(ICommandContext ctx, string input, IServiceProvider provider) { if (ItemHelper.Exists(input)) { return(Task.FromResult(TypeReaderResult.FromSuccess(ItemHelper.GetItem(input)))); } return(Task.FromResult(TypeReaderResult.FromError(CommandError.ObjectNotFound, "Could not find an Item with the specified ID."))); }
public string GetName() { if (ItemHelper.TryGetGroup(GroupId, out ItemGroup group)) { return($"{group.Prefix}{Name}"); } return(Name ?? Id); }
public static bool CanSell(Shop shop, ItemData data) { Item item = ItemHelper.GetItem(data.Id); if (item == null) { throw new Exception("Invalid data instance specified"); } return((item.Tag & shop.SellTags) != 0); }
/// <summary> /// Initializes a non-unique <see cref="ItemData"/> stack. /// </summary> /// <param name="id">The ID of the <see cref="Item"/> to store.</param> /// <param name="stackCount">The stack count of the <see cref="Item"/> to store.</param> internal ItemData(string id, int stackCount) { if (ItemHelper.IsUnique(id)) { throw new Exception("Incorrect item data initializer used."); } Id = id; TempId = KeyBuilder.Generate(5); StackCount = stackCount; }
/// <summary> /// Initializes a unique <see cref="ItemData"/> stack. /// </summary> /// <param name="id">The ID of the <see cref="Item"/> to store.</param> /// <param name="data">The unique data of the <see cref="Item"/> to store.</param> public ItemData(string id, UniqueItemData data) { if (!ItemHelper.IsUnique(id)) { throw new Exception("Incorrect item data initializer used."); } Id = id; TempId = KeyBuilder.Generate(5); Data = data; }
// sum together all unique tags public static ItemTag GetUniqueTags(ItemCatalog catalog) { ItemTag unique = 0; foreach ((string itemId, int amount) in catalog.ItemIds) { unique |= ItemHelper.GetTag(itemId); } return(unique); }
// This writes the catalog info public static string WriteCatalog(CatalogGenerator generator, ItemCatalog catalog) { var info = new StringBuilder(); foreach ((string itemId, int amount) in catalog.ItemIds) { int discountUpper = catalog.Discounts.ContainsKey(itemId) ? catalog.Discounts[itemId] : 0; info.AppendLine(WriteCatalogEntry(ItemHelper.GetItem(itemId), amount, generator.Entries.Any(x => x.ItemId == itemId && x.IsSpecial), discountUpper)); } return(info.ToString()); }
private static bool MeetsFilter(Item item, ItemFilter filter) { return(filter switch { ItemFilter.Ingredient => ItemHelper.IsIngredient(item), ItemFilter.Craftable => CraftHelper.CanCraft(item), ItemFilter.Sellable => ItemHelper.CanSell(item), ItemFilter.Buyable => ItemHelper.CanBuy(item), ItemFilter.Usable => item.Usage != null, ItemFilter.Tradable => ItemHelper.CanTrade(item), ItemFilter.Unique => ItemHelper.IsUnique(item), _ => false });
public string GetSummary() { if (Check.NotNull(Summary)) { return(Summary); } if (ItemHelper.TryGetGroup(GroupId, out ItemGroup group)) { return(group.Summary); } return(Summary); }
public string GetIcon() { if (Check.NotNull(Icon)) { return(Icon); } if (ItemHelper.TryGetGroup(GroupId, out ItemGroup group)) { return(group.Icon?.ToString() ?? ""); } return(Icon ?? ""); }
private static void RemoveBoosts(ArcadeUser user, ref List <BoostData> toRemove) { foreach (BoostData booster in toRemove) { user.Boosters.Remove(booster); if (!string.IsNullOrWhiteSpace(booster.ParentId)) { continue; } Item parent = ItemHelper.GetItem(booster.ParentId); parent?.Usage?.OnBreak?.Invoke(user); } }
public static bool CanCraft(ArcadeUser user, Recipe recipe) { if (recipe == null) { throw new Exception("Could not find a recipe with the specified ID"); } foreach ((string itemId, int amount) in recipe.Components) { if (!ItemHelper.HasItem(user, itemId) || ItemHelper.GetOwnedAmount(user, itemId) != amount) { return(false); } } return(true); }
// Invokes the trade, and lets everything go through private void Trade() { if (HostReady && ParticipantReady) { // This needs to handle unique item data. foreach ((string itemId, int amount) in HostOffer) { ItemHelper.TakeItem(Host, itemId, amount); ItemHelper.GiveItem(Participant, itemId, amount); } foreach ((string itemId, int amount) in ParticipantOffer) { ItemHelper.TakeItem(Participant, itemId, amount); ItemHelper.GiveItem(Host, itemId, amount); } } }
public static string Sell(Shop shop, ItemData data, ArcadeUser user) { if (!CanSell(shop, data)) { return(Format.Warning($"**{shop.Name}** does not accept this item.")); } Item item = ItemHelper.GetItem(data.Id); ItemHelper.TakeItem(user, data); long value = shop.SellDeduction > 0 ? (long)Math.Floor(item.Value * (1 - shop.SellDeduction / (double)100)) : item.Value; user.Give(value, item.Currency); string icon = (Check.NotNull(item.GetIcon()) ? $"{item.GetIcon()} " : ""); string name = $"{icon}**{(Check.NotNull(icon) ? item.Name : item.GetName())}**"; return($"> You have received {Icons.IconOf(item.Currency)} **{value:##,0}** for {name}."); }
public static Dictionary <string, int> GetMissingFromRecipe(ArcadeUser user, Recipe recipe) { if (recipe == null) { throw new Exception("Could not find a recipe with the specified ID"); } var missing = new Dictionary <string, int>(); foreach ((string itemId, int amount) in recipe.Components) { int owned = ItemHelper.GetOwnedAmount(user, itemId); if (owned < amount) { missing[itemId] = amount - owned; } } return(missing); }
public static string SendOffer(ArcadeUser author, ArcadeUser target, TradeOffer offer) { if (author.Offers.Count(x => x.Target.Id == target.Id) == 1) { return(Format.Warning($"You already have an active trade offer to **{target.Username}**. Please cancel your current offer to this user before sending a new one.")); } if (author.Offers.Count == 5) { return(Format.Warning("You already have too many active trade offers. Try again later.")); } foreach ((string itemId, int amount) in offer.ItemIds) { if (ItemHelper.GetOwnedAmount(author, itemId) < amount) { return(Format.Warning("You do not own one of the specified items in your trade offer.")); } } foreach ((string itemId, int amount) in offer.RequestedItemIds) { if (ItemHelper.GetOwnedAmount(target, itemId) < amount) { return(Format.Warning($"**{target.Username}** does not own one of the specified items in your trade offer.")); } } if (target.Offers.Count == 5) { return(Format.Warning( $"**{target.Username}** already has too many pending trade offers. Try again later.")); } target.Offers.Add(offer); author.Offers.Add(TradeOffer.CloneAsOutbound(offer)); return($"Successfully sent **{target.Username}** a trade offer."); }
public static string AcceptOffer(ArcadeUser target, ArcadeUser author, TradeOffer offer) { // Get the author in the command if (offer.Author.Id != author.Id) { throw new Exception("Expected author to match offer author"); } if (offer.Type == OfferType.Outbound) { return(Format.Warning("You cannot accept outbound trade offers.")); } if (!IsOfferValid(author, target, offer)) { author.Offers.RemoveAll(x => x.Id == offer.Id); target.Offers.RemoveAll(x => x.Id == offer.Id); return(Format.Warning("This trade offer has expired or is invalid due to missing items.")); } foreach ((string itemId, int amount) in offer.ItemIds) { ItemHelper.TakeItem(author, itemId, amount); ItemHelper.GiveItem(target, itemId, amount); } foreach ((string itemId, int amount) in offer.RequestedItemIds) { ItemHelper.TakeItem(target, itemId, amount); ItemHelper.GiveItem(author, itemId, amount); } target.Offers.RemoveAll(x => x.Id == offer.Id); author.Offers.RemoveAll(x => x.Id == offer.Id); return($"Successfully accepted offer `{offer.Id}` from **{offer.Author.ToString("Unknown User")}**.{(offer.ItemIds.Count > 0 ? $"\nYou have received:\n{string.Join("\n", offer.ItemIds.Select(x => WriteItem(x.Key, x.Value)))}" : "")}"); }
/// <summary> /// Generates a new <see cref="ItemCatalog"/>. /// </summary> public ItemCatalog Generate(long tier = 1) { int specials = 0; int discounts = 0; var counters = new Dictionary <string, int>(); var discountEntries = new Dictionary <string, int>(); var groupCounters = new Dictionary <string, int>(); int entries = 0; if (Size <= 0) { throw new ArgumentException("Cannot initialize a catalog with an empty or negative size."); } while (entries < Size) { CatalogEntry entry = GetNextEntry(tier, specials, counters, groupCounters); if (entry == null) { break; } if (entry.IsSpecial) { Logger.Debug($"Special entry applied ({entry.ItemId})"); specials++; } else { Logger.Debug($"Next entry loaded ({entry.ItemId})"); } if (entry.DiscountChance > 0 && entry.MaxDiscount.HasValue) { if (!discountEntries.ContainsKey(entry.ItemId) && discounts < MaxDiscountsAllowed && RandomProvider.Instance.NextDouble() <= entry.DiscountChance) { // NOTE: Since the upper bound is exclusive, add 1 to the max discount, so that it's possible to land on the specified max int discountToApply = RandomProvider.Instance .Next(entry.MinDiscount ?? CatalogEntry.DefaultMinDiscount, (entry.MaxDiscount ?? CatalogEntry.DefaultMaxDiscount) + 1); discountEntries.Add(entry.ItemId, discountToApply); Logger.Debug($"{discountToApply}% discount applied ({entry.ItemId})"); discounts++; } } if (!ItemHelper.Exists(entry.ItemId)) { throw new Exception("The specified item ID could not be found."); } if (!counters.TryAdd(entry.ItemId, 1)) { counters[entry.ItemId]++; } if (Check.NotNull(entry.GroupId)) { if (!groupCounters.TryAdd(entry.GroupId, 1)) { groupCounters[entry.GroupId]++; } } entries++; } Logger.Debug($"Compiling catalog with {entries} {Format.TryPluralize("entry", entries)}"); return(new ItemCatalog(counters, discountEntries)); }
// This is only invoked if the ID is either the Host or the Participant. public override async Task <MatchResult> InvokeAsync(SocketMessage message) { // gets the account that executed this. var account = GetAccount(message.Author.Id); // Get the input from the message string input = message.Content; // CANCEL: This cancels the current trade, regardless of who executed it. if (input == "cancel") { await SetStateAsync(TradeState.Cancel); await UpdateMessageAsync(WriteOnCancel(account)); return(MatchResult.Success); } // If the current invoker is not the current speaker, ignore their inputs if (CurrentId.HasValue) { if (CurrentId.Value != account.Id) { return(MatchResult.Continue); } } switch (State) { case TradeState.Invite: // ACCEPT if (input == "accept" && account.Id == Participant.Id) { // If the user accepted the trade invitation, go to the base trade menu. await SetStateAsync(TradeState.Menu, $"> ☑️ **{Participant.Username}** has agreed to trade."); Participant.CanTrade = false; return(MatchResult.Continue); } // DECLINE if (input == "decline" && account.Id == Participant.Id) { await SetStateAsync(TradeState.Cancel); await UpdateMessageAsync(WriteOnCancel(account)); return(MatchResult.Success); } break; case TradeState.Menu: // ACCEPT: This marks the user who executed it that they accepted their end of the trade. // If both users accept, then the trade goes through. Take all specified items from both, swap, and give both the other's items. if (input == "ready") { if (IsOfferEmpty()) { await SetStateAsync(TradeState.Menu, Format.Warning("There aren't any items specified!")); return(MatchResult.Continue); } if (CanStartTrade()) { Trade(); await SetStateAsync(TradeState.Success); return(MatchResult.Success); } MarkAsReady(account.Id); await SetStateAsync(TradeState.Menu); return(MatchResult.Continue); } if (input == "inventory") { // If someone is already inspecting their backpack, ignore input. if (CurrentId.HasValue) { return(MatchResult.Continue); } CurrentId = account.Id; await SetStateAsync(TradeState.Inventory); return(MatchResult.Continue); } break; case TradeState.Inventory: // BACK: Used by the current invoker to return to the menu. if (input == "back") { CurrentId = null; await SetStateAsync(TradeState.Menu); return(MatchResult.Continue); } // TODO: Handle unique item ID when trading. // Check if the specified item exists in the invoker inventory if (!ItemHelper.HasItem(account, input)) { await SetStateAsync(TradeState.Inventory, Format.Warning("An invalid item ID was specified.")); return(MatchResult.Continue); } // If the specified item was already selected, remove it from their trade. if (GetCurrentItems().ContainsKey(input)) { RemoveItemFromCurrent(input); await SetStateAsync(TradeState.Inventory, "> 📤 Removed the specified item from your offer."); HostReady = false; ParticipantReady = false; return(MatchResult.Continue); } ItemData selectedItem = ItemHelper.DataOf(account, input); if (!ItemHelper.CanTrade(input, selectedItem)) { await SetStateAsync(TradeState.Inventory, Format.Warning("This item is unavailable for trading.")); return(MatchResult.Continue); } AddItemToCurrent(input); await SetStateAsync(TradeState.Inventory, "> 📥 Added the specified item to your offer."); HostReady = false; ParticipantReady = false; return(MatchResult.Continue); } return(MatchResult.Continue); }
public static IEnumerable <Recipe> RecipesFor(string itemId) => RecipesFor(ItemHelper.GetItem(itemId));