Skip to content

Commit f766e10

Browse files
committed
Add support for aptitudes on characters
1 parent 8421202 commit f766e10

File tree

54 files changed

+1485
-369
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1485
-369
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace Naheulbook.Core.Features.Aptitude;
2+
3+
[Serializable]
4+
public class AptitudeNotAvailableForOriginException(Guid aptitudeId) : Exception
5+
{
6+
public Guid AptitudeId { get; } = aptitudeId;
7+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Naheulbook.Core.Features.Aptitude;
2+
3+
public class AptitudeNotFoundException(Guid aptitudeId) : Exception
4+
{
5+
public Guid AptitudeId { get; } = aptitudeId;
6+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Naheulbook.Data.EntityFrameworkCore.Entities;
2+
3+
namespace Naheulbook.Core.Features.Aptitude;
4+
5+
public interface ICharacterAptitudeFactory
6+
{
7+
public CharacterAptitudeEntity Create(int characterId, Guid aptitudeId);
8+
}
9+
10+
public class CharacterAptitudeFactory : ICharacterAptitudeFactory
11+
{
12+
public CharacterAptitudeEntity Create(int characterId, Guid aptitudeId)
13+
{
14+
return new CharacterAptitudeEntity
15+
{
16+
CharacterId = characterId,
17+
AptitudeId = aptitudeId,
18+
Count = 1,
19+
Active = false,
20+
};
21+
}
22+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
using Naheulbook.Core.Features.Character;
2+
using Naheulbook.Core.Features.Shared;
3+
using Naheulbook.Core.Notifications;
4+
using Naheulbook.Data.EntityFrameworkCore.Entities;
5+
using Naheulbook.Data.UnitOfWorks;
6+
using Naheulbook.Requests.Requests;
7+
8+
namespace Naheulbook.Core.Features.Aptitude;
9+
10+
public interface ICharacterAptitudeService
11+
{
12+
Task<CharacterAptitudeEntity> AddAptitudeAsync(
13+
NaheulbookExecutionContext executionContext,
14+
int characterId,
15+
CharacterAddAptitudeRequest request,
16+
CancellationToken cancellationToken = default
17+
);
18+
19+
Task RemoveAptitudeAsync(
20+
NaheulbookExecutionContext executionContext,
21+
int characterId,
22+
Guid aptitudeId,
23+
CancellationToken cancellationToken = default
24+
);
25+
26+
Task UpdateCharacterAptitudeAsync(
27+
NaheulbookExecutionContext executionContext,
28+
int characterId,
29+
Guid aptitudeId,
30+
UpdateCharacterAptitudeRequest request,
31+
CancellationToken cancellationToken = default
32+
);
33+
}
34+
35+
public class CharacterAptitudeService(
36+
IUnitOfWorkFactory unitOfWorkFactory,
37+
IAuthorizationUtil authorizationUtil,
38+
INotificationSessionFactory notificationSessionFactory,
39+
ICharacterHistoryUtil characterHistoryUtil
40+
) : ICharacterAptitudeService
41+
{
42+
public async Task<CharacterAptitudeEntity> AddAptitudeAsync(
43+
NaheulbookExecutionContext executionContext,
44+
int characterId,
45+
CharacterAddAptitudeRequest request,
46+
CancellationToken cancellationToken = default
47+
)
48+
{
49+
using var uow = unitOfWorkFactory.CreateUnitOfWork();
50+
51+
var character = await uow.Characters.GetWithGroupWithOriginAsync(characterId);
52+
if (character == null)
53+
throw new CharacterNotFoundException(characterId);
54+
55+
authorizationUtil.EnsureCharacterAccess(executionContext, character);
56+
57+
var aptitude = await uow.AptitudeRepository.GetAsync(request.AptitudeId);
58+
if (aptitude == null)
59+
throw new AptitudeNotFoundException(request.AptitudeId);
60+
61+
if (aptitude.AptitudeGroupId != character.Origin.AptitudeGroupId)
62+
throw new AptitudeNotAvailableForOriginException(request.AptitudeId);
63+
64+
var notificationSession = notificationSessionFactory.CreateSession();
65+
66+
var characterAptitude = await uow.CharacterAptitudes.GetByCharacterIdAndAptitudeIdAsync(characterId, request.AptitudeId, cancellationToken);
67+
if (characterAptitude != null)
68+
characterAptitude.Count++;
69+
else
70+
{
71+
characterAptitude = new CharacterAptitudeEntity
72+
{
73+
Character = character,
74+
Aptitude = aptitude,
75+
Count = 1,
76+
Active = false,
77+
};
78+
uow.CharacterAptitudes.Add(characterAptitude);
79+
}
80+
81+
character.AddHistoryEntry(characterHistoryUtil.CreateAddAptitude(characterId, aptitude.Id));
82+
83+
notificationSession.NotifyCharacterAddAptitude(characterId, characterAptitude);
84+
85+
await uow.SaveChangesAsync();
86+
await notificationSession.CommitAsync();
87+
88+
return characterAptitude;
89+
}
90+
91+
public async Task RemoveAptitudeAsync(
92+
NaheulbookExecutionContext executionContext,
93+
int characterId,
94+
Guid aptitudeId,
95+
CancellationToken cancellationToken = default
96+
)
97+
{
98+
using var uow = unitOfWorkFactory.CreateUnitOfWork();
99+
100+
var character = await uow.Characters.GetWithGroupWithOriginAsync(characterId);
101+
if (character == null)
102+
throw new CharacterNotFoundException(characterId);
103+
104+
var characterAptitude = await uow.CharacterAptitudes.GetByCharacterIdAndAptitudeIdAsync(characterId, aptitudeId, cancellationToken);
105+
if (characterAptitude == null)
106+
throw new AptitudeNotFoundException(aptitudeId);
107+
108+
authorizationUtil.EnsureCharacterAccess(executionContext, character);
109+
110+
111+
characterAptitude.Count--;
112+
if (characterAptitude.Count <= 0)
113+
uow.CharacterAptitudes.Remove(characterAptitude);
114+
character.AddHistoryEntry(characterHistoryUtil.CreateRemoveAptitude(characterId, aptitudeId));
115+
116+
var notificationSession = notificationSessionFactory.CreateSession();
117+
notificationSession.NotifyCharacterRemoveAptitude(characterId, characterAptitude);
118+
119+
await uow.SaveChangesAsync();
120+
await notificationSession.CommitAsync();
121+
}
122+
123+
public async Task UpdateCharacterAptitudeAsync(
124+
NaheulbookExecutionContext executionContext,
125+
int characterId,
126+
Guid aptitudeId,
127+
UpdateCharacterAptitudeRequest request,
128+
CancellationToken cancellationToken = default
129+
)
130+
{
131+
using var uow = unitOfWorkFactory.CreateUnitOfWork();
132+
133+
var character = await uow.Characters.GetWithGroupWithOriginAsync(characterId);
134+
if (character == null)
135+
throw new CharacterNotFoundException(characterId);
136+
137+
var characterAptitude = await uow.CharacterAptitudes.GetByCharacterIdAndAptitudeIdAsync(characterId, aptitudeId, cancellationToken);
138+
if (characterAptitude == null)
139+
throw new AptitudeNotFoundException(aptitudeId);
140+
141+
authorizationUtil.EnsureCharacterAccess(executionContext, character);
142+
143+
characterAptitude.Active = request.Active;
144+
145+
var notificationSession = notificationSessionFactory.CreateSession();
146+
notificationSession.NotifyCharacterUpdateAptitude(characterId, characterAptitude);
147+
148+
await uow.SaveChangesAsync();
149+
await notificationSession.CommitAsync();
150+
}
151+
}

backend/src/Naheulbook.Core/Features/Aptitude/ServiceCollectionExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Microsoft.Extensions.DependencyInjection;
2+
using Naheulbook.Core.Features.Character;
23
using Naheulbook.Shared.Extensions;
34

45
namespace Naheulbook.Core.Features.Aptitude;
@@ -11,5 +12,7 @@ public static void AddAptitudeService(this IServiceCollection services)
1112
return;
1213

1314
services.AddSingleton<IAptitudeGroupService, AptitudeGroupService>();
15+
services.AddSingleton<ICharacterAptitudeFactory, CharacterAptitudeFactory>();
16+
services.AddSingleton<ICharacterAptitudeService, CharacterAptitudeService>();
1417
}
1518
}

backend/src/Naheulbook.Core/Features/Character/CharacterHistoryUtil.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public interface ICharacterHistoryUtil
2626
CharacterHistoryEntryEntity CreateLogReadBook(int characterId, ItemEntity item);
2727
CharacterHistoryEntryEntity CreateLogIdentifyItem(int characterId, ItemEntity item);
2828
CharacterHistoryEntryEntity CreateLogLevelUp(int characterId, int characterLevel);
29+
CharacterHistoryEntryEntity CreateAddAptitude(int characterId, Guid aptitudeId);
30+
CharacterHistoryEntryEntity CreateRemoveAptitude(int characterId, Guid aptitudeId);
2931
}
3032

