private static bool TacticalActIsAvailableByConstrains(ICombatAct tacticalAct, IPropStore propStore) { if (tacticalAct.Constrains is null) { // Если нет никаких ограничений, то действие доступно в любом случае. return(true); } if (tacticalAct.Constrains.PropResourceType is null) { // Если нет ограничений по ресурсам, то действие доступно. return(true); } // Проверяем наличие ресурсов в нужном количестве. // Проверка осуществляется в хранилище, указанном параметром. if (propStore is null) { // Персонажи бе инвентаря не могут применять действия, // для которых нужны ресурсы. return(false); } var propResourceType = tacticalAct.Constrains.PropResourceType; var propResourceCount = tacticalAct.Constrains.PropResourceCount.Value; if (CheckPropResource(propStore, propResourceType, propResourceCount)) { return(true); } return(false); }
public static string GetActHintText(ICombatAct combatAct) { var title = GetActTitle(combatAct); var description = GetActDescription(combatAct); var usedEquipment = combatAct.Equipment; var equipmentTitle = string.Empty; if (usedEquipment is not null) { equipmentTitle = PropHelper.GetPropTitle(usedEquipment); } if (!string.IsNullOrWhiteSpace(description)) { if (!string.IsNullOrWhiteSpace(equipmentTitle)) { return($"{title}\n({equipmentTitle})\n{new string('-', 8)}\n{description}"); } return($"{title}\n{new string('-', 8)}\n{description}"); } if (!string.IsNullOrWhiteSpace(equipmentTitle)) { return($"{title}\n({equipmentTitle})"); } return(title); }
/// <summary> /// Возвращает показатель поглощения брони цели. /// Это величина, на которую будет снижен урон. /// </summary> /// <param name="targetActor"> Целевой актёр, для которого проверяется поглощение урона. </param> /// <param name="usedTacticalAct"> Действие, которое будет использовано для нанесения урона. </param> /// <returns> Возвращает показатель поглощения брони цели. </returns> private static int GetArmorAbsorbtion(IActor targetActor, ICombatAct usedTacticalAct) { var actorArmors = targetActor.Person.GetModule <ICombatStatsModule>().DefenceStats.Armors; var offence = usedTacticalAct.Stats.Offence; if (offence is null) { throw new InvalidOperationException(); } var actImpact = offence.Impact; var preferredArmor = actorArmors.FirstOrDefault(x => x.Impact == actImpact); if (preferredArmor == null) { return(0); } return(preferredArmor.AbsorbtionLevel switch { PersonRuleLevel.None => 0, PersonRuleLevel.Lesser => 1, PersonRuleLevel.Normal => 2, PersonRuleLevel.Grand => 5, PersonRuleLevel.Absolute => 10, _ => throw new InvalidOperationException( $"Unknown armor absorbtion level: {preferredArmor.AbsorbtionLevel}.") });
private void CountTargetActorAttack(IActor actor, IActor targetActor, ICombatAct tacticalAct) { if (actor.Person is MonsterPerson) { // Монстры не могут прокачиваться. return; } if (actor.Person == null) { // Это может происходить в тестах, // если в моках не определили персонажа. //TODO Поискать решение, как всегда быть уверенным, что персонаж указан в боевых условиях, и может быть null в тестах. //TODO Эта же проверка нужна в CountActorDefeat (учёт убиства актёра). return; } var evolutionData = actor.Person.GetModuleSafe <IEvolutionModule>(); //TODO Такую же проверку добавить в CountActorDefeat (учёт убиства актёра). if (evolutionData is null) { return; } var progress = new AttackActorJobProgress(targetActor, tacticalAct); _perkResolver.ApplyProgress(progress, evolutionData); }
private static void RemovePropResource(IActor actor, ICombatAct act) { var propResources = from prop in actor.Person.GetModule <IInventoryModule>().CalcActualItems() where prop is Resource where prop.Scheme.Bullet?.Caliber == act.Constrains?.PropResourceType select prop; if (propResources.FirstOrDefault() is Resource propResource) { if (propResource.Count >= act.Constrains?.PropResourceCount) { var usedResource = new Resource(propResource.Scheme, act.Constrains.PropResourceCount.Value); actor.Person.GetModule <IInventoryModule>().Remove(usedResource); } else { throw new InvalidOperationException( $"Не хватает ресурса {propResource} для использования действия {act}."); } } else { throw new InvalidOperationException( $"Не хватает ресурса {act.Constrains?.PropResourceType} для использования действия {act}."); } }
/// <summary> /// Возвращает случайное значение эффективность действия. /// </summary> /// <param name="act"> Соверщённое действие. </param> /// <returns> Возвращает выпавшее значение эффективности. </returns> private CombatActRoll GetActEfficient(ICombatAct act) { var rolledEfficient = _actUsageRandomSource.RollEfficient(act.Efficient); var roll = new CombatActRoll(act, rolledEfficient); return(roll); }
private void UseAct(IActor actor, ActTargetInfo target, ICombatAct act, ISectorMap map) { bool isInDistance; if ((act.Stats.Targets & TacticalActTargets.Self) > 0 && actor == target) { isInDistance = true; } else { isInDistance = IsInDistance(actor, target.TargetNode, act, map); } if (!isInDistance) { // Это может произойти, если цель, в процессе применения действия // успела выйти из радиуса применения. // TODO Лучше сделать, чтобы этот метод возвращал объект с результатом выполнения действия. // А внешний код пусть либо считает исход допустимым, либо выбрасывает исключения типа UsageOutOfDistanceException return; } var targetNode = target.TargetNode; var targetIsOnLine = map.TargetIsOnLine( actor.Node, targetNode); if (targetIsOnLine) { actor.UseAct(targetNode, act); var tacticalActRoll = GetActEfficient(act); var actHandler = _actUsageHandlerSelector.GetHandler(target.TargetObject); actHandler.ProcessActUsage(actor, target.TargetObject, tacticalActRoll, map); UseActResources(actor, act); } else { // Ситация, когда цель за стеной может произойти по следующим причинам: // 1. В момент начала применения действия цель была доступна. К моменту выполнения дейтвия цель скрылась. // В этом случае изымаем патроны и начинаем КД по действию, так как фактически ресурсы на него потрачены. Но на цель не воздействуем. // 2. Ошибка во внешнем коде, когда не провели предварительную проверку. Это проверяется сравнением ячейки в которую целились // на момент начала действия и текущую ячейку цели. if (target.TargetNode == target.TargetObject.Node) { throw new UsageThroughtWallException(); } UseActResources(actor, act); } }
public CombatActButton(Texture2D texture, Texture2D icon, Texture2D selectedMarkerTexture, CombatActButtonGroup buttonGroup, ICombatAct tacticalAct, Rectangle rect) : base(texture, rect) { _icon = icon; _selectedMarker = selectedMarkerTexture; _buttonGroup = buttonGroup; CombatAct = tacticalAct; }
private static bool WeaponHasTag(string tag, ICombatAct tacticalAct) { if (tacticalAct.Equipment is null) { return(false); } if (tacticalAct.Equipment.Scheme.Tags is null) { return(false); } return(tacticalAct.Equipment.Scheme.Tags.Contains(tag)); }
private static bool IsInDistance(IActor actor, IGraphNode targetNode, ICombatAct act, ISectorMap map) { var actorNodes = GetActorNodes(actor.PhysicalSize, actor.Node, map); foreach (var node in actorNodes) { var isInDistanceInNode = act.CheckDistance(node, targetNode, map); if (isInDistanceInNode) { return(true); } } return(false); }
public AttackTask(IActor actor, IActorTaskContext context, IAttackTarget target, ICombatAct tacticalAct, ITacticalActUsageService actService) : base(actor, context) { _actService = actService; TargetObject = target ?? throw new ArgumentNullException(nameof(target)); TacticalAct = tacticalAct ?? throw new ArgumentNullException(nameof(tacticalAct)); TargetNode = target.Node; var combatActDuration = tacticalAct.Stats.Duration.GetValueOrDefault(1); var durationBonus = GetDurationBonus(actor); var durationWithBonus = (int)Math.Round(GlobeMetrics.OneIterationLength * combatActDuration * durationBonus); Cost = durationWithBonus; }
public async Task SetUpAsync() { var actUsageRandomSourceMock = new Mock <ITacticalActUsageRandomSource>(); actUsageRandomSourceMock.Setup(x => x.RollToHit(It.IsAny <Roll>())).Returns(6); actUsageRandomSourceMock.Setup(x => x.RollEfficient(It.IsAny <Roll>())).Returns(1); _actUsageRandomSource = actUsageRandomSourceMock.Object; var personMock = new Mock <IPerson>(); _person = personMock.Object; var evolutionModuleMock = new Mock <IEvolutionModule>(); var evolutionModule = evolutionModuleMock.Object; personMock.Setup(x => x.GetModule <IEvolutionModule>(It.IsAny <string>())).Returns(evolutionModule); var actScheme = new TestTacticalActStatsSubScheme { Offence = new TestTacticalActOffenceSubScheme { Type = OffenseType.Tactical, Impact = ImpactType.Kinetic, ApRank = 10 } }; var actMock = new Mock <ICombatAct>(); actMock.SetupGet(x => x.Stats).Returns(actScheme); _act = actMock.Object; var map = await SquareMapFactory.CreateAsync(3).ConfigureAwait(false); var sectorMock = new Mock <ISector>(); sectorMock.SetupGet(x => x.Map).Returns(map); _sector = sectorMock.Object; }
public static string GetActTitle(ICombatAct combatAct) { var text = combatAct.Scheme.Name?.En; var currentLanguage = Thread.CurrentThread.CurrentUICulture; var langName = currentLanguage.TwoLetterISOLanguageName; if (string.Equals(langName, "en", StringComparison.InvariantCultureIgnoreCase)) { text = combatAct.Scheme.Name?.En; } else if (string.Equals(langName, "ru", StringComparison.InvariantCultureIgnoreCase)) { text = combatAct.Scheme.Name?.Ru; } else { Debug.Fail( $"Unknown language {langName} is selected. All available language must be supported in the client."); } return(text ?? "<Undef>"); }
/// <summary> /// Проверяет, допустимая ли дистанция для совершения действия. /// </summary> /// <param name="act"> Проверяемое действие. </param> /// <param name="currentNode"> Узел, из которого совершается действие. </param> /// <param name="targetNode"> Целевой узел. </param> /// <returns>Возвращает true, если дистанция допустима.</returns> public static bool CheckDistance(this ICombatAct act, IGraphNode currentNode, IGraphNode targetNode, ISectorMap map) { if (act is null) { throw new ArgumentNullException(nameof(act)); } if (currentNode is null) { throw new ArgumentNullException(nameof(currentNode)); } if (targetNode is null) { throw new ArgumentNullException(nameof(targetNode)); } if (map is null) { throw new ArgumentNullException(nameof(map)); } var range = act.Stats.Range; if (range is null) { throw new ArgumentNullException(nameof(act.Stats.Range)); } var distance = map.DistanceBetween(currentNode, targetNode); var isInDistance = range.Contains(distance); return(isInDistance); }
private void UseActResources(IActor actor, ICombatAct act) { // Изъятие патронов if (act.Constrains?.PropResourceType != null) { RemovePropResource(actor, act); } if (act.Equipment != null) { EquipmentDurableService?.UpdateByUse(act.Equipment, actor.Person); } // Сброс КД, если он есть. act.StartCooldownIfItIs(); // Consume energy. // Monster's act has no energy cost. if (act.Constrains?.EnergyCost != null) { actor.Person.GetModule <ISurvivalModule>() .DecreaseStat(SurvivalStatType.Energy, act.Constrains.EnergyCost.Value); } }
public UsedActEventArgs([NotNull] IGraphNode targetNode, [NotNull] ICombatAct tacticalAct) { TargetNode = targetNode ?? throw new ArgumentNullException(nameof(targetNode)); TacticalAct = tacticalAct ?? throw new ArgumentNullException(nameof(tacticalAct)); }
public PlayerDamagedEvent(ICombatAct tacticalAct, IActor damager) { TacticalAct = tacticalAct ?? throw new ArgumentNullException(nameof(tacticalAct)); Damager = damager ?? throw new ArgumentNullException(nameof(damager)); }
public AttackActorJobProgress(IActor targetActor, ICombatAct tacticalAct) { _targetActor = targetActor; _tacticalAct = tacticalAct; }
/// <summary> /// Возвращает ранг пробития действия. /// </summary> /// <param name="tacticalAct"></param> /// <returns></returns> private static int GetActApRank(ICombatAct tacticalAct) { return((tacticalAct.Stats.Offence?.ApRank).GetValueOrDefault()); }
public CombatActRoll(ICombatAct tacticalAct, int efficient) { CombatAct = tacticalAct; Efficient = efficient; }
/// <inheritdoc /> public void UseAct(IGraphNode targetNode, ICombatAct tacticalAct) { DoUseAct(targetNode, tacticalAct); }
private void DoUseAct(IGraphNode targetNode, ICombatAct tacticalAct) { var args = new UsedActEventArgs(targetNode, tacticalAct); UsedAct?.Invoke(this, args); }