Exemple #1
0
        public void ExecuteThrowNotEnoughMaterialException()
        {
            var row = _tableSheets.EquipmentItemRecipeSheet.Values.First();

            const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationEquipmentAction;

            for (var i = 1; i < requiredStage + 1; i++)
            {
                _avatarState.worldInformation.ClearStage(
                    1,
                    i,
                    0,
                    _tableSheets.WorldSheet,
                    _tableSheets.WorldUnlockSheet
                    );
            }

            _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize());

            var action = new CombinationEquipment()
            {
                AvatarAddress = _avatarAddress,
                RecipeId      = row.Id,
                SlotIndex     = 0,
            };

            Assert.Throws <NotEnoughMaterialException>(() => action.Execute(new ActionContext()
            {
                PreviousStates = _initialState,
                Signer         = _agentAddress,
                Random         = new ItemEnhancementTest.TestRandom(),
            }));
        }
Exemple #2
0
        public IObservable <ActionBase.ActionEvaluation <CombinationEquipment> > CombinationEquipment(
            int recipeId,
            int slotIndex,
            int?subRecipeId = null)
        {
            Mixpanel.Track("Unity/Create CombinationEquipment");

            // 결과 주소도 고정되게 바꿔야함
            var action = new CombinationEquipment
            {
                AvatarAddress = States.Instance.CurrentAvatarState.address,
                RecipeId      = recipeId,
                SubRecipeId   = subRecipeId,
                SlotIndex     = slotIndex,
            };

            ProcessAction(action);

            return(_renderer.EveryRender <CombinationEquipment>()
                   .Where(eval => eval.Action.Id.Equals(action.Id))
                   .Take(1)
                   .Last()
                   .ObserveOnMainThread()
                   .Timeout(ActionTimeout)
                   .DoOnError(e => HandleException(action.Id, e)));
        }
Exemple #3
0
        public void ExecuteWithSubRecipe()
        {
            var row         = _tableSheets.EquipmentItemRecipeSheet.Values.First(r => r.SubRecipeIds.Any());
            var subRecipeId = row.SubRecipeIds.First();
            var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId];
            var material    = ItemFactory.CreateItem(materialRow, _random);

            _avatarState.inventory.AddItem(material, row.MaterialCount);

            var subRecipeRow = _tableSheets.EquipmentItemSubRecipeSheet.Values.First(r => r.Id == subRecipeId);

            foreach (var materialInfo in subRecipeRow.Materials)
            {
                materialRow = _tableSheets.MaterialItemSheet[materialInfo.Id];
                material    = ItemFactory.CreateItem(materialRow, _random);
                _avatarState.inventory.AddItem(material, materialInfo.Count);
            }

            for (var i = 1; i < row.UnlockStage + 1; i++)
            {
                _avatarState.worldInformation.ClearStage(
                    1,
                    i,
                    0,
                    _tableSheets.WorldSheet,
                    _tableSheets.WorldUnlockSheet
                    );
            }

            _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize());

            var action = new CombinationEquipment()
            {
                AvatarAddress = _avatarAddress,
                RecipeId      = row.Id,
                SubRecipeId   = subRecipeId,
                SlotIndex     = 0,
            };

            var nextState = action.Execute(new ActionContext()
            {
                PreviousStates = _initialState,
                Signer         = _agentAddress,
                BlockIndex     = 1,
                Random         = _random,
            });

            var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0);

            Assert.NotNull(slotState.Result);
            Assert.NotNull(slotState.Result.itemUsable);
        }
Exemple #4
0
        public void ExecuteThrowCombinationSlotUnlockException()
        {
            var row         = _tableSheets.EquipmentItemRecipeSheet.Values.First(r => r.SubRecipeIds.Any());
            var subRecipeId = row.SubRecipeIds.First();
            var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId];
            var material    = ItemFactory.CreateItem(materialRow, _random);

            _avatarState.inventory.AddItem(material, row.MaterialCount);

            var subRecipeRow = _tableSheets.EquipmentItemSubRecipeSheet.Values.First(r => r.Id == subRecipeId);

            foreach (var materialInfo in subRecipeRow.Materials)
            {
                materialRow = _tableSheets.MaterialItemSheet[materialInfo.Id];
                material    = ItemFactory.CreateItem(materialRow, _random);
                _avatarState.inventory.AddItem(material, materialInfo.Count);
            }

            for (var i = 1; i < row.UnlockStage + 1; i++)
            {
                _avatarState.worldInformation.ClearStage(
                    1,
                    i,
                    0,
                    _tableSheets.WorldSheet,
                    _tableSheets.WorldUnlockSheet
                    );
            }

            _initialState = _initialState
                            .SetState(_avatarAddress, _avatarState.Serialize())
                            .SetState(
                _slotAddress,
                new CombinationSlotState(_slotAddress, row.UnlockStage + 10).Serialize()
                );

            var action = new CombinationEquipment()
            {
                AvatarAddress = _avatarAddress,
                RecipeId      = row.Id,
                SubRecipeId   = subRecipeId,
                SlotIndex     = 0,
            };

            Assert.Throws <CombinationSlotUnlockException>(() => action.Execute(new ActionContext()
            {
                PreviousStates = _initialState,
                Signer         = _agentAddress,
                Random         = new ItemEnhancementTest.TestRandom(),
            }));
        }