3133
public class CharacterHistoryUtil(IJsonUtil jsonUtil) : ICharacterHistoryUtil
@@ -51,6 +53,8 @@ public class CharacterHistoryUtil(IJsonUtil jsonUtil) : ICharacterHistoryUtil
5153
private const string ReadBookActionName = "READ_BOOK";
5254
private const string IdentifyActionName = "IDENTIFY";
5355
private const string LevelUpActionName = "LEVEL_UP";
56+
private const string AddAptitudeActionName = "ADD_APTITUDE";
57+
private const string RemoveAptitudeActionName = "REMOVE_APTITUDE";
5458

5559
public CharacterHistoryEntryEntity CreateLogChangeEv(CharacterEntity character, int? oldValue, int? newValue)
5660
{
@@ -284,4 +288,26 @@ public CharacterHistoryEntryEntity CreateLogLevelUp(int characterId, int level)
284288
Info = level.ToString(),
285289
};
286290
}
291+
292+
public CharacterHistoryEntryEntity CreateAddAptitude(int characterId, Guid aptitudeId)
293+
{
294+
return new CharacterHistoryEntryEntity
295+
{
296+
CharacterId = characterId,
297+
Action = AddAptitudeActionName,
298+
Date = DateTime.Now,
299+
AptitudeId = aptitudeId,
300+
};
301+
}
302+
303+
public CharacterHistoryEntryEntity CreateRemoveAptitude(int characterId, Guid aptitudeId)
304+
{
305+
return new CharacterHistoryEntryEntity
306+
{
307+
CharacterId = characterId,
308+
Action = RemoveAptitudeActionName,
309+
Date = DateTime.Now,
310+
AptitudeId = aptitudeId,
311+
};
312+
}
287313
}

