internal IInnerPlayerControl? ProtectedBy { get; set; }
+ internal bool IsProtected
+ {
+ get
+ {
+ // HnS doesn't have guardian angels
+ if (Game.Options is NormalGameOptions normalGameOptions && ProtectedOn != null)
+ {
+ var guardianAngelOptions = (GuardianAngelRoleOptions)normalGameOptions.RoleOptions.Roles[RoleTypes.GuardianAngel].RoleOptions;
+ var protectionExpiresAt = ProtectedOn.Value.AddSeconds(guardianAngelOptions.ProtectionDurationSeconds);
+ return protectionExpiresAt >= _dateTimeProvider.UtcNow;
+ }
+
+ return false;
+ }
+ }
+
public override ValueTask<bool> SerializeAsync(IMessageWriter writer, bool initialState)
{
throw new NotImplementedException();
}
Rpc12MurderPlayer.Deserialize(reader, Game, out var murdered, out var result);
- return await HandleMurderPlayer(sender, murdered, result);
+ return await HandleMurderPlayer(sender, (InnerPlayerControl?)murdered, result);
}
case RpcCalls.SendChat:
return false;
}
- Rpc45ProtectPlayer.Deserialize(reader, Game, out _, out _);
- break;
+ Rpc45ProtectPlayer.Deserialize(reader, Game, out var protectTarget, out _);
+ return await HandleProtectPlayer(sender, protectTarget);
}
case RpcCalls.Shapeshift:
}
Rpc47CheckMurder.Deserialize(reader, Game, out var murdered);
- return await HandleCheckMurder(sender, murdered);
+ return await HandleCheckMurder(sender, (InnerPlayerControl?)murdered);
}
case RpcCalls.CheckProtect:
return false;
}
- Rpc48CheckProtect.Deserialize(reader, Game, out var protectTarget);
- return await HandleCheckProtect(sender, protectTarget);
+ Rpc48CheckProtect.Deserialize(reader, Game, out _);
+ break;
}
default:
ProtectedBy = guardianAngel;
}
- internal bool IsProtected()
- {
- // HnS doesn't have guardian angels
- if (Game.Options.GameMode == GameModes.Normal && ProtectedOn != null)
- {
- var opts = (NormalGameOptions)Game.Options;
- var guardianAngelOpts = (GuardianAngelRoleOptions)opts.RoleOptions.Roles[RoleTypes.GuardianAngel].RoleOptions;
- var duration = guardianAngelOpts.ProtectionDurationSeconds;
- var protectionExpiresAt = ProtectedOn.Value.AddSeconds(duration);
- return protectionExpiresAt >= _dateTimeProvider.UtcNow;
- }
- else
- {
- return false;
- }
- }
-
private async ValueTask HandleCompleteTask(ClientPlayer sender, uint taskId)
{
var task = PlayerInfo.Tasks.ElementAtOrDefault((int)taskId);
return true;
}
- private async ValueTask<bool> HandleCheckMurder(ClientPlayer sender, IInnerPlayerControl? target)
+ private async ValueTask<bool> HandleCheckMurder(ClientPlayer sender, InnerPlayerControl? target)
{
if (!PlayerInfo.CanMurder(Game, _dateTimeProvider))
{
if (target != null)
{
- var tgt = (InnerPlayerControl)target;
- var result = tgt.IsProtected() ? MurderResultFlags.FailedProtected : MurderResultFlags.Succeeded;
+ var result = target.IsProtected ? MurderResultFlags.FailedProtected : MurderResultFlags.Succeeded;
var evt = new PlayerCheckMurderEvent(Game, sender, this, target, result);
await _eventManager.CallAsync(evt);
if (!evt.IsCancelled)
{
- tgt.ProtectedOn = null; // Clear GA protection in all cases
+ target.ProtectedOn = null; // Clear GA protection in all cases
await MurderPlayerAsync(target, evt.Result);
}
}
return false;
}
- private async ValueTask<bool> HandleMurderPlayer(ClientPlayer sender, IInnerPlayerControl? target, MurderResultFlags result)
+ private async ValueTask<bool> HandleMurderPlayer(ClientPlayer sender, InnerPlayerControl? target, MurderResultFlags result)
{
if (!_game.IsHostAuthoritive)
{
if (target != null && !target.PlayerInfo.IsDead)
{
- if ((result & (MurderResultFlags.FailedError | MurderResultFlags.FailedProtected)) == 0)
+ // In host authoritive mode every client has to figure out if the kill was prevented by guardian protection on it's own
+ if ((result & MurderResultFlags.Succeeded) != 0 && target.IsProtected)
{
- ((InnerPlayerControl)target).Die(DeathReason.Kill);
+ result = (result & ~MurderResultFlags.Succeeded) | MurderResultFlags.FailedProtected;
+ }
+
+ if (!result.IsFailed())
+ {
+ target.Die(DeathReason.Kill);
+ }
+ else if ((result & MurderResultFlags.FailedProtected) != 0)
+ {
+ target.ProtectedOn = null;
}
await _eventManager.CallAsync(new PlayerMurderEvent(Game, sender, this, target, result));
return true;
}
- private async ValueTask<bool> HandleCheckProtect(ClientPlayer sender, IInnerPlayerControl? target)
+ private async ValueTask<bool> HandleProtectPlayer(ClientPlayer sender, IInnerPlayerControl? target)
{
if (target == null)
{
return true;
}
- if (PlayerInfo.RoleType == RoleTypes.GuardianAngel)
+ if (PlayerInfo.RoleType != RoleTypes.GuardianAngel)
{
- if (await sender.Client.ReportCheatAsync(RpcCalls.CheckProtect, "Sender tried to protect target it couldn't protect"))
+ if (await sender.Client.ReportCheatAsync(RpcCalls.CheckProtect, "Client tried to protect but it wasn't a guardian angel"))
{
return false;
}
}
- if (_game.IsHostAuthoritive)
- {
- return true;
- }
- else
- {
- await ProtectPlayerAsync(target);
- return false;
- }
+ ((InnerPlayerControl)target).Protect(this);
+
+ return true;
}
private async ValueTask<bool> HandleSendChat(ClientPlayer sender, string message)