diff --git a/DragaliaAPI.Shared.Test/Unit/MasterAssetTest.cs b/DragaliaAPI.Shared.Test/Unit/MasterAssetTest.cs index e7dd8f63a..9f92a257d 100644 --- a/DragaliaAPI.Shared.Test/Unit/MasterAssetTest.cs +++ b/DragaliaAPI.Shared.Test/Unit/MasterAssetTest.cs @@ -64,6 +64,8 @@ public void CharaData_Get_ReturnsExpectedProperties() AwakeNeedEntityType5: EntityTypes.Dew, AwakeNeedEntityId5: 0, AwakeNeedEntityQuantity5: 25000, + GrowMaterialOnlyStartDate: DateTimeOffset.UnixEpoch, + GrowMaterialOnlyEndDate: DateTimeOffset.UnixEpoch, UniqueGrowMaterialId1: Materials.GalaLaxisConviction, UniqueGrowMaterialId2: Materials.GalaLaxisDevotion, GrowMaterialId: Materials.Empty, @@ -105,6 +107,7 @@ public void CharaData_GetManaNode_ReturnsNode() UniqueGrowMaterialCount2: 0, GrowMaterialCount: 1, MC_0: 4032, + Step: 1, ManaCircleName: "MC_0403" ) ); @@ -212,6 +215,7 @@ public void ManaNode_Get_ReturnsExpectedProperties() UniqueGrowMaterialCount2: 0, GrowMaterialCount: 1, MC_0: 5045, + Step: 3, ManaCircleName: "MC_0504" ) ); diff --git a/DragaliaAPI.Shared/MasterAsset/Models/CharaData.cs b/DragaliaAPI.Shared/MasterAsset/Models/CharaData.cs index e2e0b44d2..43929cfdf 100644 --- a/DragaliaAPI.Shared/MasterAsset/Models/CharaData.cs +++ b/DragaliaAPI.Shared/MasterAsset/Models/CharaData.cs @@ -56,6 +56,10 @@ public record CharaData( Materials UniqueGrowMaterialId1, Materials UniqueGrowMaterialId2, Materials GrowMaterialId, + [property: JsonConverter(typeof(MasterAssetDateTimeOffsetConverter))] + DateTimeOffset GrowMaterialOnlyStartDate, + [property: JsonConverter(typeof(MasterAssetDateTimeOffsetConverter))] + DateTimeOffset GrowMaterialOnlyEndDate, int DefaultAbility1Level, int DefaultAbility2Level, int DefaultAbility3Level, diff --git a/DragaliaAPI.Shared/MasterAsset/Models/ManaCircle/ManaNode.cs b/DragaliaAPI.Shared/MasterAsset/Models/ManaCircle/ManaNode.cs index 90c45528e..e78eee13a 100644 --- a/DragaliaAPI.Shared/MasterAsset/Models/ManaCircle/ManaNode.cs +++ b/DragaliaAPI.Shared/MasterAsset/Models/ManaCircle/ManaNode.cs @@ -11,5 +11,6 @@ public record ManaNode( int UniqueGrowMaterialCount2, int GrowMaterialCount, [property: JsonPropertyName("MC_0")] int MC_0, - string ManaCircleName + string ManaCircleName, + int Step ); diff --git a/DragaliaAPI/Controllers/Dragalia/CharaController.cs b/DragaliaAPI/Controllers/Dragalia/CharaController.cs index 7417eb3e1..045e567b5 100644 --- a/DragaliaAPI/Controllers/Dragalia/CharaController.cs +++ b/DragaliaAPI/Controllers/Dragalia/CharaController.cs @@ -7,6 +7,7 @@ using DragaliaAPI.Features.Missions; using DragaliaAPI.Features.Reward; using DragaliaAPI.Features.Shop; +using DragaliaAPI.Helpers; using DragaliaAPI.Models; using DragaliaAPI.Models.Generated; using DragaliaAPI.Services; @@ -23,44 +24,20 @@ namespace DragaliaAPI.Controllers.Dragalia; [Route("chara")] -public class CharaController : DragaliaControllerBase +public class CharaController( + IUserDataRepository userDataRepository, + IUnitRepository unitRepository, + IInventoryRepository inventoryRepository, + IStoryRepository storyRepository, + IUpdateDataService updateDataService, + ILogger logger, + IMapper mapper, + IMissionProgressionService missionProgressionService, + IPaymentService paymentService, + IRewardService rewardService, + IDateTimeProvider dateTimeProvider +) : DragaliaControllerBase { - private readonly IUserDataRepository userDataRepository; - private readonly IUnitRepository unitRepository; - private readonly IInventoryRepository inventoryRepository; - private readonly IStoryRepository storyRepository; - private readonly IUpdateDataService updateDataService; - private readonly ILogger logger; - private readonly IMapper mapper; - private readonly IMissionProgressionService missionProgressionService; - private readonly IPaymentService paymentService; - private readonly IRewardService rewardService; - - public CharaController( - IUserDataRepository userDataRepository, - IUnitRepository unitRepository, - IInventoryRepository inventoryRepository, - IStoryRepository storyRepository, - IUpdateDataService updateDataService, - ILogger logger, - IMapper mapper, - IMissionProgressionService missionProgressionService, - IPaymentService paymentService, - IRewardService rewardService - ) - { - this.userDataRepository = userDataRepository; - this.unitRepository = unitRepository; - this.inventoryRepository = inventoryRepository; - this.storyRepository = storyRepository; - this.updateDataService = updateDataService; - this.logger = logger; - this.mapper = mapper; - this.missionProgressionService = missionProgressionService; - this.paymentService = paymentService; - this.rewardService = rewardService; - } - [Route("awake")] [HttpPost] public async Task Awake([FromBody] CharaAwakeRequest request) @@ -76,7 +53,7 @@ public async Task Awake([FromBody] CharaAwakeRequest request) switch (request.next_rarity) { case 4: - await this.paymentService.ProcessPayment( + await paymentService.ProcessPayment( data.AwakeNeedEntityType4.ToPaymentType(), expectedPrice: data.AwakeNeedEntityQuantity4 ); @@ -84,7 +61,7 @@ await this.paymentService.ProcessPayment( playerCharData.AttackBase += (ushort)(data.MinAtk4 - data.MinAtk3); break; case 5: - await this.paymentService.ProcessPayment( + await paymentService.ProcessPayment( data.AwakeNeedEntityType5.ToPaymentType(), expectedPrice: data.AwakeNeedEntityQuantity5 ); @@ -102,7 +79,7 @@ await this.paymentService.ProcessPayment( //TODO Get and update missions relating to promoting characters - resp.update_data_list = await this.updateDataService.SaveChangesAsync(); + resp.update_data_list = await updateDataService.SaveChangesAsync(); return Ok(resp); } @@ -138,18 +115,18 @@ and not Materials.FortifyingCrystal ); } - await this.paymentService.ProcessPayment(mat.id, mat.quantity); + await paymentService.ProcessPayment(mat.id, mat.quantity); } - DbPlayerCharaData playerCharData = await this.unitRepository.Charas.FirstAsync( + DbPlayerCharaData playerCharData = await unitRepository.Charas.FirstAsync( chara => chara.CharaId == request.chara_id ); Dictionary usedMaterials = new(); CharaLevelUp(request.material_list, ref playerCharData, ref usedMaterials); - resp.update_data_list = await this.updateDataService.SaveChangesAsync(); - resp.entity_result = this.rewardService.GetEntityResult(); + resp.update_data_list = await updateDataService.SaveChangesAsync(); + resp.entity_result = rewardService.GetEntityResult(); return Ok(resp); } @@ -160,7 +137,7 @@ private void CharaLevelUp( ref Dictionary usedMaterials ) { - this.logger.LogDebug("Leveling up chara {@chara}", playerCharData); + logger.LogDebug("Leveling up chara {@chara}", playerCharData); //TODO: For now we'll trust the client to not allow leveling up/enhancing beyond allowed limits byte maxLevel = (byte)( CharaConstants.GetMaxLevelFor(playerCharData.Rarity) + playerCharData.AdditionalMaxLevel @@ -192,7 +169,7 @@ ref Dictionary usedMaterials for (int i = 0; i < materialList.quantity; i++) { // HACK (TODO: We need a mission progression refactor) - this.missionProgressionService.OnCharacterBuildup(PlusCountType.Atk); + missionProgressionService.OnCharacterBuildup(PlusCountType.Atk); } break; @@ -205,7 +182,7 @@ ref Dictionary usedMaterials for (int i = 0; i < materialList.quantity; i++) { // HACK (TODO: We need a mission progression refactor) - this.missionProgressionService.OnCharacterBuildup(PlusCountType.Hp); + missionProgressionService.OnCharacterBuildup(PlusCountType.Hp); } break; @@ -282,7 +259,7 @@ ref Dictionary usedMaterials Math.Ceiling((atkStep * (playerCharData.Level - lvlBase)) + atkBase); } - this.logger.LogDebug("New char data: {@chara}", playerCharData); + logger.LogDebug("New char data: {@chara}", playerCharData); } [Route("reset_plus_count")] @@ -291,7 +268,7 @@ public async Task CharaResetPlusCount( [FromBody] CharaResetPlusCountRequest request ) { - DbPlayerCharaData playerCharData = await this.unitRepository.Charas.FirstAsync( + DbPlayerCharaData playerCharData = await unitRepository.Charas.FirstAsync( chara => chara.CharaId == request.chara_id ); @@ -317,19 +294,15 @@ [FromBody] CharaResetPlusCountRequest request ); } - await this.paymentService.ProcessPayment( + await paymentService.ProcessPayment( PaymentTypes.Coin, expectedPrice: CharaConstants.AugmentResetCost * plusCount ); - await this.rewardService.GrantReward( - new Entity(EntityTypes.Material, (int)material, plusCount) - ); + await rewardService.GrantReward(new Entity(EntityTypes.Material, (int)material, plusCount)); UpdateDataList updateDataList = await updateDataService.SaveChangesAsync(); - return Ok( - new CharaResetPlusCountData(updateDataList, this.rewardService.GetEntityResult()) - ); + return Ok(new CharaResetPlusCountData(updateDataList, rewardService.GetEntityResult())); } [Route("buildup_mana")] @@ -338,9 +311,9 @@ public async Task CharaBuildupMana([FromBody] CharaBuildupManaRe { CharaBuildupManaData resp = new(); - this.logger.LogDebug("Received mana node request {@request}", request); + logger.LogDebug("Received mana node request {@request}", request); - DbPlayerCharaData playerCharData = await this.unitRepository.Charas.FirstAsync( + DbPlayerCharaData playerCharData = await unitRepository.Charas.FirstAsync( chara => chara.CharaId == request.chara_id ); @@ -352,8 +325,8 @@ await CharaManaNodeUnlock( //TODO: Party power calculation call - resp.update_data_list = await this.updateDataService.SaveChangesAsync(); - resp.entity_result = this.rewardService.GetEntityResult(); + resp.update_data_list = await updateDataService.SaveChangesAsync(); + resp.entity_result = rewardService.GetEntityResult(); return this.Ok(resp); } @@ -364,14 +337,14 @@ public async Task CharaLimitBreak([FromBody] CharaLimitBreakRequ { CharaBuildupData resp = new(); - DbPlayerCharaData playerCharData = await this.unitRepository.Charas.FirstAsync( + DbPlayerCharaData playerCharData = await unitRepository.Charas.FirstAsync( chara => chara.CharaId == request.chara_id ); await LimitBreakChara(playerCharData, (byte)request.next_limit_break_count); - resp.update_data_list = await this.updateDataService.SaveChangesAsync(); - resp.entity_result = this.rewardService.GetEntityResult(); + resp.update_data_list = await updateDataService.SaveChangesAsync(); + resp.entity_result = rewardService.GetEntityResult(); return Ok(resp); } @@ -384,7 +357,7 @@ [FromBody] CharaLimitBreakAndBuildupManaRequest request { CharaLimitBreakAndBuildupManaData resp = new(); - DbPlayerCharaData playerCharData = await this.unitRepository.Charas.FirstAsync( + DbPlayerCharaData playerCharData = await unitRepository.Charas.FirstAsync( chara => chara.CharaId == request.chara_id ); @@ -399,8 +372,8 @@ await CharaManaNodeUnlock( ); } - resp.update_data_list = await this.updateDataService.SaveChangesAsync(); - resp.entity_result = this.rewardService.GetEntityResult(); + resp.update_data_list = await updateDataService.SaveChangesAsync(); + resp.entity_result = rewardService.GetEntityResult(); return Ok(resp); } @@ -413,7 +386,7 @@ [FromBody] CharaBuildupPlatinumRequest request { CharaBuildupPlatinumData resp = new(); - DbPlayerCharaData playerCharaData = await this.unitRepository.Charas.FirstAsync( + DbPlayerCharaData playerCharaData = await unitRepository.Charas.FirstAsync( chara => chara.CharaId == request.chara_id ); @@ -450,8 +423,8 @@ await CharaManaNodeUnlock( CharaUpgradeMaterialTypes.Omnicite ); - resp.update_data_list = await this.updateDataService.SaveChangesAsync(); - resp.entity_result = this.rewardService.GetEntityResult(); + resp.update_data_list = await updateDataService.SaveChangesAsync(); + resp.entity_result = rewardService.GetEntityResult(); return Ok(resp); } @@ -460,7 +433,7 @@ private async Task LimitBreakChara(DbPlayerCharaData charaData, byte limitBreakN { CharaData data = MasterAsset.CharaData[charaData.CharaId]; - this.logger.LogDebug( + logger.LogDebug( "Limit-breaking chara {charaId} to {limitBreakNum}", data.Id, limitBreakNum @@ -478,23 +451,17 @@ int growMaterial foreach ((Materials id, int quantity) in orbs) { if (id != Materials.Empty) - await this.paymentService.ProcessPayment(id, quantity); + await paymentService.ProcessPayment(id, quantity); } if (uniqueGrowMaterial1 > 0) { - await this.paymentService.ProcessPayment( - data.UniqueGrowMaterialId1, - uniqueGrowMaterial1 - ); + await paymentService.ProcessPayment(data.UniqueGrowMaterialId1, uniqueGrowMaterial1); } if (uniqueGrowMaterial2 > 0) { - await this.paymentService.ProcessPayment( - data.UniqueGrowMaterialId2, - uniqueGrowMaterial2 - ); + await paymentService.ProcessPayment(data.UniqueGrowMaterialId2, uniqueGrowMaterial2); } // GrowMaterial is always 1 but unused? @@ -518,7 +485,9 @@ CharaUpgradeMaterialTypes isUseSpecialMaterial if (!manaNodes.Any()) return; - this.logger.LogDebug("Pre-upgrade CharaData: {@charaData}", playerCharData); + DateTimeOffset time = dateTimeProvider.UtcNow; + + logger.LogDebug("Pre-upgrade CharaData: {@charaData}", playerCharData); CharaData charaData = MasterAsset.CharaData[playerCharData.CharaId]; @@ -533,14 +502,6 @@ CharaUpgradeMaterialTypes isUseSpecialMaterial List unlockedStories = new(); - int[] stepLookup = new int[70]; - Dictionary typeSteps = Enum.GetValues() - .ToDictionary(x => x, x => 1); - - List materials = MasterAsset.ManaPieceMaterial.Enumerable - .Where(x => x.ElementId == charaData.PieceMaterialElementId) - .ToList(); - for (int i = 0; i < manaNodeInfos.Count && i < 70; i++) { int floor = Math.Min(i / 10, 5); @@ -557,18 +518,6 @@ CharaUpgradeMaterialTypes isUseSpecialMaterial atkNodesOnFloor[floor].Add(i + 1); break; } - - int currentStep = typeSteps[manaNodeInfos[i].ManaPieceType]; - stepLookup[i] = currentStep; - - if ( - materials.Any( - x => x.ManaPieceType == manaNodeInfos[i].ManaPieceType && x.Step == currentStep - ) - ) - { - typeSteps[manaNodeInfos[i].ManaPieceType]++; - } } int[] hpPerCircleTotals = @@ -592,11 +541,11 @@ CharaUpgradeMaterialTypes isUseSpecialMaterial SortedSet nodes = playerCharData.ManaCirclePieceIdList; - this.logger.LogInformation("Unlocking nodes {@nodes}", manaNodes); + logger.LogInformation("Unlocking nodes {@nodes}", manaNodes); foreach (int nodeNr in manaNodes) { - this.logger.LogTrace("Node: {nodeNr}", nodeNr); + logger.LogTrace("Node: {nodeNr}", nodeNr); if (manaNodeInfos.Count < nodeNr) { @@ -688,7 +637,7 @@ CharaUpgradeMaterialTypes isUseSpecialMaterial playerCharData.ExAbility2Level++; break; case ManaNodeTypes.Mat: - await this.rewardService.GrantReward( + await rewardService.GrantReward( new Entity(EntityTypes.Material, (int)Materials.DamascusCrystal) ); break; @@ -733,7 +682,7 @@ await storyRepository.GetOrCreateStory( if (isUseSpecialMaterial == CharaUpgradeMaterialTypes.Omnicite) continue; - await this.paymentService.ProcessPayment( + await paymentService.ProcessPayment( PaymentTypes.ManaPoint, expectedPrice: manaNodeInfo.NecessaryManaPoint ); @@ -741,7 +690,7 @@ await this.paymentService.ProcessPayment( // they smoked some shit if (manaNodeInfo.UniqueGrowMaterialCount1 > 0) { - await this.paymentService.ProcessPayment( + await paymentService.ProcessPayment( charaData.UniqueGrowMaterialId1, manaNodeInfo.UniqueGrowMaterialCount1 ); @@ -749,15 +698,20 @@ await this.paymentService.ProcessPayment( if (manaNodeInfo.UniqueGrowMaterialCount2 > 0) { - await this.paymentService.ProcessPayment( + await paymentService.ProcessPayment( charaData.UniqueGrowMaterialId2, manaNodeInfo.UniqueGrowMaterialCount2 ); } - if (manaNodeInfo.GrowMaterialCount > 0 && charaData.GrowMaterialId != Materials.Empty) + if ( + charaData.GrowMaterialOnlyStartDate <= time + && charaData.GrowMaterialOnlyEndDate >= time + && manaNodeInfo.GrowMaterialCount > 0 + && charaData.GrowMaterialId != Materials.Empty + ) { - await this.paymentService.ProcessPayment( + await paymentService.ProcessPayment( charaData.GrowMaterialId, manaNodeInfo.GrowMaterialCount ); @@ -766,7 +720,7 @@ await this.paymentService.ProcessPayment( ManaPieceMaterial? material = MasterAsset.ManaPieceMaterial.Enumerable.FirstOrDefault( x => x.ElementId == charaData.PieceMaterialElementId - && x.Step == stepLookup[nodeNr - 1] + && x.Step == manaNodeInfo.Step && x.ManaPieceType == manaNodeInfo.ManaPieceType ); @@ -774,7 +728,7 @@ await this.paymentService.ProcessPayment( { if (material.DewPoint > 0) { - await this.paymentService.ProcessPayment( + await paymentService.ProcessPayment( PaymentTypes.DewPoint, expectedPrice: material.DewPoint ); @@ -783,7 +737,7 @@ await this.paymentService.ProcessPayment( foreach ((Materials id, int quantity) in material.NeededMaterials) { if (id != Materials.Empty) - await this.paymentService.ProcessPayment(id, quantity); + await paymentService.ProcessPayment(id, quantity); } ManaPieceType pieceType = MasterAsset.ManaPieceType[manaNodeInfo.ManaPieceType]; @@ -792,7 +746,7 @@ await this.paymentService.ProcessPayment( { if (type == EntityTypes.Material) { - await this.paymentService.ProcessPayment((Materials)id, quantity); + await paymentService.ProcessPayment((Materials)id, quantity); } } } @@ -802,15 +756,15 @@ await this.paymentService.ProcessPayment( if (manaNodes.Contains(50)) { - this.logger.LogDebug("Applying 50MC bonus"); + logger.LogDebug("Applying 50MC bonus"); playerCharData.HpNode += (ushort)charaData.McFullBonusHp5; playerCharData.AttackNode += (ushort)charaData.McFullBonusAtk5; } playerCharData.ManaCirclePieceIdList = nodes; - this.logger.LogDebug("New CharaData: {@charaData}", playerCharData); + logger.LogDebug("New CharaData: {@charaData}", playerCharData); - this.logger.LogDebug( + logger.LogDebug( "New bitmask: {bitmask}", Convert.ToString(playerCharData.ManaNodeUnlockCount, 2) ); @@ -824,7 +778,7 @@ [FromBody] CharaUnlockEditSkillRequest request { CharaUnlockEditSkillData resp = new(); - DbPlayerCharaData playerCharData = await this.unitRepository.Charas.FirstAsync( + DbPlayerCharaData playerCharData = await unitRepository.Charas.FirstAsync( chara => chara.CharaId == request.chara_id ); @@ -845,12 +799,12 @@ [FromBody] CharaUnlockEditSkillRequest request Materials usedMat = UpgradeMaterials.tomes[charData.ElementalType]; int usedMatCount = charData.EditSkillCost; - await this.paymentService.ProcessPayment(usedMat, usedMatCount); + await paymentService.ProcessPayment(usedMat, usedMatCount); playerCharData.IsUnlockEditSkill = true; - resp.update_data_list = await this.updateDataService.SaveChangesAsync(); - resp.entity_result = this.rewardService.GetEntityResult(); + resp.update_data_list = await updateDataService.SaveChangesAsync(); + resp.entity_result = rewardService.GetEntityResult(); return Ok(resp); } @@ -965,7 +919,7 @@ await unitRepository.GetCharaSetData(request.chara_id, request.unit_set_no) ) }; - UpdateDataList ul = await this.updateDataService.SaveChangesAsync(); + UpdateDataList ul = await updateDataService.SaveChangesAsync(); ul.chara_unit_set_list = new List { setList }; return Ok(new CharaSetCharaUnitSetData() { update_data_list = ul, entity_result = null }); }