Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Player Freeze (Walk Open, Walk Close) #367

Merged
merged 7 commits into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion EOBot/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public void JunkItem(int id, int amountRemoved)
ConsoleHelper.WriteMessage(ConsoleHelper.Type.JunkItem, $"{weight,3}/{maxWeight,3} - weight - {inventoryCount?.Amount ?? 0} in inventory");
}

public void MakeDrunk() { }
public void NotifyFrozen() { }
}

[AutoMappedType]
Expand Down
8 changes: 7 additions & 1 deletion EOLib/Domain/Character/AttackValidationActions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Linq;
using AutomaticTypeMapper;
using EOLib.Domain.Extensions;
using EOLib.Domain.Login;
using EOLib.Domain.Map;
using EOLib.IO.Repositories;

Expand All @@ -27,9 +28,13 @@ public AttackValidationActions(ICharacterProvider characterProvider,

public AttackValidationError ValidateCharacterStateBeforeAttacking()
{
if (_characterProvider.MainCharacter.Frozen)
return AttackValidationError.Frozen;

if (_characterProvider.MainCharacter.Stats[CharacterStat.Weight] >
_characterProvider.MainCharacter.Stats[CharacterStat.MaxWeight])
return AttackValidationError.Overweight;

if (_characterProvider.MainCharacter.Stats[CharacterStat.SP] <= 0)
return AttackValidationError.Exhausted;

Expand Down Expand Up @@ -73,6 +78,7 @@ public enum AttackValidationError
Overweight,
Exhausted,
NotYourBattle,
MissingArrows
MissingArrows,
Frozen,
}
}
2 changes: 2 additions & 0 deletions EOLib/Domain/Character/Character.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ public sealed partial class Character : ISpellTargetable

public bool NoWall { get; }

public bool Frozen { get; }

public static Character FromCharacterSelectionListEntry(CharacterSelectionListEntry selectionListEntry)
{
return new Builder
Expand Down
7 changes: 6 additions & 1 deletion EOLib/Domain/Character/WalkValidationActions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ public enum WalkValidationResult
NotWalkable,
Walkable,
BlockedByCharacter,
GhostComplete
GhostComplete,
Frozen
}

[AutoMappedType]
Expand All @@ -36,6 +37,7 @@ public WalkValidationActions(IMapCellStateProvider mapCellStateProvider,
_currentMapStateProvider = currentMapStateProvider;
_unlockDoorValidator = unlockDoorValidator;
_ghostingRepository = ghostingRepository;

}

public WalkValidationResult CanMoveToDestinationCoordinates()
Expand Down Expand Up @@ -65,6 +67,9 @@ public WalkValidationResult IsCellStateWalkable(IMapCellState cellState)
{
ClearGhostCache();

if (_characterProvider.MainCharacter.Frozen)
return WalkValidationResult.Frozen;

var mc = _characterProvider.MainCharacter;

var cellChar = cellState.Character.FlatMap(c => c.SomeWhen(cc => cc != mc));
Expand Down
2 changes: 1 addition & 1 deletion EOLib/Domain/Login/LoginActions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public async Task<int> RequestCharacterLogin(Character.Character character)
.WithAdminLevel(data.Admin)
.WithStats(CharacterStats.FromSelectCharacterData(data));

_playerInfoRepository.IsFirstTimePlayer = data.LoginMessageCode == LoginMessageCode.Yes;
_playerInfoRepository.IsFirstTimePlayer = data.LoginMessageCode != LoginMessageCode.No;
_playerInfoRepository.PlayerHasAdminCharacter = _characterSelectorRepository.Characters.Any(x => x.AdminLevel > 0);

_currentMapStateRepository.CurrentMapID = data.MapId;
Expand Down
4 changes: 4 additions & 0 deletions EOLib/Domain/Notifiers/IMainCharacterEventNotifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public interface IMainCharacterEventNotifier
void DropItem(int id, int amountDropped);

void JunkItem(int id, int amountRemoved);

void NotifyFrozen();
}

[AutoMappedType]
Expand All @@ -28,5 +30,7 @@ public void TakeItemFromMap(int id, int amountTaken) { }
public void DropItem(int id, int amountDropped) { }

