public override IAccountStateDelta Execute(IActionContext context) { var states = context.PreviousStates; if (context.Rehearsal) { return(states.SetState(avatarAddress, MarkChanged)); } if (!states.TryGetAgentAvatarStates(context.Signer, avatarAddress, out var _, out var avatarState)) { return(states); } var tableSheets = TableSheets.FromActionContext(context); var row = tableSheets.MaterialItemSheet.Values.FirstOrDefault(r => r.ItemSubType == ItemSubType.ApStone); var apStone = ItemFactory.CreateMaterial(row); if (!avatarState.inventory.RemoveFungibleItem(apStone)) { Log.Error($"Not enough item {apStone}"); return(states); } var gameConfigState = states.GetGameConfigState(); if (gameConfigState is null) { return(states); } avatarState.actionPoint = gameConfigState.ActionPointMax; return(states.SetState(avatarAddress, avatarState.Serialize())); }
public override IAccountStateDelta Execute(IActionContext context) { var states = context.PreviousStates; if (context.Rehearsal) { states = states.SetState(RedeemCodeState.Address, MarkChanged); states = states.SetState(avatarAddress, MarkChanged); states = states.SetState(context.Signer, MarkChanged); return(states); } if (!states.TryGetAgentAvatarStates(context.Signer, avatarAddress, out AgentState agentState, out AvatarState avatarState)) { return(states); } var redeemState = states.GetRedeemCodeState(); if (redeemState is null) { return(states); } int redeemId; try { redeemId = redeemState.Redeem(code, avatarAddress); } catch (InvalidRedeemCodeException) { Log.Error("Invalid Code"); throw; } catch (DuplicateRedeemException e) { Log.Warning(e.Message); throw; } var tableSheets = TableSheets.FromActionContext(context); var row = tableSheets.RedeemRewardSheet.Values.First(r => r.Id == redeemId); var rewards = row.Rewards; var materialRow = tableSheets.MaterialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Chest); var chest = ItemFactory.CreateChest(materialRow, rewards); avatarState.inventory.AddItem(chest, 1); states = states.SetState(avatarAddress, avatarState.Serialize()); states = states.SetState(RedeemCodeState.Address, redeemState.Serialize()); states = states.SetState(context.Signer, agentState.Serialize()); return(states); }
public void UpdateFromQuestReward(Quest.Quest quest, IActionContext context) { var items = new List <Material>(); foreach (var pair in quest.Reward.ItemMap) { var row = TableSheets.FromActionContext(context) .MaterialItemSheet.Values.First(itemRow => itemRow.Id == pair.Key); var item = ItemFactory.CreateMaterial(row); var map = inventory.AddItem(item, pair.Value); itemMap.Add(map); items.Add(item); } quest.IsPaidInAction = true; questList.UpdateCollectQuest(itemMap); questList.UpdateItemTypeCollectQuest(items); UpdateCompletedQuest(); }
private static AvatarState CreateAvatarState(string name, Address avatarAddress, IActionContext ctx) { var tableSheets = TableSheets.FromActionContext(ctx); var gameConfigState = ctx.PreviousStates.GetGameConfigState(); var avatarState = new AvatarState( avatarAddress, ctx.Signer, ctx.BlockIndex, tableSheets, gameConfigState, name ); if (GameConfig.IsEditor) { AddItemsForTest(avatarState, ctx.Random, tableSheets); } return(avatarState); }
public override IAccountStateDelta Execute(IActionContext context) { var states = context.PreviousStates; if (context.Rehearsal) { states = states.SetState(RedeemCodeState.Address, MarkChanged); states = states.SetState(AvatarAddress, MarkChanged); states = states.SetState(context.Signer, MarkChanged); states = states.MarkBalanceChanged(GoldCurrencyMock, GoldCurrencyState.Address); states = states.MarkBalanceChanged(GoldCurrencyMock, context.Signer); return(states); } if (!states.TryGetAgentAvatarStates(context.Signer, AvatarAddress, out AgentState agentState, out AvatarState avatarState)) { return(states); } var redeemState = states.GetRedeemCodeState(); if (redeemState is null) { return(states); } int redeemId; try { redeemId = redeemState.Redeem(Code, AvatarAddress); } catch (InvalidRedeemCodeException) { Log.Error("Invalid Code"); throw; } catch (DuplicateRedeemException e) { Log.Warning(e.Message); throw; } var tableSheets = TableSheets.FromActionContext(context); var row = tableSheets.RedeemRewardSheet.Values.First(r => r.Id == redeemId); var rewards = row.Rewards; var itemSheets = tableSheets.ItemSheet; foreach (RedeemRewardSheet.RewardInfo info in row.Rewards) { switch (info.Type) { case RewardType.Item: for (var i = 0; i < info.Quantity; i++) { if (info.ItemId is int itemId) { ItemBase item = ItemFactory.CreateItem(itemSheets[itemId]); // We should fix count as 1 because ItemFactory.CreateItem // will create a new item every time. avatarState.inventory.AddItem(item, 1); } } states = states.SetState(AvatarAddress, avatarState.Serialize()); break; case RewardType.Gold: states = states.TransferAsset( GoldCurrencyState.Address, context.Signer, states.GetGoldCurrency() * info.Quantity ); break; default: // FIXME: We should raise exception here. break; } } states = states.SetState(RedeemCodeState.Address, redeemState.Serialize()); states = states.SetState(context.Signer, agentState.Serialize()); return(states); }
public override IAccountStateDelta Execute(IActionContext context) { IActionContext ctx = context; var states = ctx.PreviousStates; var slotAddress = AvatarAddress.Derive( string.Format( CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, slotIndex ) ); if (ctx.Rehearsal) { return(states .SetState(AvatarAddress, MarkChanged) .SetState(ctx.Signer, MarkChanged) .SetState(slotAddress, MarkChanged)); } var sw = new Stopwatch(); sw.Start(); var started = DateTimeOffset.UtcNow; Log.Debug("Combination exec started."); if (!states.TryGetAgentAvatarStates(ctx.Signer, AvatarAddress, out AgentState agentState, out AvatarState avatarState)) { return(LogError(context, "Aborted as the avatar state of the signer was failed to load.")); } sw.Stop(); Log.Debug("Combination Get AgentAvatarStates: {Elapsed}", sw.Elapsed); sw.Restart(); if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world)) { return(LogError(context, "Aborted as the WorldInformation was failed to load.")); } if (world.StageClearedId < GameConfig.RequireClearedStageLevel.CombinationEquipmentAction) { // 스테이지 클리어 부족 에러. return(LogError( context, "Aborted as the signer is not cleared the minimum stage level required to combine consumables yet: {ClearedLevel} < {RequiredLevel}.", world.StageClearedId, GameConfig.RequireClearedStageLevel.CombinationEquipmentAction )); } var slotState = states.GetCombinationSlotState(AvatarAddress, slotIndex); if (slotState is null || !(slotState.Validate(avatarState, ctx.BlockIndex))) { return(LogError( context, "Aborted as the slot state is failed to load or invalid: {@SlotState} @ {SlotIndex}", slotState, slotIndex )); } var tableSheets = TableSheets.FromActionContext(ctx); sw.Stop(); Log.Debug("Combination Get TableSheetsState: {Elapsed}", sw.Elapsed); sw.Restart(); Log.Debug("Execute Combination; player: {Player}", AvatarAddress); var consumableItemSheet = tableSheets.ConsumableItemSheet; var recipeRow = tableSheets.ConsumableItemRecipeSheet.Values.FirstOrDefault(r => r.Id == recipeId); if (recipeRow is null) { return(LogError(context, "Aborted as the recipe was failed to load.")); } var materials = new Dictionary <Material, int>(); foreach (var materialInfo in recipeRow.Materials) { var materialId = materialInfo.Id; var count = materialInfo.Count; if (avatarState.inventory.HasItem(materialId, count)) { avatarState.inventory.TryGetFungibleItem(materialId, out var inventoryItem); var material = (Material)inventoryItem.item; materials[material] = count; avatarState.inventory.RemoveFungibleItem(material, count); } else { return(LogError( context, "Aborted as the player has no enough material ({Material} * {Quantity})", materialId, count )); } } sw.Stop(); Log.Debug("Combination Remove Materials: {Elapsed}", sw.Elapsed); sw.Restart(); var result = new ResultModel { materials = materials, itemType = ItemType.Consumable, }; var costAP = recipeRow.RequiredActionPoint; if (avatarState.actionPoint < costAP) { // ap 부족 에러. return(LogError( context, "Aborted due to insufficient action point: {ActionPointBalance} < {ActionCost}", avatarState.actionPoint, costAP )); } // ap 차감. avatarState.actionPoint -= costAP; result.actionPoint = costAP; var resultConsumableItemId = recipeRow.ResultConsumableItemId; sw.Stop(); Log.Debug("Combination Get Food id: {Elapsed}", sw.Elapsed); sw.Restart(); result.recipeId = recipeRow.Id; if (!consumableItemSheet.TryGetValue(resultConsumableItemId, out var consumableItemRow)) { // 소모품 테이블 값 가져오기 실패. return(LogError( context, "Aborted as the consumable item ({ItemId} was failed to load from the data table.", resultConsumableItemId )); } // 조합 결과 획득. var requiredBlockIndex = ctx.BlockIndex + recipeRow.RequiredBlockIndex; var itemId = ctx.Random.GenerateRandomGuid(); var itemUsable = GetFood(consumableItemRow, itemId, requiredBlockIndex); // 액션 결과 result.itemUsable = itemUsable; var mail = new CombinationMail( result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), requiredBlockIndex ); result.id = mail.id; avatarState.Update(mail); avatarState.UpdateFromCombination(itemUsable); sw.Stop(); Log.Debug("Combination Update AvatarState: {Elapsed}", sw.Elapsed); sw.Restart(); avatarState.UpdateQuestRewards(ctx); avatarState.updatedAt = DateTimeOffset.UtcNow; avatarState.blockIndex = ctx.BlockIndex; states = states.SetState(AvatarAddress, avatarState.Serialize()); slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); sw.Stop(); Log.Debug("Combination Set AvatarState: {Elapsed}", sw.Elapsed); var ended = DateTimeOffset.UtcNow; Log.Debug("Combination Total Executed Time: {Elapsed}", ended - started); return(states .SetState(ctx.Signer, agentState.Serialize()) .SetState(slotAddress, slotState.Serialize())); }
public override IAccountStateDelta Execute(IActionContext context) { IActionContext ctx = context; var states = ctx.PreviousStates; var slotAddress = AvatarAddress.Derive( string.Format( CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, SlotIndex ) ); if (ctx.Rehearsal) { return(states .SetState(AvatarAddress, MarkChanged) .SetState(slotAddress, MarkChanged) .SetState(ctx.Signer, MarkChanged) .MarkBalanceChanged(GoldCurrencyMock, ctx.Signer, BlacksmithAddress)); } if (!states.TryGetAgentAvatarStates(ctx.Signer, AvatarAddress, out var agentState, out var avatarState)) { return(LogError(context, "Aborted as the avatar state of the signer was failed to load.")); } var slotState = states.GetCombinationSlotState(AvatarAddress, SlotIndex); if (slotState is null || !(slotState.Validate(avatarState, ctx.BlockIndex))) { return(LogError( context, "Aborted as the slot state is failed to load or invalid: {@SlotState} @ {SlotIndex}", slotState, SlotIndex )); } var tableSheets = TableSheets.FromActionContext(ctx); var recipeSheet = tableSheets.EquipmentItemRecipeSheet; var materialSheet = tableSheets.MaterialItemSheet; var materials = new Dictionary <Material, int>(); // 레시피 검증 if (!recipeSheet.TryGetValue(RecipeId, out var recipe)) { return(LogError( context, "Aborted as the recipe {RecipeId} was failed to load from the sheet.", RecipeId )); } if (!(SubRecipeId is null)) { if (!recipe.SubRecipeIds.Contains((int)SubRecipeId)) { return(LogError( context, "Aborted as the subrecipe {SubRecipeId} was failed to load from the sheet.", SubRecipeId )); } } // 메인 레시피 해금 검사. if (!avatarState.worldInformation.IsStageCleared(recipe.UnlockStage)) { return(LogError( context, "Aborted as the signer is not cleared the minimum stage level required to use the recipe {@Recipe} yet.", recipe )); } if (!materialSheet.TryGetValue(recipe.MaterialId, out var material)) { return(LogError( context, "Aborted as the material {MaterialId} was failed to load from the sheet.", recipe.MaterialId )); } if (!avatarState.inventory.RemoveMaterial(material.ItemId, recipe.MaterialCount)) { return(LogError( context, "Aborted as the player has no enough material ({Material} * {Quantity})", material, recipe.MaterialCount )); } var equipmentMaterial = ItemFactory.CreateMaterial(materialSheet, material.Id); materials[equipmentMaterial] = recipe.MaterialCount; BigInteger requiredGold = recipe.RequiredGold; var requiredActionPoint = recipe.RequiredActionPoint; // 장비 제작 if (!tableSheets.EquipmentItemSheet.TryGetValue(recipe.ResultEquipmentId, out var equipRow)) { return(LogError( context, "Aborted as the equipment item {EquipmentId} was failed to load from the sheet.", recipe.ResultEquipmentId )); } var requiredBlockIndex = ctx.BlockIndex + recipe.RequiredBlockIndex; var equipment = (Equipment)ItemFactory.CreateItemUsable( equipRow, ctx.Random.GenerateRandomGuid(), requiredBlockIndex ); // 서브 레시피 검증 HashSet <int> optionIds = null; if (SubRecipeId.HasValue) { var subSheet = tableSheets.EquipmentItemSubRecipeSheet; if (!subSheet.TryGetValue((int)SubRecipeId, out var subRecipe)) { return(LogError( context, "Aborted as the subrecipe {SubRecipeId} was failed to load from the subsheet.", SubRecipeId )); } // 서브 레시피 해금 검사. if (!avatarState.worldInformation.IsStageCleared(subRecipe.UnlockStage)) { return(LogError( context, "Aborted as the signer is not cleared the minimum stage level required to use the subrecipe {@SubRecipe} yet.", subRecipe )); } requiredBlockIndex += subRecipe.RequiredBlockIndex; requiredGold += subRecipe.RequiredGold; requiredActionPoint += subRecipe.RequiredActionPoint; foreach (var materialInfo in subRecipe.Materials) { if (!materialSheet.TryGetValue(materialInfo.Id, out var subMaterialRow)) { return(LogError( context, "Aborted as the meterial info {MaterialInfoId} was failed to load from the submaterial sheet.", materialInfo.Id )); } if (!avatarState.inventory.RemoveMaterial(subMaterialRow.ItemId, materialInfo.Count)) { return(LogError( context, "Aborted as the player has no enough material ({Material} * {Quantity})", subMaterialRow, materialInfo.Count )); } var subMaterial = ItemFactory.CreateMaterial(materialSheet, materialInfo.Id); materials[subMaterial] = materialInfo.Count; } optionIds = SelectOption(tableSheets, subRecipe, ctx.Random, equipment); equipment.Update(requiredBlockIndex); } // 자원 검증 BigInteger agentBalance = states.GetBalance(ctx.Signer, states.GetGoldCurrency()); if (agentBalance < requiredGold || avatarState.actionPoint < requiredActionPoint) { return(LogError( context, "Aborted due to insufficient action point: {ActionPointBalance} < {ActionCost}", avatarState.actionPoint, requiredActionPoint )); } avatarState.actionPoint -= requiredActionPoint; if (!(optionIds is null)) { foreach (var id in optionIds) { agentState.unlockedOptions.Add(id); } } // FIXME: BlacksmithAddress 계좌로 돈이 쌓이기만 하는데 이걸 어떻게 순환시킬지 기획이 필요. if (requiredGold > 0) { states = states.TransferAsset(ctx.Signer, BlacksmithAddress, states.GetGoldCurrency(), requiredGold); } var result = new CombinationConsumable.ResultModel { actionPoint = requiredActionPoint, gold = requiredGold, materials = materials, itemUsable = equipment, recipeId = RecipeId, subRecipeId = SubRecipeId, itemType = ItemType.Equipment, }; slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); var mail = new CombinationMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), requiredBlockIndex); result.id = mail.id; avatarState.Update(mail); avatarState.questList.UpdateCombinationEquipmentQuest(RecipeId, SubRecipeId); avatarState.UpdateFromCombination(equipment); avatarState.UpdateQuestRewards(ctx); return(states .SetState(AvatarAddress, avatarState.Serialize()) .SetState(slotAddress, slotState.Serialize()) .SetState(ctx.Signer, agentState.Serialize())); }
public override IAccountStateDelta Execute(IActionContext context) { var states = context.PreviousStates; if (context.Rehearsal) { states = states .SetState(context.Signer, MarkChanged) .SetState(avatarAddress, MarkChanged) .MarkBalanceChanged(GoldCurrencyMock, GoldCurrencyState.Address, context.Signer); return(states); } if (!states.TryGetAgentAvatarStates(context.Signer, avatarAddress, out AgentState _, out AvatarState avatarState)) { // FIXME: 오류 처리 필요하지 않나요? } var tableSheets = TableSheets.FromActionContext(context); foreach (var pair in chestList) { var itemId = pair.Key; var count = pair.Value; if (avatarState.inventory.TryGetMaterial(itemId, out var inventoryItem) && inventoryItem.count >= count) { var chest = (Chest)inventoryItem.item; foreach (var info in chest.Rewards) { switch (info.Type) { case RewardType.Item: var itemRow = tableSheets.MaterialItemSheet.Values.FirstOrDefault(r => r.Id == info.ItemId); if (itemRow is null) { continue; } var material = ItemFactory.CreateMaterial(itemRow); avatarState.inventory.AddItem(material, info.Quantity); break; case RewardType.Gold: states = states.TransferAsset( GoldCurrencyState.Address, context.Signer, states.GetGoldCurrency() * info.Quantity ); break; default: throw new ArgumentOutOfRangeException(nameof(info.Type), info.Type, null); } } avatarState.inventory.RemoveMaterial(itemId, count); } } states = states.SetState(avatarAddress, avatarState.Serialize()); return(states); }
public override IAccountStateDelta Execute(IActionContext context) { IActionContext ctx = context; var states = ctx.PreviousStates; var slotAddress = avatarAddress.Derive( string.Format( CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, slotIndex ) ); if (ctx.Rehearsal) { return(states .MarkBalanceChanged(GoldCurrencyMock, BlacksmithAddress) .SetState(avatarAddress, MarkChanged) .SetState(slotAddress, MarkChanged)); } var sw = new Stopwatch(); sw.Start(); var started = DateTimeOffset.UtcNow; Log.Debug("ItemEnhancement exec started."); if (!states.TryGetAgentAvatarStates(ctx.Signer, avatarAddress, out AgentState agentState, out AvatarState avatarState)) { return(LogError(context, "Aborted as the avatar state of the signer was failed to load.")); } sw.Stop(); Log.Debug("ItemEnhancement Get AgentAvatarStates: {Elapsed}", sw.Elapsed); sw.Restart(); if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out ItemUsable enhancementItem)) { // 강화 장비가 없는 에러. return(LogError( context, "Aborted as the NonFungibleItem ({ItemId}) was failed to load from avatar's inventory.", itemId )); } if (enhancementItem.RequiredBlockIndex > context.BlockIndex) { return(LogError( context, "Aborted as the equipment to enhance ({ItemId}) is not available yet; it will be available at the block #{RequiredBlockIndex}.", itemId, enhancementItem.RequiredBlockIndex )); } if (!(enhancementItem is Equipment enhancementEquipment)) { // 캐스팅 버그. 예외상황. return(LogError( context, $"Aborted as the item is not a {nameof(Equipment)}, but {{ItemType}}.", enhancementItem.GetType().Name )); } var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); if (slotState is null || !(slotState.Validate(avatarState, ctx.BlockIndex))) { return(LogError(context, "Aborted as the slot state was failed to load or invalid.")); } sw.Stop(); Log.Debug("ItemEnhancement Get Equipment: {Elapsed}", sw.Elapsed); sw.Restart(); if (enhancementEquipment.level > 9) { // 최대 강화도 초과 에러. return(LogError( context, "Aborted due to invaild equipment level: {EquipmentLevel} < 9", enhancementEquipment.level )); } var result = new ResultModel { itemUsable = enhancementEquipment, materialItemIdList = materialIds }; var requiredAP = GetRequiredAp(); if (avatarState.actionPoint < requiredAP) { // AP 부족 에러. return(LogError( context, "Aborted due to insufficient action point: {ActionPointBalance} < {ActionCost}", avatarState.actionPoint, requiredAP )); } var tableSheets = TableSheets.FromActionContext(ctx); var requiredNCG = GetRequiredNCG(tableSheets, enhancementEquipment.Grade, enhancementEquipment.level + 1); avatarState.actionPoint -= requiredAP; result.actionPoint = requiredAP; if (requiredNCG > 0) { states = states.TransferAsset(ctx.Signer, BlacksmithAddress, states.GetGoldCurrency(), requiredNCG); } sw.Stop(); Log.Debug("ItemEnhancement Get TableSheets: {Elapsed}", sw.Elapsed); sw.Restart(); var materials = new List <Equipment>(); foreach (var materialId in materialIds) { if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out ItemUsable materialItem)) { // 인벤토리에 재료로 등록한 장비가 없는 에러. return(LogError( context, "Aborted as the the signer does not have a necessary material ({MaterialId}).", materialId )); } if (materialItem.RequiredBlockIndex > context.BlockIndex) { return(LogError( context, "Aborted as the material ({MaterialId}) is not available yet; it will be available at the block #{RequiredBlockIndex}.", materialId, materialItem.RequiredBlockIndex )); } if (!(materialItem is Equipment materialEquipment)) { return(LogError( context, $"Aborted as the material item is not a {nameof(Equipment)}, but {{ItemType}}.", materialItem.GetType().Name )); } if (materials.Contains(materialEquipment)) { // 같은 guid의 아이템이 중복해서 등록된 에러. return(LogError( context, "Aborted as the same material was used more than once: {Material}", materialEquipment )); } if (enhancementEquipment.ItemId == materialId) { // 강화 장비와 재료로 등록한 장비가 같은 에러. return(LogError( context, "Aborted as an equipment to enhance ({ItemId}) was used as a material too.", materialId )); } if (materialEquipment.ItemSubType != enhancementEquipment.ItemSubType) { // 서브 타입이 다른 에러. return(LogError( context, "Aborted as the material item is not a {ExpectedItemSubType}, but {MaterialSubType}.", enhancementEquipment.ItemSubType, materialEquipment.ItemSubType )); } if (materialEquipment.Grade != enhancementEquipment.Grade) { // 등급이 다른 에러. return(LogError( context, "Aborted as grades of the equipment to enhance ({EquipmentGrade}) and a material ({MaterialGrade}) do not match.", enhancementEquipment.Grade, materialEquipment.Grade )); } if (materialEquipment.level != enhancementEquipment.level) { // 강화도가 다른 에러. return(LogError( context, "Aborted as levels of the equipment to enhance ({EquipmentLevel}) and a material ({MaterialLevel}) do not match.", enhancementEquipment.level, materialEquipment.level )); } sw.Stop(); Log.Debug("ItemEnhancement Get Material: {Elapsed}", sw.Elapsed); sw.Restart(); materialEquipment.Unequip(); materials.Add(materialEquipment); } enhancementEquipment.Unequip(); enhancementEquipment = UpgradeEquipment(enhancementEquipment); sw.Stop(); Log.Debug("ItemEnhancement Upgrade Equipment: {Elapsed}", sw.Elapsed); sw.Restart(); result.gold = 0; foreach (var material in materials) { avatarState.inventory.RemoveNonFungibleItem(material); } sw.Stop(); Log.Debug("ItemEnhancement Remove Materials: {Elapsed}", sw.Elapsed); sw.Restart(); var mail = new ItemEnhanceMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex); result.id = mail.id; avatarState.inventory.RemoveNonFungibleItem(enhancementEquipment); avatarState.Update(mail); avatarState.UpdateFromItemEnhancement(enhancementEquipment); avatarState.UpdateQuestRewards(ctx); slotState.Update(result, ctx.BlockIndex, ctx.BlockIndex); sw.Stop(); Log.Debug("ItemEnhancement Update AvatarState: {Elapsed}", sw.Elapsed); sw.Restart(); states = states.SetState(avatarAddress, avatarState.Serialize()); sw.Stop(); Log.Debug("ItemEnhancement Set AvatarState: {Elapsed}", sw.Elapsed); var ended = DateTimeOffset.UtcNow; Log.Debug("ItemEnhancement Total Executed Time: {Elapsed}", ended - started); return(states.SetState(slotAddress, slotState.Serialize())); }
public override IAccountStateDelta Execute(IActionContext context) { var states = context.PreviousStates; var slotAddress = avatarAddress.Derive( string.Format( CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, slotIndex ) ); if (context.Rehearsal) { return(states .SetState(avatarAddress, MarkChanged) .SetState(slotAddress, MarkChanged)); } if (!states.TryGetAgentAvatarStates(context.Signer, avatarAddress, out var agentState, out var avatarState)) { return(states); } var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); if (slotState?.Result is null) { Log.Warning("CombinationSlot Result is null."); return(states); } if (slotState.UnlockBlockIndex <= context.BlockIndex) { Log.Warning($"Can't use combination slot. it unlock on {slotState.UnlockBlockIndex} block."); return(states); } var gameConfigState = states.GetGameConfigState(); if (gameConfigState is null) { return(states); } var diff = slotState.Result.itemUsable.RequiredBlockIndex - context.BlockIndex; if (diff < 0) { Log.Information("Skip rapid combination."); return(states); } var count = CalculateHourglassCount(gameConfigState, diff); var tableSheets = TableSheets.FromActionContext(context); var row = tableSheets.MaterialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Hourglass); var hourGlass = ItemFactory.CreateMaterial(row); if (!avatarState.inventory.RemoveFungibleItem(hourGlass, count)) { Log.Error($"Not enough item {hourGlass} : {count}"); return(states); } slotState.Update(context.BlockIndex, hourGlass, count); avatarState.UpdateFromRapidCombination( ((CombinationConsumable.ResultModel)slotState.Result), context.BlockIndex ); return(states .SetState(avatarAddress, avatarState.Serialize()) .SetState(slotAddress, slotState.Serialize())); }