Exemple #5
0
        public void ExecuteThrowFailedLoadStateException()
        {
            var action = new CombinationEquipment()
            {
                AvatarAddress = _avatarAddress,
                RecipeId      = 1,
                SubRecipeId   = 1,
                SlotIndex     = 0,
            };

            Assert.Throws <FailedLoadStateException>(() => action.Execute(new ActionContext()
            {
                PreviousStates = new State(),
                Signer         = _agentAddress,
                Random         = new ItemEnhancementTest.TestRandom(),
            }));
        }
Exemple #6
0
        public void Execute()
        {
            var row         = _tableSheets.EquipmentItemRecipeSheet.Values.First();
            var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId];
            var material    = ItemFactory.CreateItem(materialRow, _random);

            _avatarState.inventory.AddItem(material, row.MaterialCount);

            const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationEquipmentAction;

            for (var i = 1; i < requiredStage + 1; i++)
            {
                _avatarState.worldInformation.ClearStage(
                    1,
                    i,
                    0,
                    _tableSheets.WorldSheet,
                    _tableSheets.WorldUnlockSheet
                    );
            }

            _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize());

            var action = new CombinationEquipment()
            {
                AvatarAddress = _avatarAddress,
                RecipeId      = row.Id,
                SlotIndex     = 0,
            };

            var nextState = action.Execute(new ActionContext()
            {
                PreviousStates = _initialState,
                Signer         = _agentAddress,
                BlockIndex     = 1,
                Random         = _random,
            });

            var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0);

            Assert.NotNull(slotState.Result);
            Assert.NotNull(slotState.Result.itemUsable);
        }
        public void Rehearsal()
        {
            var action = new CombinationEquipment
            {
                avatarAddress = _avatarAddress,
                slotIndex     = 0,
                recipeId      = 1,
                subRecipeId   = 255,
            };
            var slotAddress = _avatarAddress.Derive(
                string.Format(
                    CultureInfo.InvariantCulture,
                    CombinationSlotState.DeriveFormat,
                    0
                    )
                );

            var updatedAddresses = new List <Address>
            {
                _agentAddress,
                _avatarAddress,
                slotAddress,
                _avatarAddress.Derive(LegacyInventoryKey),
                _avatarAddress.Derive(LegacyWorldInformationKey),
                _avatarAddress.Derive(LegacyQuestListKey),
                Addresses.Blacksmith,
            };

            var state = new State();

            var nextState = action.Execute(new ActionContext
            {
                PreviousStates = state,
                Signer         = _agentAddress,
                BlockIndex     = 0,
                Rehearsal      = true,
            });

            Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.UpdatedAddresses);
        }
        public void AddAndUnlockOption()
        {
            var agentState = _initialState.GetAgentState(_agentAddress);
            var subRecipe  = _tableSheets.EquipmentItemSubRecipeSheetV2.Last;

            Assert.NotNull(subRecipe);
            var equipment = (Necklace)ItemFactory.CreateItemUsable(
                _tableSheets.EquipmentItemSheet[10411000],
                Guid.NewGuid(),
                default);

            Assert.Equal(0, equipment.optionCountFromCombination);
            CombinationEquipment.AddAndUnlockOption(
                agentState,
                equipment,
                _random,
                subRecipe,
                _tableSheets.EquipmentItemOptionSheet,
                _tableSheets.SkillSheet
                );
            Assert.True(equipment.optionCountFromCombination > 0);
        }
        public void ExecuteThrowNotEnoughClearedStageLevelException()
        {
            const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationEquipmentAction;
            var       row           = _tableSheets.EquipmentItemRecipeSheet.Values.First(r => r.UnlockStage > requiredStage);
            var       materialRow   = _tableSheets.MaterialItemSheet[row.MaterialId];
            var       material      = ItemFactory.CreateItem(materialRow, _random);

            _avatarState.inventory.AddItem(material, row.MaterialCount);

            for (var i = 1; i < requiredStage + 1; i++)
            {
                _avatarState.worldInformation.ClearStage(
                    1,
                    i,
                    0,
                    _tableSheets.WorldSheet,
                    _tableSheets.WorldUnlockSheet
                    );
            }

            _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize());

            var action = new CombinationEquipment()
            {
                AvatarAddress = _avatarAddress,
                RecipeId      = row.Id,
                SlotIndex     = 0,
            };

            Assert.Throws <NotEnoughClearedStageLevelException>(() => action.Execute(new ActionContext()
            {
                PreviousStates = _initialState,
                Signer         = _agentAddress,
                Random         = new TestRandom(),
            }));
        }