public void JunkItem(int id, int amountTaken) { }

public void NotifyFrozen() { }
}
}
19 changes: 18 additions & 1 deletion EOLib/Domain/Spells/SpellCastValidationActions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System.Linq;

using AutomaticTypeMapper;

using EOLib.Domain.Character;
using EOLib.Domain.Map;
using EOLib.Domain.Party;
using EOLib.IO;
using EOLib.IO.Repositories;

Expand All @@ -13,31 +16,42 @@ public class SpellCastValidationActions : ISpellCastValidationActions
private readonly IPubFileProvider _pubFileProvider;
private readonly ICurrentMapProvider _currentMapProvider;
private readonly ICharacterProvider _characterProvider;
private readonly IPartyDataProvider _partyDataProvider;

public SpellCastValidationActions(IPubFileProvider pubFileProvider,
ICurrentMapProvider currentMapProvider,
ICharacterProvider characterProvider)
ICharacterProvider characterProvider,
IPartyDataProvider partyDataProvider)
{
_pubFileProvider = pubFileProvider;
_currentMapProvider = currentMapProvider;
_characterProvider = characterProvider;
_partyDataProvider = partyDataProvider;
}

public SpellCastValidationResult ValidateSpellCast(int spellId)
{
if (_characterProvider.MainCharacter.Frozen)
return SpellCastValidationResult.Frozen;

var spellData = _pubFileProvider.ESFFile[spellId];

var stats = _characterProvider.MainCharacter.Stats;
if (stats[CharacterStat.SP] - spellData.SP < 0)
return SpellCastValidationResult.ExhaustedNoSp;
if (stats[CharacterStat.TP] - spellData.TP < 0)
return SpellCastValidationResult.ExhaustedNoTp;
if (spellData.Target == SpellTarget.Group && !_partyDataProvider.Members.Any())
return SpellCastValidationResult.NotMemberOfGroup;

return SpellCastValidationResult.Ok;
}

public SpellCastValidationResult ValidateSpellCast(int spellId, ISpellTargetable spellTarget)
{
if (_characterProvider.MainCharacter.Frozen)
return SpellCastValidationResult.Frozen;

var res = ValidateSpellCast(spellId);
if (res != SpellCastValidationResult.Ok)
return res;
Expand Down Expand Up @@ -75,6 +89,9 @@ public SpellCastValidationResult ValidateSpellCast(int spellId, ISpellTargetable

public bool ValidateBard()
{
if (_characterProvider.MainCharacter.Frozen)
return false;

var weapon = _characterProvider.MainCharacter.RenderProperties.WeaponGraphic;
return Constants.Instruments.Any(x => x == weapon);
}
Expand Down
2 changes: 2 additions & 0 deletions EOLib/Domain/Spells/SpellCastValidationResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ public enum SpellCastValidationResult
WrongTargetType,
ExhaustedNoSp,
ExhaustedNoTp,
NotMemberOfGroup,
Frozen,
}
}
44 changes: 44 additions & 0 deletions EOLib/PacketHandlers/Walk/WalkClosedHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System.Collections.Generic;

using AutomaticTypeMapper;

using EOLib.Domain.Character;
using EOLib.Domain.Login;
using EOLib.Domain.Notifiers;
using EOLib.Net.Handlers;

using Moffat.EndlessOnline.SDK.Protocol.Net;
using Moffat.EndlessOnline.SDK.Protocol.Net.Server;

namespace EOLib.PacketHandlers.Walk
{
[AutoMappedType]
public class WalkCloseHandler : InGameOnlyPacketHandler<WalkCloseServerPacket>
{
private readonly ICharacterRepository _characterRepository;
private readonly IEnumerable<IMainCharacterEventNotifier> _mainCharacterEventNotifiers;

public override PacketFamily Family => PacketFamily.Walk;

public override PacketAction Action => PacketAction.Close;

public WalkCloseHandler(IPlayerInfoProvider playerInfoProvider,
ICharacterRepository characterRepository,
IEnumerable<IMainCharacterEventNotifier> mainCharacterEventNotifiers)
: base(playerInfoProvider)
{
_characterRepository = characterRepository;
_mainCharacterEventNotifiers = mainCharacterEventNotifiers;
}

public override bool HandlePacket(WalkCloseServerPacket packet)
{
_characterRepository.MainCharacter = _characterRepository.MainCharacter.WithFrozen(true);

foreach (var notifier in _mainCharacterEventNotifiers)
notifier.NotifyFrozen();

return true;
}
}
}
44 changes: 44 additions & 0 deletions EOLib/PacketHandlers/Walk/WalkOpenHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System.Collections.Generic;

