コード例 #1
0
        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));
        }
コード例 #2
0
    public async Task <ActionResult <EncounterDropDto> > PutAssign(long id, [FromBody] AwardDropSubmissionDto dto, [FromServices] DiscordClientProvider dcp)
    {
        var now  = _realmTimeZone.TimeZoneNow();
        var drop = await _context.Drops.FindAsync(id);

        if (drop is null)
        {
            return(NotFound());
        }

        var raid = await _context.Raids
                   .AsNoTracking()
                   .Where(r => r.Id == drop.EncounterKillRaidId)
                   .Select(r => new { r.RaidTeamId, r.LocksAt })
                   .FirstOrDefaultAsync();

        if (raid is null)
        {
            return(NotFound());
        }

        var authResult = await _authorizationService.AuthorizeAsync(User, raid.RaidTeamId, AppPolicies.LootMaster);

        if (!authResult.Succeeded)
        {
            return(Unauthorized());
        }

        if (DateTimeOffset.UtcNow > raid.LocksAt)
        {
            return(Problem("Can't alter a locked raid."));
        }

        if (dto.WinnerId.HasValue && drop.WinnerId.HasValue)
        {
            ModelState.AddModelError(nameof(dto.WinnerId), "Existing winner must be cleared before setting a new winner.");
            return(ValidationProblem());
        }

        drop.AwardedAt = now;
        drop.AwardedBy = User.GetDiscordId();
        var scope = await _context.GetCurrentPriorityScopeAsync();

        var teamId      = raid.RaidTeamId;
        var attendances = await _context.GetAttendanceTableAsync(teamId, scope.ObservedAttendances);

        var donationMatrix = await _context.GetDonationMatrixAsync(d => d.Character.Attendances.Any(a => a.RaidId == drop.EncounterKillRaidId), scope);

        var presentTeamRaiders = await _context.CharacterEncounterKills
                                 .AsTracking()
                                 .Where(cek => cek.EncounterKillEncounterId == drop.EncounterKillEncounterId && cek.EncounterKillRaidId == drop.EncounterKillRaidId && cek.EncounterKillTrashIndex == drop.EncounterKillTrashIndex)
                                 .Select(cek => cek.Character)
                                 .Select(ConvertToDropInfo(drop.ItemId))
                                 .ToListAsync();

        await _context.Entry(drop).Collection(drop => drop.Passes).LoadAsync();

        drop.Passes.Clear();

        if (dto.WinnerId.HasValue)
        {
            var winner = presentTeamRaiders.Find(e => e.Id == dto.WinnerId);

            if (winner is null)
            {
                ModelState.AddModelError(nameof(dto.WinnerId), "Character was not present for the kill.");
                return(ValidationProblem());
            }

            int?winnerPrio = null;

            if (winner.Entry is not null)
            {
                winnerPrio = winner.Entry.Rank;

                var passes = await _context.DropPasses
                             .AsTracking()
                             .Where(p => p.LootListEntryId == winner.Entry.Id && p.RemovalId == null)
                             .ToListAsync();

                Debug.Assert(passes.Count == winner.Entry.Passes);

                var donated = donationMatrix.GetCreditForMonth(winner.Id, now);
                attendances.TryGetValue(winner.Id, out int attended);

                foreach (var bonus in PrioCalculator.GetAllBonuses(scope, attended, winner.MemberStatus, donated, passes.Count, winner.Enchanted, winner.Prepared))
                {
                    winnerPrio = winnerPrio.Value + bonus.Value;
                }

                drop.WinningEntry = await _context.LootListEntries.FindAsync(winner.Entry.Id);

                Debug.Assert(drop.WinningEntry is not null);
                drop.WinningEntry.Drop   = drop;
                drop.WinningEntry.DropId = drop.Id;

                foreach (var pass in passes)
                {
                    pass.WonEntryId = winner.Entry.Id;
                }
            }

            drop.WinnerId = winner.Id;

            foreach (var killer in presentTeamRaiders)
            {
                if (killer.Entry is not null && killer.TeamId == teamId && killer != winner)
                {
                    var thisPrio = (int)killer.Entry.Rank;

                    var donated = donationMatrix.GetCreditForMonth(killer.Id, now);
                    attendances.TryGetValue(killer.Id, out int attended);

                    foreach (var bonus in PrioCalculator.GetAllBonuses(scope, attended, killer.MemberStatus, donated, killer.Entry.Passes, killer.Enchanted, killer.Prepared))
                    {
                        thisPrio += bonus.Value;
                    }

                    _context.DropPasses.Add(new DropPass
                    {
                        Drop             = drop,
                        DropId           = drop.Id,
                        CharacterId      = killer.Id,
                        RelativePriority = thisPrio - (winnerPrio ?? 0),
                        LootListEntryId  = killer.Entry.Id
                    });
                }
            }
        }
        else
        {
            var oldWinningEntry = await _context.LootListEntries
                                  .AsTracking()
                                  .Where(e => e.DropId == drop.Id)
                                  .SingleOrDefaultAsync();

            drop.Winner       = null;
            drop.WinnerId     = null;
            drop.WinningEntry = null;

            if (oldWinningEntry is not null)
            {
                oldWinningEntry.Drop   = null;
                oldWinningEntry.DropId = null;

                await foreach (var pass in _context.DropPasses
                               .AsTracking()
                               .Where(pass => pass.WonEntryId == oldWinningEntry.Id)
                               .AsAsyncEnumerable())
                {
                    pass.WonEntryId = null;
                }
            }
        }

        await _context.SaveChangesAsync();

        var kill = await _context.EncounterKills
                   .AsNoTracking()
                   .Where(kill => kill.EncounterId == drop.EncounterKillEncounterId && kill.RaidId == drop.EncounterKillRaidId && kill.TrashIndex == drop.EncounterKillTrashIndex)
                   .Select(kill => new { kill.DiscordMessageId, kill.KilledAt, kill.RaidId, TeamName = kill.Raid.RaidTeam.Name, EncounterName = kill.Encounter.Name })
                   .FirstAsync();

        var drops = new List <(uint, string, string?)>();

        await foreach (var d in _context.Drops
                       .AsNoTracking()
                       .Where(d => d.EncounterKillEncounterId == drop.EncounterKillEncounterId && d.EncounterKillRaidId == drop.EncounterKillRaidId && d.EncounterKillTrashIndex == drop.EncounterKillTrashIndex)
                       .Select(d => new { d.ItemId, ItemName = d.Item.Name, WinnerName = (string?)d.Winner !.Name })