backend/src/Naheulbook.Core/Notifications/INotificationPacketBuilder.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public interface INotificationPacketBuilder
2828
INotificationPacket BuildCharacterLevelUp(int characterId, LevelUpResult levelUpResult);
2929
INotificationPacket BuildCharacterAddJob(int characterId, Guid jobId);
3030
INotificationPacket BuildCharacterRemoveJob(int characterId, Guid jobId);
31+
INotificationPacket BuildCharacterAddAptitude(int characterId, CharacterAptitudeEntity characterAptitude);
32+
INotificationPacket BuildCharacterRemoveAptitude(int characterId, CharacterAptitudeEntity characterAptitude);
33+
INotificationPacket BuildCharacterUpdateAptitude(int characterId, CharacterAptitudeEntity characterAptitude);
3134

3235
INotificationPacket BuildCharacterChangeColor(CharacterEntity character);
3336
INotificationPacket BuildCharacterChangeTarget(CharacterEntity character, TargetRequest requestTarget);

backend/src/Naheulbook.Core/Notifications/NotificationSession.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public interface INotificationSession
2828
void NotifyCharacterLevelUp(int characterId, LevelUpResult levelUpResult);
2929
void NotifyCharacterAddJob(int characterId, Guid jobId);
3030
void NotifyCharacterRemoveJob(int characterId, Guid jobId);
31+
void NotifyCharacterAddAptitude(int characterId, CharacterAptitudeEntity characterAptitude);
32+
void NotifyCharacterRemoveAptitude(int characterId, CharacterAptitudeEntity characterAptitude);
33+
void NotifyCharacterUpdateAptitude(int characterId, CharacterAptitudeEntity characterAptitude);
3134