using AutomaticTypeMapper;

using EOLib.Domain.Character;
using EOLib.Domain.Login;
using EOLib.Domain.Notifiers;
using EOLib.Net.Handlers;

using Moffat.EndlessOnline.SDK.Protocol.Net;
using Moffat.EndlessOnline.SDK.Protocol.Net.Server;

namespace EOLib.PacketHandlers.Walk
{
[AutoMappedType]
public class WalkOpenHandler : InGameOnlyPacketHandler<WalkOpenServerPacket>
{
private readonly ICharacterRepository _characterRepository;
private readonly IEnumerable<IMainCharacterEventNotifier> _mainCharacterEventNotifiers;

public override PacketFamily Family => PacketFamily.Walk;

public override PacketAction Action => PacketAction.Open;

public WalkOpenHandler(IPlayerInfoProvider playerInfoProvider,
ICharacterRepository characterRepository,
IEnumerable<IMainCharacterEventNotifier> mainCharacterEventNotifiers)
: base(playerInfoProvider)
{
_characterRepository = characterRepository;
_mainCharacterEventNotifiers = mainCharacterEventNotifiers;
}

public override bool HandlePacket(WalkOpenServerPacket packet)
{
_characterRepository.MainCharacter = _characterRepository.MainCharacter.WithFrozen(false);

foreach (var notifier in _mainCharacterEventNotifiers)
notifier.NotifyFrozen();

return true;
}
}
}
2 changes: 1 addition & 1 deletion EndlessClient/Audio/SoundEffectID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public enum SoundEffectID
DoorOrChestLocked,
BuySell,
Craft,
UnknownBuzzSound = 28,
PlayerFrozen = 28,
AdminChatReceived,
AdminChatSent = AdminChatReceived,
AlternateMeleeAttack,
Expand Down
9 changes: 9 additions & 0 deletions EndlessClient/Controllers/ArrowKeyController.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
using AutomaticTypeMapper;

using EndlessClient.Audio;
using EndlessClient.HUD;
using EndlessClient.Input;
using EndlessClient.Rendering.Character;

using EOLib;
using EOLib.Domain.Character;
using EOLib.Domain.Extensions;
using EOLib.Domain.Map;
using EOLib.Localization;

using Optional;

namespace EndlessClient.Controllers
Expand Down Expand Up @@ -101,6 +104,9 @@ private bool CurrentDirectionIs(EODirection direction)

