private async Task <(bool allowed, string?reason)> CheckValidAsync(LootListEntry entry, LootListEntrySubmissionDto dto) { if (entry.ItemId.HasValue) { var item = await _context.Items .AsNoTracking() .Where(i => i.Id == entry.ItemId.Value) .Select(i => new { i.Id, i.Name, i.Phase, i.RewardFromId, QuestId = (uint?)i.RewardFrom !.QuestId, MaxCount = (!i.IsUnique && (i.Slot == InventorySlot.Trinket || i.Slot == InventorySlot.Finger || i.Slot == InventorySlot.OneHand)) ? 2 : 1 })
public async Task <ActionResult <LootListDto> > PostLootList(long characterId, byte phase, [FromBody] LootListSubmissionDto dto, [FromServices] IdGen.IIdGenerator <long> idGenerator) { var authorizationResult = await _authorizationService.AuthorizeAsync(User, characterId, AppPolicies.CharacterOwnerOrAdmin); if (!authorizationResult.Succeeded) { return(Unauthorized()); } var bracketTemplates = await _context.Brackets.AsNoTracking().Where(b => b.Phase == phase).OrderBy(b => b.Index).ToListAsync(); if (bracketTemplates.Count == 0) { return(NotFound()); } if (!ModelState.IsValid) { return(ValidationProblem()); } var character = await _context.Characters.FindAsync(characterId); if (character is null) { return(NotFound()); } if (character.Deactivated) { return(Problem("Character has been deactivated.")); } if (await _context.CharacterLootLists.AsNoTracking().AnyAsync(ll => ll.CharacterId == character.Id && ll.Phase == phase)) { return(Problem("A loot list for that character and phase already exists.")); } if (!dto.MainSpec.IsClass(character.Class)) { ModelState.AddModelError(nameof(dto.MainSpec), "Selected specialization does not fit the player's class."); return(ValidationProblem()); } if (dto.OffSpec != default && !dto.OffSpec.IsClass(character.Class)) { ModelState.AddModelError(nameof(dto.OffSpec), "Selected specialization does not fit the player's class."); return(ValidationProblem()); } var list = new CharacterLootList { CharacterId = character.Id, Character = character, Entries = new List <LootListEntry>(28), Status = LootListStatus.Editing, MainSpec = dto.MainSpec, OffSpec = dto.OffSpec, Phase = phase }; var entries = new List <LootListEntry>(); var brackets = await _context.Brackets .AsNoTracking() .Where(b => b.Phase == phase) .OrderBy(b => b.Index) .ToListAsync(); foreach (var bracket in brackets) { for (byte rank = bracket.MinRank; rank <= bracket.MaxRank; rank++) { for (int col = 0; col < bracket.MaxItems; col++) { var entry = new LootListEntry(idGenerator.CreateId()) { LootList = list, Rank = rank }; _context.LootListEntries.Add(entry); entries.Add(entry); } } } _context.CharacterLootLists.Add(list); await _context.SaveChangesAsync(); _telemetry.TrackEvent("LootListCreated", User, props => { props["CharacterId"] = character.Id.ToString(); props["CharacterName"] = character.Name; props["Phase"] = list.Phase.ToString(); }); var scope = await _context.GetCurrentPriorityScopeAsync(); var observedDates = await _context.Raids .AsNoTracking() .Where(r => r.RaidTeamId == character.TeamId) .OrderByDescending(r => r.StartedAt) .Select(r => r.StartedAt.Date) .Distinct() .Take(scope.ObservedAttendances) .ToListAsync(); var attendance = await _context.RaidAttendees .AsNoTracking() .Where(x => !x.IgnoreAttendance && x.RemovalId == null && x.CharacterId == character.Id && x.Raid.RaidTeamId == character.TeamId) .Select(x => x.Raid.StartedAt.Date) .Where(date => observedDates.Contains(date)) .Distinct() .CountAsync(); var now = _serverTimeZoneInfo.TimeZoneNow(); var donationMatrix = await _context.GetDonationMatrixAsync(d => d.CharacterId == characterId, scope); var donations = donationMatrix.GetCreditForMonth(characterId, now); var returnDto = new LootListDto { ApprovedBy = null, Bonuses = PrioCalculator.GetListBonuses(scope, attendance, character.MemberStatus, donations).ToList(), CharacterId = character.Id, CharacterMemberStatus = character.MemberStatus, CharacterName = character.Name, MainSpec = list.MainSpec, OffSpec = list.OffSpec, Phase = list.Phase, Status = list.Status, TeamId = character.TeamId, Timestamp = list.Timestamp }; if (returnDto.TeamId.HasValue) { returnDto.TeamName = await _context.RaidTeams.AsNoTracking().Where(t => t.Id == returnDto.TeamId).Select(t => t.Name).FirstOrDefaultAsync(); } foreach (var entry in entries) { var bracket = brackets.Find(b => entry.Rank >= b.MinRank && entry.Rank <= b.MaxRank); Debug.Assert(bracket is not null); returnDto.Entries.Add(new LootListEntryDto { Bracket = bracket.Index, BracketAllowsOffspec = bracket.AllowOffspec, BracketAllowsTypeDuplicates = bracket.AllowTypeDuplicates, Id = entry.Id, Rank = entry.Rank }); } return(CreatedAtAction(nameof(Get), new { characterId = character.Id, phase }, returnDto)); }
public async Task <ActionResult <LootListDto> > ResetLootList(long characterId, byte phase, [FromServices] IdGen.IIdGenerator <long> idGenerator) { var authorizationResult = await _authorizationService.AuthorizeAsync(User, characterId, AppPolicies.CharacterOwnerOrAdmin); if (!authorizationResult.Succeeded) { return(Unauthorized()); } var list = await _context.CharacterLootLists.FindAsync(characterId, phase); if (list is null) { return(NotFound()); } if (list.Status != LootListStatus.Editing) { return(Problem("Loot list is not editable.")); } var character = await _context.Characters.FindAsync(characterId); if (character is null) { return(NotFound()); } if (character.Deactivated) { return(Problem("Character has been deactivated.")); } var entriesToRemove = await _context.LootListEntries .AsTracking() .Where(e => e.LootList == list) .ToListAsync(); var entries = new List <LootListEntry>(); var brackets = await _context.Brackets .AsNoTracking() .Where(b => b.Phase == phase) .OrderBy(b => b.Index) .ToListAsync(); foreach (var bracket in brackets) { for (byte rank = bracket.MinRank; rank <= bracket.MaxRank; rank++) { int processed = 0; foreach (var entry in entriesToRemove.Where(e => e.Rank == rank)) { if (entry.DropId.HasValue) { entries.Add(entry); processed++; } else if (processed < bracket.MaxItems) { entry.ItemId = null; entry.Justification = null; entries.Add(entry); processed++; } } for (; processed < bracket.MaxItems; processed++) { var entry = new LootListEntry(idGenerator.CreateId()) { LootList = list, Rank = rank }; _context.LootListEntries.Add(entry); entries.Add(entry); } } } entriesToRemove.RemoveAll(entries.Contains); _context.LootListEntries.RemoveRange(entriesToRemove); await _context.SaveChangesAsync(); _telemetry.TrackEvent("LootListReset", User, props => { props["CharacterId"] = character.Id.ToString(); props["CharacterName"] = character.Name; props["Phase"] = list.Phase.ToString(); }); var lootLists = await CreateDtosAsync(characterId, null, phase); Debug.Assert(lootLists?.Count == 1); return(lootLists[0]); }