3235
void NotifyCharacterGmChangeColor(CharacterEntity character);
3336
void NotifyCharacterGmChangeTarget(CharacterEntity character, TargetRequest requestTarget);
@@ -145,6 +148,21 @@ public void NotifyCharacterRemoveJob(int characterId, Guid jobId)
145148
_packets.Add(packetBuilder.BuildCharacterRemoveJob(characterId, jobId));
146149
}
147150

151+
public void NotifyCharacterAddAptitude(int characterId, CharacterAptitudeEntity characterAptitude)
152+
{
153+
_packets.Add(packetBuilder.BuildCharacterAddAptitude(characterId, characterAptitude));
154+
}
155+
156+
public void NotifyCharacterRemoveAptitude(int characterId, CharacterAptitudeEntity characterAptitude)
157+
{
158+
_packets.Add(packetBuilder.BuildCharacterRemoveAptitude(characterId, characterAptitude));
159+
}
160+
161+
public void NotifyCharacterUpdateAptitude(int characterId, CharacterAptitudeEntity characterAptitude)
162+
{
163+
_packets.Add(packetBuilder.BuildCharacterUpdateAptitude(characterId, characterAptitude));
164+
}
165+
148166
public void NotifyCharacterGmChangeColor(CharacterEntity character)
149167
{
150168
_packets.Add(packetBuilder.BuildCharacterChangeColor(character));

backend/src/Naheulbook.Data.EntityFrameworkCore/DbContexts/NaheulbookDbContext.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public class NaheulbookDbContext(DbContextOptions<NaheulbookDbContext> options)
1010
public DbSet<AptitudeEntity> Aptitudes { get; set; } = null!;
1111
public DbSet<AptitudeGroupEntity> AptitudeGroups { get; set; } = null!;
1212
public DbSet<CharacterEntity> Characters { get; set; } = null!;
13+
public DbSet<CharacterAptitudeEntity> CharacterAptitudes { get; set; } = null!;
1314
public DbSet<CharacterModifierEntity> CharacterModifiers { get; set; } = null!;
1415
public DbSet<CharacterHistoryEntryEntity> CharacterHistory { get; set; } = null!;
1516
public DbSet<EffectEntity> Effects { get; set; } = null!;

backend/src/Naheulbook.Data.EntityFrameworkCore/Entities/CharacterAptitudeEntity.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
namespace Naheulbook.Data.EntityFrameworkCore.Entities;
44

5+
[Serializable]
56
public class CharacterAptitudeEntity
67
{
78
public int CharacterId { get; set; }
89
private CharacterEntity? _character;
910
public CharacterEntity Character { get => _character.ThrowIfNotLoaded(); set => _character = value; }
1011

11-
public int AptitudeId { get; set; }
12+
public Guid AptitudeId { get; set; }
1213
private AptitudeEntity? _aptitude;
1314
public AptitudeEntity Aptitude { get => _aptitude.ThrowIfNotLoaded(); set => _aptitude = value; }
1415

0 commit comments

Comments
 (0)