Example #1
0
        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);
        }
Example #2
0
        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);
            }
        }
Example #3
0
        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));
        }
Example #4
0
        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);
        }
Example #5
0
        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));
        }
Example #6
0
        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());
        }
Example #7
0
 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);
Example #8
0
        // 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)));
Example #9
0
        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}**)" : "")}");
        }
Example #10
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.")));
        }
Example #11
0
        public string GetName()
        {
            if (ItemHelper.TryGetGroup(GroupId, out ItemGroup group))
            {
                return($"{group.Prefix}{Name}");
            }

            return(Name ?? Id);
        }
Example #12
0
        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);
        }
Example #13
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;
        }
Example #14
0
        /// <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;
        }
Example #15
0
        // 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);
        }
Example #16
0
        // 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());
        }
Example #17
0
 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
     });
Example #18
0
        public string GetSummary()
        {
            if (Check.NotNull(Summary))
            {
                return(Summary);
            }

            if (ItemHelper.TryGetGroup(GroupId, out ItemGroup group))
            {
                return(group.Summary);
            }

            return(Summary);
        }
Example #19
0
        public string GetIcon()
        {
            if (Check.NotNull(Icon))
            {
                return(Icon);
            }

            if (ItemHelper.TryGetGroup(GroupId, out ItemGroup group))
            {
                return(group.Icon?.ToString() ?? "");
            }

            return(Icon ?? "");
        }
Example #20
0
        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);
            }
        }
Example #21
0
        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);
        }
Example #22
0
        // 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);
                }
            }
        }
Example #23
0
        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}.");
        }
Example #24
0
        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);
        }
Example #25
0
        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.");
        }
Example #26
0
        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)))}" : "")}");
        }
Example #27
0
        /// <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));
        }
Example #28
0
        // 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);
        }
Example #29
0
 public static IEnumerable <Recipe> RecipesFor(string itemId)
 => RecipesFor(ItemHelper.GetItem(itemId));