// // UnsafeSkillsApi.cs // // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using NosSmooth.Core.Client; using NosSmooth.Core.Contracts; using NosSmooth.Game.Contracts; using NosSmooth.Game.Data.Characters; using NosSmooth.Game.Data.Entities; using NosSmooth.Game.Errors; using NosSmooth.Game.Events.Battle; using NosSmooth.Packets.Client.Battle; using NosSmooth.Packets.Enums.Entities; using NosSmooth.Packets.Server.Skills; using Remora.Results; namespace NosSmooth.Game.Apis.Unsafe; /// /// Packet api for using character skills. /// public class UnsafeSkillsApi { private readonly INostaleClient _client; private readonly Game _game; private readonly Contractor _contractor; /// /// Initializes a new instance of the class. /// /// The nostale client. /// The game. /// The contractor. public UnsafeSkillsApi(INostaleClient client, Game game, Contractor contractor) { _client = client; _game = game; _contractor = contractor; } /// /// Use the given (targetable) skill on specified entity. /// /// /// For skills that can be used only on self, use of the character. /// For skills that cannot be targeted on an entity, proceed to UseSkillAt. /// /// The cast id of the skill. /// The id of the entity to use the skill on. /// The type of the supplied entity. /// The x coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.) /// The y coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.) /// The cancellation token for cancelling the operation. /// A result that may or may not have succeeded. public Task UseSkillOn ( short castId, long entityId, EntityType entityType, short? mapX = default, short? mapY = default, CancellationToken ct = default ) { return _client.SendPacketAsync ( new UseSkillPacket ( castId, entityType, entityId, mapX, mapY ), ct ); } /// /// Use the given (targetable) skill on character itself. /// /// /// For skills that cannot be targeted on an entity, proceed to UseSkillAt. /// /// The cast id of the skill. /// The x coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.) /// The y coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.) /// The cancellation token for cancelling the operation. /// A result that may or may not have succeeded. public async Task UseSkillOnCharacter ( short castId, short? mapX = default, short? mapY = default, CancellationToken ct = default ) { var character = _game.Character; if (character is null) { return new NotInitializedError("Character"); } return await _client.SendPacketAsync ( new UseSkillPacket ( castId, EntityType.Player, character.Id, mapX, mapY ), ct ); } /// /// Use the given (targetable) skill on specified entity. /// /// /// For skills that can be used only on self, use of the character. /// For skills that cannot be targeted on an entity, proceed to UseSkillAt. /// /// The cast id of the skill. /// The entity to use the skill on. /// The x coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.) /// The y coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.) /// The cancellation token for cancelling the operation. /// A result that may or may not have succeeded. public Task UseSkillOn ( short castId, ILivingEntity entity, short? mapX = default, short? mapY = default, CancellationToken ct = default ) { return _client.SendPacketAsync ( new UseSkillPacket ( castId, entity.Type, entity.Id, mapX, mapY ), ct ); } /// /// Use the given (targetable) skill on specified entity. /// /// /// For skills that can be used only on self, use of the character. /// For skills that cannot be targeted on an entity, proceed to UseSkillAt. /// /// The skill to use. /// The entity to use the skill on. /// The x coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.) /// The y coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.) /// The cancellation token for cancelling the operation. /// A result that may or may not have succeeded. public Task UseSkillOn ( Skill skill, ILivingEntity entity, short? mapX = default, short? mapY = default, CancellationToken ct = default ) { if (skill.Info is null) { return Task.FromResult(new NotInitializedError("skill info")); } return _client.SendPacketAsync ( new UseSkillPacket ( skill.Info.CastId, entity.Type, entity.Id, mapX, mapY ), ct ); } /// /// Use the given (targetable) skill on specified entity. /// /// /// For skills that can be used only on self, use of the character. /// For skills that cannot be targeted on an entity, proceed to UseSkillAt. /// /// The skill to use. /// The id of the entity to use the skill on. /// The type of the supplied entity. /// The x coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.) /// The y coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.) /// The cancellation token for cancelling the operation. /// A result that may or may not have succeeded. public Task UseSkillOn ( Skill skill, long entityId, EntityType entityType, short? mapX = default, short? mapY = default, CancellationToken ct = default ) { var info = skill.Info; if (info is null) { return Task.FromResult(new NotInitializedError("skill info")); } return _client.SendPacketAsync ( new UseSkillPacket ( info.CastId, entityType, entityId, mapX, mapY ), ct ); } /// /// Use the given (aoe) skill on the specified place. /// /// /// For skills that can have targets, proceed to UseSkillOn. /// /// The id of the skill. /// The x coordinate to use the skill at. /// The y coordinate to use the skill at. /// The cancellation token for cancelling the operation. /// A result that may or may not have succeeded. public Task UseSkillAt ( long castId, short mapX, short mapY, CancellationToken ct = default ) { return _client.SendPacketAsync ( new UseAOESkillPacket(castId, mapX, mapY), ct ); } /// /// Use the given (aoe) skill on the specified place. /// /// /// For skills that can have targets, proceed to UseSkillOn. /// /// The skill to use. /// The x coordinate to use the skill at. /// The y coordinate to use the skill at. /// The cancellation token for cancelling the operation. /// A result that may or may not have succeeded. public Task UseSkillAt ( Skill skill, short mapX, short mapY, CancellationToken ct = default ) { var info = skill.Info; if (info is null) { return Task.FromResult(new NotInitializedError("skill info")); } return _client.SendPacketAsync ( new UseAOESkillPacket(info.CastId, mapX, mapY), ct ); } /// /// Creates a contract for using a skill on the given entity. /// /// The skill to use. /// The id of the entity to use the skill on. /// The type of the supplied entity. /// The x coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.) /// The y coordinate on the map. (Used for non targeted dashes etc., says where the dash will be to.) /// The contract or an error. public Result> ContractUseSkillOn ( Skill skill, long entityId, EntityType entityType, short? mapX = default, short? mapY = default ) { var characterId = _game?.Character?.Id; if (characterId is null) { return new NotInitializedError("Game.Character"); } if (skill.Info is null) { return new NotInitializedError("skill info"); } return Result>.FromSuccess ( CreateUseSkillContract ( _contractor, skill.SkillVNum, characterId.Value, ct => UseSkillOn ( skill.Info.CastId, entityId, entityType, mapX, mapY, ct ) ) ); } /// /// Creates a contract for using a skill at the given location. /// /// The skill to use. /// The x coordinate to use the skill at. /// The y coordinate to use the skill at. /// The contract or an error. public Result> ContractUseSkillAt ( Skill skill, short mapX, short mapY ) { var characterId = _game?.Character?.Id; if (characterId is null) { return new NotInitializedError("Game.Character"); } if (skill.Info is null) { return new NotInitializedError("skill info"); } return Result>.FromSuccess ( CreateUseSkillContract ( _contractor, skill.SkillVNum, characterId.Value, ct => { return UseSkillAt(skill.Info.CastId, mapX, mapY, ct); } ) ); } /// /// Creates a use skill contract, /// casting the skill using the given action. /// /// The contractor to register the contract at. /// The vnum of the casting skill. /// The id of the caster, character. /// The used skill event. /// A contract for using the given skill. public static IContract CreateUseSkillContract ( Contractor contractor, int skillVNum, long characterId, Func> useSkill ) { return new ContractBuilder(contractor, UseSkillStates.None) .SetMoveAction ( UseSkillStates.None, async (data, ct) => (await useSkill(ct)).Map(true), UseSkillStates.SkillUseRequested ) .SetMoveFilter ( UseSkillStates.SkillUseRequested, data => data.Skill.SkillVNum == skillVNum && data.Caster.Id == characterId, UseSkillStates.SkillUsedResponse ) .SetFillData ( UseSkillStates.SkillUsedResponse, skillUseEvent => skillUseEvent ) .SetError(UseSkillStates.SkillUseRequested, _ => UseSkillErrors.Unknown) .SetTimeout(UseSkillStates.SkillUsedResponse, TimeSpan.FromSeconds(1), UseSkillStates.CharacterRestored) .Build(); } }