Exemple #10
0
        public void Case(int randomSeed, int[] optionNumbers)
        {
            var gameConfigState = _initialState.GetGameConfigState();

            Assert.NotNull(gameConfigState);

            var recipeRow    = _tableSheets.EquipmentItemRecipeSheet.OrderedList.First(e => e.SubRecipeIds.Any());
            var subRecipeRow = _tableSheets.EquipmentItemSubRecipeSheetV2[recipeRow.SubRecipeIds.First()];
            var combinationEquipmentAction = new CombinationEquipment
            {
                avatarAddress = _avatarAddress,
                slotIndex     = 0,
                recipeId      = recipeRow.Id,
                subRecipeId   = subRecipeRow.Id,
            };

            var inventoryValue = _initialState.GetState(_inventoryAddress);

            Assert.NotNull(inventoryValue);
            var inventoryState = new Inventory((List)inventoryValue);

            inventoryState.AddFungibleItem(
                ItemFactory.CreateMaterial(_tableSheets.MaterialItemSheet, recipeRow.MaterialId),
                recipeRow.MaterialCount);
            foreach (var materialInfo in subRecipeRow.Materials)
            {
                inventoryState.AddFungibleItem(
                    ItemFactory.CreateMaterial(_tableSheets.MaterialItemSheet, materialInfo.Id),
                    materialInfo.Count);
            }

            var worldInformation = new WorldInformation(
                0,
                _tableSheets.WorldSheet,
                recipeRow.UnlockStage);

            var nextState = _initialState
                            .SetState(_inventoryAddress, inventoryState.Serialize())
                            .SetState(_worldInformationAddress, worldInformation.Serialize());

            var random = new TestRandom(randomSeed);

            nextState = combinationEquipmentAction.Execute(new ActionContext
            {
                PreviousStates = nextState,
                BlockIndex     = 0,
                Random         = random,
                Signer         = _agentAddress,
            });

            var slot0Value = nextState.GetState(_slot0Address);

            Assert.NotNull(slot0Value);
            var slot0State = new CombinationSlotState((Dictionary)slot0Value);

            Assert.NotNull(slot0State.Result.itemUsable);
            var equipment       = (Equipment)slot0State.Result.itemUsable;
            var additionalStats = equipment.StatsMap
                                  .GetAdditionalStats(true)
                                  .ToArray();
            var skills = equipment.Skills;

            Assert.Equal(optionNumbers.Length, equipment.optionCountFromCombination);
            var optionSheet           = _tableSheets.EquipmentItemOptionSheet;
            var mainAdditionalStatMin = 0;
            var mainAdditionalStatMax = 0;
            var requiredBlockIndex    = 0;

            foreach (var optionNumber in optionNumbers)
            {
                var optionInfo = subRecipeRow.Options[optionNumber - 1];
                requiredBlockIndex += optionInfo.RequiredBlockIndex;
                var optionRow = optionSheet[optionInfo.Id];
                if (optionRow.StatMin > 0 || optionRow.StatMax > 0)
                {
                    if (optionRow.StatType == equipment.UniqueStatType)
                    {
                        mainAdditionalStatMin += optionRow.StatMin;
                        mainAdditionalStatMax += optionRow.StatMax;
                        continue;
                    }

                    var additionalStatValue = additionalStats
                                              .First(e => e.statType == optionRow.StatType)
                                              .additionalValue;
                    Assert.True(additionalStatValue >= optionRow.StatMin);
                    Assert.True(additionalStatValue <= optionRow.StatMax + 1);
                }
                else if (optionRow.SkillId != default)
                {
                    var skill = skills.First(e => e.SkillRow.Id == optionRow.SkillId);
                    Assert.True(skill.Chance >= optionRow.SkillChanceMin);
                    Assert.True(skill.Chance <= optionRow.SkillChanceMax + 1);
                    Assert.True(skill.Power >= optionRow.SkillDamageMin);
                    Assert.True(skill.Power <= optionRow.SkillDamageMax + 1);
                }
            }

            var mainAdditionalStatValue = additionalStats
                                          .First(e => e.statType == equipment.UniqueStatType)
                                          .additionalValue;

            Assert.True(mainAdditionalStatValue >= mainAdditionalStatMin);
            Assert.True(mainAdditionalStatValue <= mainAdditionalStatMax + 1);
            Assert.Equal(requiredBlockIndex + 1, slot0State.RequiredBlockIndex);

            // FIXME
            // https://github.com/planetarium/lib9c/pull/517#discussion_r679218764
            // The tests after this line should be finished. However, since then the logic is being developed by
            // different developers in different branches. I wrote a test beforehand, but it's failing.
            // I plan to move to another branch after this PR is merged and finish writing the tests.
            return;

            if (requiredBlockIndex == 0)
            {
                return;
            }

            var hourglassRow = _tableSheets.MaterialItemSheet
                               .First(pair => pair.Value.ItemSubType == ItemSubType.Hourglass)
                               .Value;

            inventoryValue = nextState.GetState(_inventoryAddress);
            Assert.NotNull(inventoryValue);
            inventoryState = new Inventory((List)inventoryValue);
            Assert.False(inventoryState.TryGetFungibleItems(hourglassRow.ItemId, out _));

            var hourglassCount = requiredBlockIndex * gameConfigState.HourglassPerBlock;

            inventoryState.AddFungibleItem(
                ItemFactory.CreateMaterial(_tableSheets.MaterialItemSheet, hourglassRow.Id),
                hourglassCount);
            Assert.True(inventoryState.TryGetFungibleItems(hourglassRow.ItemId, out var hourglasses));
            Assert.Equal(hourglassCount, hourglasses.Sum(e => e.count));
            nextState = nextState.SetState(_inventoryAddress, inventoryState.Serialize());

            var rapidCombinationAction = new RapidCombination
            {
                avatarAddress = _avatarAddress,
                slotIndex     = 0,
            };

            nextState = rapidCombinationAction.Execute(new ActionContext
            {
                PreviousStates = nextState,
                BlockIndex     = 1,
                Random         = random,
                Signer         = _agentAddress,
            });
            inventoryValue = nextState.GetState(_inventoryAddress);
            Assert.NotNull(inventoryValue);
            inventoryState = new Inventory((List)inventoryValue);
            Assert.False(inventoryState.TryGetFungibleItems(hourglassRow.ItemId, out _));
        }
        private void Execute(bool backward, int recipeId, int?subRecipeId, int mintNCG)
        {
            var currency        = new Currency("NCG", 2, minter: null);
            var row             = _tableSheets.EquipmentItemRecipeSheet[recipeId];
            var requiredStage   = row.UnlockStage;
            var costActionPoint = row.RequiredActionPoint;
            var costNCG         = row.RequiredGold * currency;
            var materialRow     = _tableSheets.MaterialItemSheet[row.MaterialId];
            var material        = ItemFactory.CreateItem(materialRow, _random);

            var avatarState                  = _initialState.GetAvatarState(_avatarAddress);
            var previousActionPoint          = avatarState.actionPoint;
            var previousResultEquipmentCount =
                avatarState.inventory.Equipments.Count(e => e.Id == row.ResultEquipmentId);
            var previousMailCount = avatarState.mailBox.Count;

            avatarState.worldInformation = new WorldInformation(
                0,
                _tableSheets.WorldSheet,
                requiredStage);

            avatarState.inventory.AddItem(material, row.MaterialCount);

            if (subRecipeId.HasValue)
            {
                var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value];
                costActionPoint += subRow.RequiredActionPoint;
                costNCG         += subRow.RequiredGold * currency;

                foreach (var materialInfo in subRow.Materials)
                {
                    material = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random);
                    avatarState.inventory.AddItem(material, materialInfo.Count);
                }
            }

            IAccountStateDelta previousState;

            if (backward)
            {
                previousState = _initialState.SetState(_avatarAddress, avatarState.Serialize());
            }
            else
            {
                previousState = _initialState
                                .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize())
                                .SetState(
                    _avatarAddress.Derive(LegacyWorldInformationKey),
                    avatarState.worldInformation.Serialize())
                                .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize())
                                .SetState(_avatarAddress, avatarState.SerializeV2());
            }

            previousState = previousState.MintAsset(_agentAddress, mintNCG * currency);
            var goldCurrencyState = previousState.GetGoldCurrency();
            var previousNCG       = previousState.GetBalance(_agentAddress, goldCurrencyState);

            Assert.Equal(mintNCG * currency, previousNCG);

            var action = new CombinationEquipment
            {
                avatarAddress = _avatarAddress,
                slotIndex     = 0,
                recipeId      = recipeId,
                subRecipeId   = subRecipeId,
            };

            var nextState = action.Execute(new ActionContext
            {
                PreviousStates = previousState,
                Signer         = _agentAddress,
                BlockIndex     = 1,
                Random         = _random,
            });

            var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0);

            Assert.NotNull(slotState.Result);
            Assert.NotNull(slotState.Result.itemUsable);

            if (subRecipeId.HasValue)
            {
                Assert.True(((Equipment)slotState.Result.itemUsable).optionCountFromCombination > 0);
            }
            else
            {
                Assert.Equal(0, ((Equipment)slotState.Result.itemUsable).optionCountFromCombination);
            }

            var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress);

            Assert.Equal(previousActionPoint - costActionPoint, nextAvatarState.actionPoint);
            Assert.Equal(previousMailCount + 1, nextAvatarState.mailBox.Count);
            Assert.IsType <CombinationMail>(nextAvatarState.mailBox.First());
            Assert.Equal(
                previousResultEquipmentCount + 1,
                nextAvatarState.inventory.Equipments.Count(e => e.Id == row.ResultEquipmentId));

            var agentGold = nextState.GetBalance(_agentAddress, goldCurrencyState);

            Assert.Equal(previousNCG - costNCG, agentGold);

            var blackSmithGold = nextState.GetBalance(Addresses.Blacksmith, goldCurrencyState);

            Assert.Equal(costNCG, blackSmithGold);
        }
        public void Case(int randomSeed, int[] optionNumbers)
        {
            var gameConfigState = _initialState.GetGameConfigState();

            Assert.NotNull(gameConfigState);

            var subRecipeRow = _tableSheets.EquipmentItemSubRecipeSheetV2.OrderedList.First(e =>
                                                                                            e.Options.Count == 4 &&
                                                                                            e.RequiredBlockIndex > GameConfig.RequiredAppraiseBlock &&
                                                                                            e.RequiredGold == 0);
            var recipeRow =
                _tableSheets.EquipmentItemRecipeSheet.OrderedList.First(e => e.SubRecipeIds.Contains(subRecipeRow.Id));
            var combinationEquipmentAction = new CombinationEquipment
            {
                avatarAddress = _avatarAddress,
                slotIndex     = 0,
                recipeId      = recipeRow.Id,
                subRecipeId   = subRecipeRow.Id,
            };

            var inventoryValue = _initialState.GetState(_inventoryAddress);

            Assert.NotNull(inventoryValue);

            var inventoryState = new Inventory((List)inventoryValue);

            inventoryState.AddFungibleItem(
                ItemFactory.CreateMaterial(_tableSheets.MaterialItemSheet, recipeRow.MaterialId),
                recipeRow.MaterialCount);
            foreach (var materialInfo in subRecipeRow.Materials)
            {
                inventoryState.AddFungibleItem(
                    ItemFactory.CreateMaterial(_tableSheets.MaterialItemSheet, materialInfo.Id),
                    materialInfo.Count);
            }

            var worldInformation = new WorldInformation(
                0,
                _tableSheets.WorldSheet,
                recipeRow.UnlockStage);

            var nextState = _initialState
                            .SetState(_inventoryAddress, inventoryState.Serialize())
                            .SetState(_worldInformationAddress, worldInformation.Serialize());

            var random = new TestRandom(randomSeed);

            nextState = combinationEquipmentAction.Execute(new ActionContext
            {
                PreviousStates = nextState,
                BlockIndex     = 0,
                Random         = random,
                Signer         = _agentAddress,
            });

            var slot0Value = nextState.GetState(_slot0Address);

            Assert.NotNull(slot0Value);

            var slot0State = new CombinationSlotState((Dictionary)slot0Value);

            Assert.NotNull(slot0State.Result.itemUsable);

            var equipment       = (Equipment)slot0State.Result.itemUsable;
            var additionalStats = equipment.StatsMap
                                  .GetAdditionalStats(true)
                                  .ToArray();
            var skills = equipment.Skills;

            Assert.Equal(optionNumbers.Length, equipment.optionCountFromCombination);

            var optionSheet           = _tableSheets.EquipmentItemOptionSheet;
            var mainAdditionalStatMin = 0;
            var mainAdditionalStatMax = 0;
            var requiredBlockIndex    = recipeRow.RequiredBlockIndex + subRecipeRow.RequiredBlockIndex;
            var orderedOptions        = subRecipeRow.Options
                                        .OrderByDescending(e => e.Ratio)
                                        .ThenBy(e => e.RequiredBlockIndex)
                                        .ThenBy(e => e.Id)
                                        .ToArray();

            foreach (var optionNumber in optionNumbers)
            {
                var optionInfo = orderedOptions[optionNumber - 1];
                requiredBlockIndex += optionInfo.RequiredBlockIndex;
                var optionRow = optionSheet[optionInfo.Id];
                if (optionRow.StatMin > 0 || optionRow.StatMax > 0)
                {
                    if (optionRow.StatType == equipment.UniqueStatType)
                    {
                        mainAdditionalStatMin += optionRow.StatMin;
                        mainAdditionalStatMax += optionRow.StatMax;
                        continue;
                    }

                    var additionalStatValue = additionalStats
                                              .First(e => e.statType == optionRow.StatType)
                                              .additionalValue;
                    Assert.True(additionalStatValue >= optionRow.StatMin);
                    Assert.True(additionalStatValue <= optionRow.StatMax + 1);
                }
                else if (optionRow.SkillId != default)
                {
                    var skill = skills.First(e => e.SkillRow.Id == optionRow.SkillId);
                    Assert.True(skill.Chance >= optionRow.SkillChanceMin);
                    Assert.True(skill.Chance <= optionRow.SkillChanceMax + 1);
                    Assert.True(skill.Power >= optionRow.SkillDamageMin);
                    Assert.True(skill.Power <= optionRow.SkillDamageMax + 1);
                }
            }

            var mainAdditionalStatValue = additionalStats
                                          .First(e => e.statType == equipment.UniqueStatType)
                                          .additionalValue;

            Assert.True(mainAdditionalStatValue >= mainAdditionalStatMin);
            Assert.True(mainAdditionalStatValue <= mainAdditionalStatMax + 1);
            Assert.Equal(requiredBlockIndex, slot0State.RequiredBlockIndex);

            if (requiredBlockIndex == 0)
            {
                return;
            }

            var hourglassRow = _tableSheets.MaterialItemSheet
                               .First(pair => pair.Value.ItemSubType == ItemSubType.Hourglass)
                               .Value;

            inventoryValue = nextState.GetState(_inventoryAddress);
            Assert.NotNull(inventoryValue);
            inventoryState = new Inventory((List)inventoryValue);
            Assert.False(inventoryState.TryGetFungibleItems(hourglassRow.ItemId, out _));

            var diff           = slot0State.RequiredBlockIndex - GameConfig.RequiredAppraiseBlock;
            var hourglassCount = RapidCombination0.CalculateHourglassCount(gameConfigState, diff);

            inventoryState.AddFungibleItem(
                ItemFactory.CreateMaterial(_tableSheets.MaterialItemSheet, hourglassRow.Id),
                hourglassCount);
            Assert.True(inventoryState.TryGetFungibleItems(hourglassRow.ItemId, out var hourglasses));
            Assert.Equal(hourglassCount, hourglasses.Sum(e => e.count));
            nextState = nextState.SetState(_inventoryAddress, inventoryState.Serialize());

            var rapidCombinationAction = new RapidCombination
            {
                avatarAddress = _avatarAddress,
                slotIndex     = 0,
            };

            nextState = rapidCombinationAction.Execute(new ActionContext
            {
                PreviousStates = nextState,
                BlockIndex     = GameConfig.RequiredAppraiseBlock,
                Random         = random,
                Signer         = _agentAddress,
            });
            inventoryValue = nextState.GetState(_inventoryAddress);
            Assert.NotNull(inventoryValue);
            inventoryState = new Inventory((List)inventoryValue);
            Assert.False(inventoryState.TryGetFungibleItems(hourglassRow.ItemId, out _));
        }
        public ActionMutation()
        {
            Field <NonNullGraphType <BooleanGraphType> >("createAvatar",
                                                         resolve: context =>
            {
                try
                {
                    NineChroniclesNodeService service = context.Source;
                    PrivateKey privatekey             = service.PrivateKey;
                    BlockChain <NineChroniclesActionType> blockChain = service.Swarm.BlockChain;
                    Address userAddress   = privatekey.PublicKey.ToAddress();
                    Address avatarAddress = userAddress.Derive("avatar_0");

                    var action = new CreateAvatar
                    {
                        avatarAddress = avatarAddress,
                        index         = 0,
                        hair          = 0,
                        lens          = 0,
                        ear           = 0,
                        tail          = 0,
                        name          = "createbymutation",
                    };

                    var actions = new PolymorphicAction <ActionBase>[] { action };
                    blockChain.MakeTransaction(privatekey, actions);
                }
                catch (Exception e)
                {
                    var msg = $"Unexpected exception occurred during {typeof(ActionMutation)}: {e}";
                    context.Errors.Add(new ExecutionError(msg, e));
                    Log.Error(msg, e);
                    return(false);
                }

                return(true);
            });

            Field <NonNullGraphType <BooleanGraphType> >("hackAndSlash",
                                                         arguments: new QueryArguments(
                                                             new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name = "weeklyArenaAddress",
            },
                                                             new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name = "rankingArenaAddress",
            }),
                                                         resolve: context =>
            {
                try
                {
                    NineChroniclesNodeService service = context.Source;
                    PrivateKey privatekey             = service.PrivateKey;
                    BlockChain <NineChroniclesActionType> blockChain = service.Swarm.BlockChain;
                    Address userAddress         = privatekey.PublicKey.ToAddress();
                    Address avatarAddress       = userAddress.Derive("avatar_0");
                    Address weeklyArenaAddress  = new Address(context.GetArgument <string>("weeklyArenaAddress"));
                    Address rankingArenaAddress = new Address(context.GetArgument <string>("rankingArenaAddress"));

                    var action = new HackAndSlash
                    {
                        avatarAddress      = avatarAddress,
                        worldId            = 1,
                        stageId            = 1,
                        WeeklyArenaAddress = weeklyArenaAddress,
                        RankingMapAddress  = rankingArenaAddress,
                        costumes           = new List <int>(),
                        equipments         = new List <Guid>(),
                        foods = new List <Guid>(),
                    };

                    var actions = new PolymorphicAction <ActionBase>[] { action };
                    blockChain.MakeTransaction(privatekey, actions);
                }
                catch (Exception e)
                {
                    var msg = $"Unexpected exception occurred during {typeof(ActionMutation)}: {e}";
                    context.Errors.Add(new ExecutionError(msg, e));
                    Log.Error(msg, e);
                    return(false);
                }

                return(true);
            });

            Field <NonNullGraphType <BooleanGraphType> >("combinationEquipment",
                                                         arguments: new QueryArguments(
                                                             new QueryArgument <NonNullGraphType <DecimalGraphType> >
            {
                Name = "recipeId",
            },
                                                             new QueryArgument <NonNullGraphType <DecimalGraphType> >
            {
                Name = "slotIndex",
            },
                                                             new QueryArgument <DecimalGraphType>
            {
                Name = "subRecipeId",
            }),
                                                         resolve: context =>
            {
                try
                {
                    NineChroniclesNodeService service = context.Source;
                    PrivateKey privatekey             = service.PrivateKey;
                    BlockChain <NineChroniclesActionType> blockChain = service.Swarm.BlockChain;
                    Address userAddress   = privatekey.PublicKey.ToAddress();
                    Address avatarAddress = userAddress.Derive("avatar_0");
                    int recipeId          = context.GetArgument <int>("recipeId");
                    int slotIndex         = context.GetArgument <int>("slotIndex");
                    int?subRecipeId       = context.GetArgument <int>("subRecipeId");

                    var action = new CombinationEquipment
                    {
                        AvatarAddress = avatarAddress,
                        RecipeId      = recipeId,
                        SlotIndex     = slotIndex,
                        SubRecipeId   = subRecipeId
                    };

                    var actions = new PolymorphicAction <ActionBase>[] { action };
                    blockChain.MakeTransaction(privatekey, actions);
                }
                catch (Exception e)
                {
                    var msg = $"Unexpected exception occurred during {typeof(ActionMutation)}: {e}";
                    context.Errors.Add(new ExecutionError(msg, e));
                    Log.Error(msg, e);
                    return(false);
                }

                return(true);
            });

            Field <NonNullGraphType <BooleanGraphType> >("itemEnhancement",
                                                         arguments: new QueryArguments(
                                                             new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name = "itemId",
            },
                                                             new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name = "materialIds",
            }),
                                                         resolve: context =>
            {
                try
                {
                    NineChroniclesNodeService service = context.Source;
                    PrivateKey privatekey             = service.PrivateKey;
                    BlockChain <NineChroniclesActionType> blockChain = service.Swarm.BlockChain;
                    Address userAddress   = privatekey.PublicKey.ToAddress();
                    Address avatarAddress = userAddress.Derive("avatar_0");
                    Guid itemId           = Guid.Parse(context.GetArgument <string>("itemId"));
                    Guid materialId       = Guid.Parse(context.GetArgument <string>("materialIds"));

                    var action = new ItemEnhancement
                    {
                        avatarAddress = avatarAddress,
                        slotIndex     = 0,
                        itemId        = itemId,
                        materialIds   = new[] { materialId }
                    };

                    var actions = new PolymorphicAction <ActionBase>[] { action };
                    blockChain.MakeTransaction(privatekey, actions);
                }
                catch (Exception e)
                {
                    var msg = $"Unexpected exception occurred during {typeof(ActionMutation)}: {e}";
                    context.Errors.Add(new ExecutionError(msg, e));
                    Log.Error(msg, e);
                    return(false);
                }

                return(true);
            });

            Field <NonNullGraphType <BooleanGraphType> >("buy",
                                                         arguments: new QueryArguments(
                                                             new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name = "sellerAgentAddress",
            },
                                                             new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name = "sellerAvatarAddress",
            },
                                                             new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name = "productId",
            }),
                                                         resolve: context =>
            {
                try
                {
                    NineChroniclesNodeService service = context.Source;
                    PrivateKey privatekey             = service.PrivateKey;
                    BlockChain <NineChroniclesActionType> blockChain = service.Swarm.BlockChain;
                    Address userAddress         = privatekey.PublicKey.ToAddress();
                    Address avatarAddress       = userAddress.Derive("avatar_0");
                    Address sellerAgentAddress  = new Address(context.GetArgument <string>("sellerAgentAddress"));
                    Address sellerAvatarAddress = new Address(context.GetArgument <string>("sellerAvatarAddress"));
                    Guid productId = Guid.Parse(context.GetArgument <string>("productId"));

                    var action = new Buy
                    {
                        buyerAvatarAddress  = avatarAddress,
                        sellerAgentAddress  = sellerAgentAddress,
                        sellerAvatarAddress = sellerAvatarAddress,
                        productId           = productId,
                    };

                    var actions = new PolymorphicAction <ActionBase>[] { action };
                    blockChain.MakeTransaction(privatekey, actions);
                }
                catch (Exception e)
                {
                    var msg = $"Unexpected exception occurred during {typeof(ActionMutation)}: {e}";
                    context.Errors.Add(new ExecutionError(msg, e));
                    Log.Error(msg, e);
                    return(false);
                }

                return(true);
            });
            Field <NonNullGraphType <BooleanGraphType> >("sell",
                                                         arguments: new QueryArguments(
                                                             new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name = "sellerAvatarAddress",
            },
                                                             new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name = "productId",
            },
                                                             new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name = "itemId",
            },
                                                             new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name = "price",
            }),
                                                         resolve: context =>
            {
                try
                {
                    NineChroniclesNodeService service = context.Source;
                    PrivateKey privatekey             = service.PrivateKey;
                    BlockChain <NineChroniclesActionType> blockChain = service.Swarm.BlockChain;
                    Address sellerAvatarAddress = new Address(context.GetArgument <string>("sellerAvatarAddress"));
                    Guid itemId  = Guid.Parse(context.GetArgument <string>("itemId"));
                    var currency = new GoldCurrencyState(
                        (Dictionary)blockChain.GetState(GoldCurrencyState.Address)
                        ).Currency;
                    FungibleAssetValue price =
                        FungibleAssetValue.Parse(currency, context.GetArgument <string>("price"));

                    var action = new Sell
                    {
                        sellerAvatarAddress = sellerAvatarAddress,
                        itemId = itemId,
                        price  = price
                    };

                    var actions = new PolymorphicAction <ActionBase>[] { action };
                    blockChain.MakeTransaction(privatekey, actions);
                }
                catch (Exception e)
                {
                    var msg = $"Unexpected exception occurred during {typeof(ActionMutation)}: {e}";
                    context.Errors.Add(new ExecutionError(msg, e));
                    Log.Error(msg, e);
                    return(false);
                }

                return(true);
            });

            Field <NonNullGraphType <BooleanGraphType> >("dailyReward",
                                                         resolve: context =>
            {
                try
                {
                    NineChroniclesNodeService service = context.Source;
                    PrivateKey privatekey             = service.PrivateKey;
                    BlockChain <NineChroniclesActionType> blockChain = service.Swarm.BlockChain;
                    Address userAddress   = privatekey.PublicKey.ToAddress();
                    Address avatarAddress = userAddress.Derive("avatar_0");

                    var action = new DailyReward
                    {
                        avatarAddress = avatarAddress
                    };

                    var actions = new PolymorphicAction <ActionBase>[] { action };
                    blockChain.MakeTransaction(privatekey, actions);
                }
                catch (Exception e)
                {
                    var msg = $"Unexpected exception occurred during {typeof(ActionMutation)}: {e}";
                    context.Errors.Add(new ExecutionError(msg, e));
                    Log.Error(msg, e);
                    return(false);
                }

                return(true);
            });
        }