文字游戏
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

355 lines
14 KiB

using Build_God_Api.DB;
using Build_God_Api.Dto;
using SqlSugar;
namespace Build_God_Api.Services
{
public interface IDailyMissionService
{
/// <summary>
/// 获取角色所有未领取的任务(包括昨日的)
/// </summary>
Task<List<DailyMissionDto>> GetCharacterDailyMissions(int characterId);
/// <summary>
/// 接取任务
/// </summary>
Task<bool> AcceptMission(int characterId, int dailyMissionId);
/// <summary>
/// 领取奖励
/// </summary>
Task<bool> ClaimReward(int characterId, int dailyMissionId);
/// <summary>
/// 检查并完成所有到期任务
/// </summary>
Task CheckAndCompleteExpiredMissions();
/// <summary>
/// 为角色分配每日任务(登录时调用)
/// </summary>
Task AssignDailyMissionsIfNeeded(int characterId);
}
public class DailyMissionDto
{
public int Id { get; set; }
public int CharacterId { get; set; }
public int MissionId { get; set; }
public string MissionName { get; set; } = string.Empty;
public string MissionTitle { get; set; } = string.Empty;
public string MissionDescription { get; set; } = string.Empty;
public int SpendTimeMinutes { get; set; }
public DailyMissionStatus Status { get; set; }
public DateTime? StartTime { get; set; }
public DateTime? EndTime { get; set; }
public bool IsFromYesterday { get; set; }
public DateTime AssignedDate { get; set; }
public int ExpReward { get; set; }
public int TodayClaimedCount { get; set; }
public int TodayTotalCount { get; set; }
public List<MissionRewardDto> Rewards { get; set; } = new();
}
public class MissionRewardDto
{
public RewardType RewardType { get; set; }
public string RewardTypeName { get; set; } = string.Empty;
public int ItemId { get; set; }
public string ItemName { get; set; } = string.Empty;
public int Count { get; set; }
}
public class DailyMissionService(
ISqlSugarClient db,
IBagService bagService,
ILogger<DailyMissionService> logger
) : IDailyMissionService
{
private readonly ISqlSugarClient _db = db;
private readonly IBagService _bagService = bagService;
private readonly ILogger<DailyMissionService> _logger = logger;
private const int DailyMissionCount = 5;
private const int HardMissionCount = 2;
private const int PurgatoryMissionCount = 1;
private static readonly TimeZoneInfo BeijingTimeZone = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
private int CalculateExpReward(MissionDifficulty difficulty, int levelId)
{
var coefficient = difficulty switch
{
MissionDifficulty.Normal => 2,
MissionDifficulty.Hard => 4,
MissionDifficulty.Purgatory => 6,
_ => 2
};
return coefficient * levelId * levelId * levelId;
}
private DateTime GetBeijingTime()
{
return TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, BeijingTimeZone);
}
private DateTime GetBeijingDate()
{
return GetBeijingTime().Date;
}
public async Task<List<DailyMissionDto>> GetCharacterDailyMissions(int characterId)
{
var today = GetBeijingDate();
var yesterday = today.AddDays(-1);
var dailyMissions = await _db.Queryable<CharacterDailyMission>()
.Where(x => x.CharacterId == characterId)
.Where(x => x.AssignedDate == today || x.AssignedDate == yesterday)
.OrderByDescending(x => x.AssignedDate)
.OrderByDescending(x => x.IsFromYesterday)
.ToListAsync();
var character = await _db.Queryable<Character>()
.FirstAsync(x => x.Id == characterId)
?? throw new Exception("角色不存在");
var todayMissions = dailyMissions.Where(x => x.AssignedDate == today).ToList();
var todayClaimedCount = todayMissions.Count(x => x.Status == DailyMissionStatus.Claimed);
var todayTotalCount = todayMissions.Count;
var result = new List<DailyMissionDto>();
foreach (var dm in dailyMissions)
{
var mission = await _db.Queryable<Mission>()
.Includes(x => x.Rewards)
.FirstAsync(x => x.Id == dm.MissionId);
if (mission == null) continue;
var dto = new DailyMissionDto
{
Id = dm.Id,
CharacterId = dm.CharacterId,
MissionId = dm.MissionId,
MissionName = mission.Name,
MissionTitle = mission.Title,
MissionDescription = mission.Description,
SpendTimeMinutes = mission.SpendTimeMinutes,
Status = dm.Status,
StartTime = dm.StartTime,
EndTime = dm.EndTime,
IsFromYesterday = dm.IsFromYesterday,
AssignedDate = dm.AssignedDate,
ExpReward = CalculateExpReward(mission.Difficulty, character.LevelId),
TodayClaimedCount = todayClaimedCount,
TodayTotalCount = todayTotalCount
};
if (mission.Rewards != null)
{
foreach (var reward in mission.Rewards)
{
dto.Rewards.Add(new MissionRewardDto
{
RewardType = reward.RewardType,
RewardTypeName = reward.RewardType.ToString(),
ItemId = reward.ItemId,
ItemName = reward.ItemName ?? string.Empty,
Count = reward.Count
});
}
}
result.Add(dto);
}
return result;
}
public async Task<bool> AcceptMission(int characterId, int dailyMissionId)
{
var existingInProgress = await _db.Queryable<CharacterDailyMission>()
.Where(x => x.CharacterId == characterId && x.Status == DailyMissionStatus.InProgress)
.AnyAsync();
if (existingInProgress)
throw new Exception("已有任务在执行中,请先完成当前任务");
var dailyMission = await _db.Queryable<CharacterDailyMission>()
.FirstAsync(x => x.Id == dailyMissionId && x.CharacterId == characterId)
?? throw new Exception("任务不存在");
if (dailyMission.Status != DailyMissionStatus.Pending)
throw new Exception("任务不是待接取状态");
var mission = await _db.Queryable<Mission>()
.FirstAsync(x => x.Id == dailyMission.MissionId)
?? throw new Exception("任务不存在");
var now = GetBeijingTime();
dailyMission.Status = DailyMissionStatus.InProgress;
dailyMission.StartTime = now;
dailyMission.EndTime = now.AddMinutes(mission.SpendTimeMinutes);
await _db.Updateable(dailyMission).ExecuteCommandAsync();
_logger.LogInformation("角色 {CharacterId} 接取了任务 {MissionId}", characterId, dailyMission.MissionId);
return true;
}
public async Task<bool> ClaimReward(int characterId, int dailyMissionId)
{
var dailyMission = await _db.Queryable<CharacterDailyMission>()
.FirstAsync(x => x.Id == dailyMissionId && x.CharacterId == characterId)
?? throw new Exception("任务不存在");
if (dailyMission.Status != DailyMissionStatus.Completed)
throw new Exception("任务未完成,无法领取奖励");
var mission = await _db.Queryable<Mission>()
.Includes(x => x.Rewards)
.FirstAsync(x => x.Id == dailyMission.MissionId)
?? throw new Exception("任务不存在");
var character = await _db.Queryable<Character>()
.FirstAsync(x => x.Id == characterId)
?? throw new Exception("角色不存在");
var expReward = CalculateExpReward(mission.Difficulty, character.LevelId);
character.CurrentExp += expReward;
_logger.LogInformation("角色 {CharacterId} 领取任务奖励:经验 +{ExpReward}", characterId, expReward);
if (mission.Rewards != null)
{
foreach (var reward in mission.Rewards)
{
switch (reward.RewardType)
{
case RewardType.Money:
character.Money += reward.Count;
_logger.LogInformation("角色 {CharacterId} 领取任务奖励:灵石 +{Count}", characterId, reward.Count);
break;
case RewardType.Pill:
case RewardType.Equipment:
var characterBag = await _db.Queryable<CharacterBag>()
.FirstAsync(x => x.CharacterId == characterId);
if (characterBag != null)
{
await _bagService.AddItemToBag(characterBag.Id, (int)reward.RewardType, reward.ItemId, reward.Count);
_logger.LogInformation("角色 {CharacterId} 领取任务奖励:{ItemName} x{Count}", characterId, reward.ItemName, reward.Count);
}
break;
}
}
}
await _db.Updateable(character).ExecuteCommandAsync();
dailyMission.Status = DailyMissionStatus.Claimed;
await _db.Updateable(dailyMission).ExecuteCommandAsync();
_logger.LogInformation("角色 {CharacterId} 领取了任务 {MissionId} 的奖励", characterId, mission.Id);
return true;
}
public async Task CheckAndCompleteExpiredMissions()
{
var now = GetBeijingTime();
var expiredMissions = await _db.Queryable<CharacterDailyMission>()
.Where(x => x.Status == DailyMissionStatus.InProgress && x.EndTime <= now)
.ToListAsync();
foreach (var mission in expiredMissions)
{
mission.Status = DailyMissionStatus.Completed;
await _db.Updateable(mission).ExecuteCommandAsync();
_logger.LogInformation("任务 {MissionId} 已自动完成,等待角色 {CharacterId} 领取", mission.MissionId, mission.CharacterId);
}
}
public async Task AssignDailyMissionsIfNeeded(int characterId)
{
var today = GetBeijingDate();
var character = await _db.Queryable<Character>()
.FirstAsync(x => x.Id == characterId)
?? throw new Exception("角色不存在");
var existingMissions = await _db.Queryable<CharacterDailyMission>()
.Where(x => x.CharacterId == characterId && x.AssignedDate == today)
.ToListAsync();
var hasInProgressOrPending = existingMissions.Any(x =>
x.Status == DailyMissionStatus.InProgress || x.Status == DailyMissionStatus.Pending);
if (hasInProgressOrPending)
{
_logger.LogInformation("角色 {CharacterId} 今日已有任务,无需分配", characterId);
return;
}
var availableMissions = await _db.Queryable<Mission>()
.Where(x => x.Type == MissionType.DailyTask)
.Where(x => x.IsAvailable == true)
.Where(x => x.RequiredLevelId <= character.LevelId)
.ToListAsync();
if (!availableMissions.Any())
{
_logger.LogWarning("没有可用的日常任务配置");
return;
}
var random = new Random();
var normalMissions = availableMissions
.Where(x => x.Difficulty == MissionDifficulty.Normal)
//.Where(x => random.Next(100) < (int)x.ObtainPercentage)
//.OrderBy(x => random.Next())
.Take(DailyMissionCount)
.ToList();
var hardMissions = availableMissions
.Where(x => x.Difficulty == MissionDifficulty.Hard)
//.Where(x => random.Next(100) < (int)x.ObtainPercentage)
//.OrderBy(x => random.Next())
.Take(HardMissionCount)
.ToList();
var purgatoryMissions = availableMissions
.Where(x => x.Difficulty == MissionDifficulty.Purgatory)
//.Where(x => random.Next(100) < (int)x.ObtainPercentage)
//.OrderBy(x => random.Next())
.Take(PurgatoryMissionCount)
.ToList();
var allSelectedMissions = normalMissions.Concat(hardMissions).Concat(purgatoryMissions).ToList();
foreach (var mission in allSelectedMissions)
{
var dailyMission = new CharacterDailyMission
{
CharacterId = characterId,
MissionId = mission.Id,
AssignedDate = today,
Status = DailyMissionStatus.Pending,
IsFromYesterday = false,
CreatedOn = DateTime.UtcNow
};
await _db.Insertable(dailyMission).ExecuteCommandAsync();
}
_logger.LogInformation("为角色 {CharacterId} 分配了 {Count} 个每日任务(普通:{NormalCount}, 困难:{HardCount}, 炼狱:{PurgatoryCount})",
characterId, allSelectedMissions.Count, normalMissions.Count, hardMissions.Count, purgatoryMissions.Count);
}
}
}