public static bool TryGetMail( this CombinationSlotState state, long blockIndex, long requiredBlockIndex, out CombinationMail combinationMail, out ItemEnhanceMail itemEnhanceMail) { combinationMail = null; itemEnhanceMail = null; if (!state.TryGetResultId(out var resultId)) { return(false); } switch (state.Result) { case ItemEnhancement.ResultModel r: itemEnhanceMail = new ItemEnhanceMail( r, blockIndex, resultId, requiredBlockIndex); return(true); case ItemEnhancement7.ResultModel r: itemEnhanceMail = new ItemEnhanceMail( r, blockIndex, resultId, requiredBlockIndex); return(true); case CombinationConsumable5.ResultModel r: combinationMail = new CombinationMail( r, blockIndex, resultId, requiredBlockIndex); return(true); default: return(false); } }
public void Read(ItemEnhanceMail itemEnhanceMail) { var avatarAddress = States.Instance.CurrentAvatarState.address; var attachment = (ItemEnhancement.ResultModel)itemEnhanceMail.attachment; var popup = Find <CombinationResultPopup>(); var itemBase = attachment.itemUsable ?? (ItemBase)attachment.costume; var nonFungibleItem = attachment.itemUsable ?? (INonFungibleItem)attachment.costume; var model = new UI.Model.CombinationResultPopup(new CountableItem(itemBase, 1)) { isSuccess = true, materialItems = new List <CombinationMaterial>() }; model.OnClickSubmit.Subscribe(_ => { LocalLayerModifier.AddItem(avatarAddress, nonFungibleItem.ItemId, false); LocalLayerModifier.RemoveNewAttachmentMail(avatarAddress, itemEnhanceMail.id); }); popup.Pop(model); }
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, ctx.Signer, 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)) { throw new FailedLoadStateException("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)) { throw new ItemDoesNotExistException( $"Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory." ); } if (enhancementItem.RequiredBlockIndex > context.BlockIndex) { throw new RequiredBlockIndexException( $"Aborted as the equipment to enhance ({itemId}) is not available yet; it will be available at the block #{enhancementItem.RequiredBlockIndex}." ); } if (!(enhancementItem is Equipment enhancementEquipment)) { throw new InvalidCastException( $"Aborted as the item is not a {nameof(Equipment)}, but {enhancementItem.GetType().Name}." ); } var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); if (slotState is null) { throw new FailedLoadStateException($"Aborted as the slot state was failed to load. #{slotIndex}"); } if (!slotState.Validate(avatarState, ctx.BlockIndex)) { throw new CombinationSlotUnlockException($"Aborted as the slot state was failed to invalid. #{slotIndex}"); } sw.Stop(); Log.Debug("ItemEnhancement Get Equipment: {Elapsed}", sw.Elapsed); sw.Restart(); if (enhancementEquipment.level > 9) { // Maximum level exceeded. throw new EquipmentLevelExceededException( $"Aborted due to invalid equipment level: {enhancementEquipment.level} < 9" ); } var result = new ItemEnhancement.ResultModel { itemUsable = enhancementEquipment, materialItemIdList = new[] { materialId } }; var requiredAP = ItemEnhancement.GetRequiredAp(); if (avatarState.actionPoint < requiredAP) { throw new NotEnoughActionPointException( $"Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredAP}" ); } var enhancementCostSheet = states.GetSheet <EnhancementCostSheet>(); var requiredNCG = ItemEnhancement.GetRequiredNCG(enhancementCostSheet, enhancementEquipment.Grade, enhancementEquipment.level + 1); avatarState.actionPoint -= requiredAP; result.actionPoint = requiredAP; if (requiredNCG > 0) { states = states.TransferAsset( ctx.Signer, BlacksmithAddress, states.GetGoldCurrency() * requiredNCG ); } if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out ItemUsable materialItem)) { throw new NotEnoughMaterialException( $"Aborted as the signer does not have a necessary material ({materialId})." ); } if (materialItem.RequiredBlockIndex > context.BlockIndex) { throw new RequiredBlockIndexException( $"Aborted as the material ({materialId}) is not available yet; it will be available at the block #{materialItem.RequiredBlockIndex}." ); } if (!(materialItem is Equipment materialEquipment)) { throw new InvalidCastException( $"Aborted as the material item is not an {nameof(Equipment)}, but {materialItem.GetType().Name}." ); } if (enhancementEquipment.ItemId == materialId) { throw new InvalidMaterialException( $"Aborted as an equipment to enhance ({materialId}) was used as a material too." ); } if (materialEquipment.ItemSubType != enhancementEquipment.ItemSubType) { // Invalid ItemSubType throw new InvalidMaterialException( $"Aborted as the material item is not a {enhancementEquipment.ItemSubType}, but {materialEquipment.ItemSubType}." ); } if (materialEquipment.Grade != enhancementEquipment.Grade) { // Invalid Grade throw new InvalidMaterialException( $"Aborted as grades of the equipment to enhance ({enhancementEquipment.Grade}) and a material ({materialEquipment.Grade}) does not match." ); } if (materialEquipment.level != enhancementEquipment.level) { // Invalid level throw new InvalidMaterialException( $"Aborted as levels of the equipment to enhance ({enhancementEquipment.level}) and a material ({materialEquipment.level}) does not match." ); } sw.Stop(); Log.Debug("ItemEnhancement Get Material: {Elapsed}", sw.Elapsed); sw.Restart(); materialEquipment.Unequip(); enhancementEquipment.Unequip(); enhancementEquipment = ItemEnhancement.UpgradeEquipment(enhancementEquipment); var requiredBlockIndex = ctx.BlockIndex + RequiredBlockCount; enhancementEquipment.Update(requiredBlockIndex); sw.Stop(); Log.Debug("ItemEnhancement Upgrade Equipment: {Elapsed}", sw.Elapsed); sw.Restart(); result.gold = requiredNCG; avatarState.inventory.RemoveNonFungibleItem(materialId); sw.Stop(); Log.Debug("ItemEnhancement Remove Materials: {Elapsed}", sw.Elapsed); sw.Restart(); var mail = new ItemEnhanceMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), requiredBlockIndex); result.id = mail.id; avatarState.inventory.RemoveNonFungibleItem(enhancementEquipment); avatarState.UpdateV2(mail); avatarState.UpdateFromItemEnhancement(enhancementEquipment); var materialSheet = states.GetSheet <MaterialItemSheet>(); avatarState.UpdateQuestRewards(materialSheet); slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); 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 ctx = context; var states = ctx.PreviousStates; var slotAddress = avatarAddress.Derive( string.Format( CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, slotIndex ) ); var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); var questListAddress = avatarAddress.Derive(LegacyQuestListKey); if (ctx.Rehearsal) { return(states .MarkBalanceChanged(GoldCurrencyMock, ctx.Signer, BlacksmithAddress) .SetState(avatarAddress, MarkChanged) .SetState(inventoryAddress, MarkChanged) .SetState(worldInformationAddress, MarkChanged) .SetState(questListAddress, MarkChanged) .SetState(slotAddress, MarkChanged)); } var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); var sw = new Stopwatch(); sw.Start(); var started = DateTimeOffset.UtcNow; Log.Verbose("{AddressesHex}ItemEnhancement exec started", addressesHex); if (!states.TryGetAgentAvatarStatesV2(ctx.Signer, avatarAddress, out var agentState, out var avatarState)) { throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); } if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out ItemUsable enhancementItem)) { throw new ItemDoesNotExistException( $"{addressesHex}Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory." ); } if (enhancementItem.RequiredBlockIndex > context.BlockIndex) { throw new RequiredBlockIndexException( $"{addressesHex}Aborted as the equipment to enhance ({itemId}) is not available yet;" + $" it will be available at the block #{enhancementItem.RequiredBlockIndex}." ); } if (!(enhancementItem is Equipment enhancementEquipment)) { throw new InvalidCastException( $"{addressesHex}Aborted as the item is not a {nameof(Equipment)}, but {enhancementItem.GetType().Name}." ); } var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); if (slotState is null) { throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state was failed to load. #{slotIndex}"); } if (!slotState.Validate(avatarState, ctx.BlockIndex)) { throw new CombinationSlotUnlockException($"{addressesHex}Aborted as the slot state was failed to invalid. #{slotIndex}"); } sw.Stop(); Log.Verbose("{AddressesHex}ItemEnhancement Get Equipment: {Elapsed}", addressesHex, sw.Elapsed); sw.Restart(); var enhancementCostSheet = states.GetSheet <EnhancementCostSheetV2>(); if (!TryGetRow(enhancementEquipment, enhancementCostSheet, out var row)) { throw new SheetRowNotFoundException(addressesHex, nameof(WorldSheet), enhancementEquipment.level); } var maxLevel = GetEquipmentMaxLevel(enhancementEquipment, enhancementCostSheet); if (enhancementEquipment.level >= maxLevel) { throw new EquipmentLevelExceededException( $"{addressesHex}Aborted due to invalid equipment level: {enhancementEquipment.level} < {maxLevel}"); } if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out ItemUsable materialItem)) { throw new NotEnoughMaterialException( $"{addressesHex}Aborted as the signer does not have a necessary material ({materialId})." ); } if (materialItem.RequiredBlockIndex > context.BlockIndex) { throw new RequiredBlockIndexException( $"{addressesHex}Aborted as the material ({materialId}) is not available yet;" + $" it will be available at the block #{materialItem.RequiredBlockIndex}." ); } if (!(materialItem is Equipment materialEquipment)) { throw new InvalidCastException( $"{addressesHex}Aborted as the material item is not an {nameof(Equipment)}, but {materialItem.GetType().Name}." ); } if (enhancementEquipment.ItemId == materialId) { throw new InvalidMaterialException( $"{addressesHex}Aborted as an equipment to enhance ({materialId}) was used as a material too." ); } if (materialEquipment.ItemSubType != enhancementEquipment.ItemSubType) { throw new InvalidMaterialException( $"{addressesHex}Aborted as the material item is not a {enhancementEquipment.ItemSubType}," + $" but {materialEquipment.ItemSubType}." ); } if (materialEquipment.Grade != enhancementEquipment.Grade) { throw new InvalidMaterialException( $"{addressesHex}Aborted as grades of the equipment to enhance ({enhancementEquipment.Grade})" + $" and a material ({materialEquipment.Grade}) does not match." ); } if (materialEquipment.level != enhancementEquipment.level) { throw new InvalidMaterialException( $"{addressesHex}Aborted as levels of the equipment to enhance ({enhancementEquipment.level})" + $" and a material ({materialEquipment.level}) does not match." ); } sw.Stop(); Log.Verbose("{AddressesHex}ItemEnhancement Get Material: {Elapsed}", addressesHex, sw.Elapsed); sw.Restart(); // Subtract required action point var requiredActionPoint = GetRequiredAp(); if (avatarState.actionPoint < requiredActionPoint) { throw new NotEnoughActionPointException( $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredActionPoint}" ); } avatarState.actionPoint -= requiredActionPoint; // TransferAsset (NCG) var requiredNcg = row.Cost; if (requiredNcg > 0) { states = states.TransferAsset(ctx.Signer, BlacksmithAddress, states.GetGoldCurrency() * requiredNcg); } // Unequip items materialEquipment.Unequip(); enhancementEquipment.Unequip(); // clone items var preItemUsable = new Equipment((Dictionary)enhancementEquipment.Serialize()); // Equipment level up & Update var equipmentResult = GetEnhancementResult(row, ctx.Random); if (equipmentResult != EnhancementResult.Fail) { enhancementEquipment.LevelUpV2(ctx.Random, row, equipmentResult == EnhancementResult.GreatSuccess); } var requiredBlockCount = GetRequiredBlockCount(row, equipmentResult); var requiredBlockIndex = ctx.BlockIndex + requiredBlockCount; enhancementEquipment.Update(requiredBlockIndex); // Remove material avatarState.inventory.RemoveNonFungibleItem(materialId); sw.Stop(); Log.Verbose("{AddressesHex}ItemEnhancement Upgrade Equipment: {Elapsed}", addressesHex, sw.Elapsed); // Send scheduled mail var result = new ResultModel { preItemUsable = preItemUsable, itemUsable = enhancementEquipment, materialItemIdList = new[] { materialId }, actionPoint = requiredActionPoint, enhancementResult = equipmentResult, gold = requiredNcg, }; var mail = new ItemEnhanceMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), requiredBlockIndex); result.id = mail.id; avatarState.inventory.RemoveNonFungibleItem(enhancementEquipment); avatarState.Update(mail); avatarState.UpdateFromItemEnhancement(enhancementEquipment); // Update quest reward var materialSheet = states.GetSheet <MaterialItemSheet>(); avatarState.UpdateQuestRewards(materialSheet); // Update slot state slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); // Set state sw.Restart(); states = states .SetState(inventoryAddress, avatarState.inventory.Serialize()) .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) .SetState(questListAddress, avatarState.questList.Serialize()) .SetState(avatarAddress, avatarState.SerializeV2()); sw.Stop(); Log.Verbose("{AddressesHex}ItemEnhancement Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); var ended = DateTimeOffset.UtcNow; Log.Verbose("{AddressesHex}ItemEnhancement Total Executed Time: {Elapsed}", addressesHex, ended - started); return(states.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 .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())); }