Skip to content

Commit

Permalink
feat: 增加獵人觸發技能 (#67)
Browse files Browse the repository at this point in the history
  • Loading branch information
ricksu978 committed Aug 20, 2023
1 parent e0d5adb commit e3ebc39
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 0 deletions.
44 changes: 44 additions & 0 deletions src/BackEnd/src/Application/UseCases/PlayerTriggerSkillUseCase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Wsa.Gaas.Werewolf.Application.Common;
using Wsa.Gaas.Werewolf.Domain.Events;
using Wsa.Gaas.Werewolf.Domain.Exceptions;

namespace Wsa.Gaas.Werewolf.Application.UseCases
{
public class PlayerTriggerSkillRequest
{
public ulong DiscordVoiceChannelId { get; set; }
public ulong PlayerId { get; set; }
public ulong TargetPlayerId { get; set; }
}

public class PlayerTriggerSkillUseCase : UseCase<PlayerTriggerSkillRequest, PlayerTriggerSkillEvent>
{
public PlayerTriggerSkillUseCase(IRepository repository, GameEventBus gameEventBus) : base(repository, gameEventBus)
{
}

public override async Task ExecuteAsync(PlayerTriggerSkillRequest request, IPresenter<PlayerTriggerSkillEvent> presenter, CancellationToken cancellationToken = default)
{
// 查
var game = Repository.FindByDiscordChannelId(request.DiscordVoiceChannelId);

if(game == null)
{
throw new GameNotFoundException(request.DiscordVoiceChannelId);
}

// 改
var @event = game.TriggerPlayerSkill(
request.PlayerId,
request.TargetPlayerId
);

// 存
await Repository.SaveAsync(game);

// 推
await presenter.PresentAsync(@event, cancellationToken);

}
}
}
9 changes: 9 additions & 0 deletions src/BackEnd/src/Domain/Events/PlayerTriggerSkillEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Wsa.Gaas.Werewolf.Domain.Objects;

namespace Wsa.Gaas.Werewolf.Domain.Events
{
public class PlayerTriggerSkillEvent : GameEvent
{
public PlayerTriggerSkillEvent(Game data) : base(data) { }
}
}
30 changes: 30 additions & 0 deletions src/BackEnd/src/Domain/Objects/Game.cs
Original file line number Diff line number Diff line change
Expand Up @@ -309,4 +309,34 @@ public WitchUsePoisonEvent WitchUsePoison(ulong witchUserId, ulong targetPlayerI

return new WitchUsePoisonEvent(this);
}

public PlayerTriggerSkillEvent TriggerPlayerSkill(ulong playerId, ulong targetPlayerId)
{
// 檢查 playerId 是獵人
var checkHunter = Players.Single(p => p.UserId == playerId).Role is Hunter;
if (!checkHunter)
{
throw new GameException("This player is not hunter");
}

// 檢查 targetPlayerId 還活著
var targetPlayer = Players.FirstOrDefault(x =>
x.UserId == targetPlayerId
);

if (targetPlayer == null)
{
throw new PlayerNotFoundException(DiscordVoiceChannelId, targetPlayerId);
}

if (targetPlayer.IsDead)
{
throw new GameException("Target player is already dead");
}

// 標記玩家死亡
targetPlayer.IsDead = true;

return new PlayerTriggerSkillEvent(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Wsa.Gaas.Werewolf.Application.UseCases;
using Wsa.Gaas.Werewolf.Domain.Events;
using Wsa.Gaas.Werewolf.WebApi.Common;

namespace Wsa.Gaas.Werewolf.WebApi.Endpoints
{
public class PlayerTriggerSkillResponse { }

public class PlayerTriggerSkillEndpoint : WebApiEndpoint<PlayerTriggerSkillRequest, PlayerTriggerSkillEvent, PlayerTriggerSkillResponse>
{
public override void Configure()
{
Post("/games/{DiscordVoiceChannelId}/players/{PlayerId}:killPlayer");
AllowAnonymous();
}

public override async Task<PlayerTriggerSkillResponse> ExecuteAsync(PlayerTriggerSkillRequest req, CancellationToken ct)
{
await UseCase.ExecuteAsync(req, this, ct);

if (ViewModel == null)
{
throw new Exception("View Model is null");
}

return ViewModel;
}

public override Task PresentAsync(PlayerTriggerSkillEvent gameEvent, CancellationToken cancellationToken = default)
{
ViewModel = new PlayerTriggerSkillResponse(
//gameEvent.Data.DiscordVoiceChannelId.ToString(),
//gameEvent.PlayerId.ToString()
);

return Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using FastEndpoints;
using Wsa.Gaas.Werewolf.Application.Common;
using Wsa.Gaas.Werewolf.Application.UseCases;
using Wsa.Gaas.Werewolf.Domain.Objects;
using Wsa.Gaas.Werewolf.Domain.Objects.Roles;
using Wsa.Gaas.Werewolf.WebApi.Endpoints;
using Wsa.Gaas.Werewolf.WebApiTests.ATDD.Common;

namespace Wsa.Gaas.Werewolf.WebApiTests.ATDD.GameTests
{
internal class PlayerTriggerSkillTests
{
readonly WebApiTestServer _server = new();

[OneTimeSetUp]
public async Task OneTimeSetup()
{
await _server.StartAsync();
}

[Test]
[Description("""
Given:
9 位玩家 (1,2,3,4,5,6,7,8,9)
3 號玩家是獵人
3 號玩家出局後
When:
3 號玩家選擇 4 號玩家一起出局
Then:
4 號玩家出局
""")]
public async Task PlayerTriggerTest()
{
// Arrange
var players = Enumerable.Range(0, 10)
.Select(x => (ulong)x)
.ToArray();

var game = _server.CreateGameBuilder()
.WithRandomDiscordVoiceChannel()
.WithGameStatus(GameStatus.Created)
.Build();

game.StartGame(players);

// 3 號玩家是獵人
// 3 號玩家出局後
var hunter = game.Players.First(x => x.Role is Hunter);
hunter.IsDead = true;

// target Player
var targetPlayer = game.Players.First(x => x.UserId != hunter.UserId);

// 宣布昨晚結果
game.Status = GameStatus.LastNightResultAnnounced;

var request = new PlayerTriggerSkillRequest
{
DiscordVoiceChannelId = game.DiscordVoiceChannelId,
PlayerId = hunter.UserId,
TargetPlayerId = targetPlayer.UserId,
};

// Act => 呼叫 API
var (response, result) = await _server.Client.POSTAsync<PlayerTriggerSkillEndpoint, PlayerTriggerSkillRequest, PlayerTriggerSkillResponse>(request);

// Assert Http Response
response.EnsureSuccessStatusCode();

// Assert Repository
var repository = _server.GetRequiredService<IRepository>();
var actualGame = await repository.FindByDiscordChannelIdAsync(game.DiscordVoiceChannelId);
actualGame!.Should().NotBeNull();

// Target Player 真的死掉
var actualTargetPlayer = actualGame!.Players.FirstOrDefault(x => x.UserId == targetPlayer.UserId);
actualTargetPlayer!.IsDead.Should().BeTrue();

}
}
}

0 comments on commit e3ebc39

Please sign in to comment.