private void FaceOrAttemptWalk(EODirection direction)
{
if (_characterProvider.MainCharacter.Frozen)
return;

if (!CurrentDirectionIs(direction) && _characterProvider.MainCharacter.RenderProperties.IsActing(CharacterActionState.Standing))
{
_characterAnimationActions.Face(direction);
Expand Down Expand Up @@ -133,6 +139,9 @@ private void AttemptToStartWalking()
case WalkValidationResult.Walkable:
_characterAnimationActions.StartWalking(Option.None<MapCoordinate>());
break;
case WalkValidationResult.Frozen:
// no-op
break;
}
}
}
Expand Down
61 changes: 40 additions & 21 deletions EndlessClient/Controllers/FunctionKeyController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,29 +68,48 @@ public bool SelectSpell(int index, bool isAlternate)
{
_spellSelectActions.SelectSpellBySlot(index + (isAlternate ? ActiveSpellsPanel.SpellRowLength : 0));

_spellSlotDataProvider.SelectedSpellInfo.MatchSome(x =>
{
var spellData = _esfFileProvider.ESFFile[x.ID];
if (spellData.Type == SpellType.Bard && _spellCastValidationActions.ValidateBard())
_spellSlotDataProvider.SelectedSpellInfo.Match(
some: x =>
{
_inGameDialogActions.ShowBardDialog();
}
else if (spellData.Target == SpellTarget.Self || spellData.Target == SpellTarget.Group)
{
var castResult = _spellCastValidationActions.ValidateSpellCast(x.ID);
var spellData = _esfFileProvider.ESFFile[x.ID];
if (spellData.Type == SpellType.Bard && _spellCastValidationActions.ValidateBard())
{
_inGameDialogActions.ShowBardDialog();
}
else
{
var castResult = _spellCastValidationActions.ValidateSpellCast(x.ID);

if (castResult == SpellCastValidationResult.ExhaustedNoTp)
_statusLabelSetter.SetStatusLabel(EOResourceID.STATUS_LABEL_TYPE_WARNING, EOResourceID.ATTACK_YOU_ARE_EXHAUSTED_TP);
else if (castResult == SpellCastValidationResult.ExhaustedNoSp)
_statusLabelSetter.SetStatusLabel(EOResourceID.STATUS_LABEL_TYPE_WARNING, EOResourceID.ATTACK_YOU_ARE_EXHAUSTED_SP);
else if (_characterAnimationActions.PrepareMainCharacterSpell(x.ID, _characterProvider.MainCharacter))
_characterActions.PrepareCastSpell(x.ID);
}
else
{
_sfxPlayer.PlaySfx(SoundEffectID.SpellActivate);
}
});
switch (castResult)
{
case SpellCastValidationResult.ExhaustedNoTp:
_statusLabelSetter.SetStatusLabel(EOResourceID.STATUS_LABEL_TYPE_WARNING, EOResourceID.ATTACK_YOU_ARE_EXHAUSTED_TP);
break;
case SpellCastValidationResult.ExhaustedNoSp:
_statusLabelSetter.SetStatusLabel(EOResourceID.STATUS_LABEL_TYPE_WARNING, EOResourceID.ATTACK_YOU_ARE_EXHAUSTED_SP);
break;
case SpellCastValidationResult.NotMemberOfGroup:
_statusLabelSetter.SetStatusLabel(EOResourceID.STATUS_LABEL_TYPE_WARNING, EOResourceID.SPELL_ONLY_WORKS_ON_GROUP);
break;
case SpellCastValidationResult.Frozen:
// no-op
break;
default:
_statusLabelSetter.SetStatusLabel(EOResourceID.SKILLMASTER_WORD_SPELL, $"{spellData.Name} ", EOResourceID.SPELL_WAS_SELECTED);
if (spellData.Target == SpellTarget.Normal)
{
_sfxPlayer.PlaySfx(SoundEffectID.SpellActivate);
}
else if (_characterAnimationActions.PrepareMainCharacterSpell(x.ID, _characterProvider.MainCharacter))
{
_characterActions.PrepareCastSpell(x.ID);
}
break;
}
}
},
none: () => _statusLabelSetter.SetStatusLabel(EOResourceID.STATUS_LABEL_TYPE_WARNING, EOResourceID.SPELL_NOTHING_WAS_SELECTED)
);

return true;
}
Expand Down
4 changes: 4 additions & 0 deletions EndlessClient/Controllers/InventoryController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using EOLib.Domain.Interact;
using EOLib.Domain.Interact.Bank;
using EOLib.Domain.Item;
using EOLib.Domain.Login;
using EOLib.Domain.Map;
using EOLib.Domain.Trade;
using EOLib.IO;
Expand Down Expand Up @@ -118,6 +119,9 @@ public void UseItem(EIFRecord record)
{
//usable items
case ItemType.Teleport:
if (_characterProvider.MainCharacter.Frozen)
break;

if (!_currentMapProvider.CurrentMap.Properties.CanScroll)
{
_statusLabelSetter.SetStatusLabel(EOResourceID.STATUS_LABEL_TYPE_ACTION, EOResourceID.STATUS_LABEL_NOTHING_HAPPENED);
Expand Down
Loading