private static IEnumerable <ICombatEventData> ApplyActionEffect(IAction selectedAction, IActorChangeCalculator actorChangeCalculator) { // Pre-apply copies IInitiativeActor sourceBeforeEffect = selectedAction.Source.Copy(); IReadOnlyList <IInitiativeActor> targetsBeforeEffect = selectedAction.Targets.Select(t => t.Copy()).ToArray(); // Apply selectedAction.Effect.Apply(selectedAction.Source, selectedAction.Targets); // Post-apply comparison foreach (ICombatEventData eventData in actorChangeCalculator.GetChangeEvents(sourceBeforeEffect, selectedAction.Source)) { yield return(eventData); } foreach (var beforeAfterPair in targetsBeforeEffect.ToDictionary( x => x, x => selectedAction.Targets.First(y => y.Id == x.Id))) { foreach (ICombatEventData combatEventData in actorChangeCalculator.GetChangeEvents(beforeAfterPair.Key, beforeAfterPair.Value)) { yield return(combatEventData); } } }
private IAction SendActionsAndWaitForResponse( IInitiativeActor activeEntity, ICharacterController activeController, IReadOnlyDictionary <uint, IAction> availableActions) { IAction selectedAction; do { // Send Actions to Controller _pendingSelectedActionId = null; activeController.SelectAction(activeEntity, availableActions, OnActionSelected); // Wait for Controller response do { Thread.Sleep(ACTION_REQUEST_SLEEP_TIME); } while (!_pendingSelectedActionId.HasValue); // Validate response availableActions.TryGetValue(_pendingSelectedActionId.Value, out selectedAction); _pendingSelectedActionId = null; } while (selectedAction == null || !selectedAction.Available); return(selectedAction); }
public void SelectAction(IInitiativeActor activeEntity, IReadOnlyDictionary <uint, IAction> availableActions, Action <uint> selectAction) { ActiveCharacter = activeEntity as IPlayerCharacterActor; AvailableActions = availableActions; _selectActionCallback = selectAction; ActiveCharacterChanged?.Invoke(); }
public IEnumerable <IAction> TakeTurn( IInitiativeActor activeActor, ICharacterController actorController, IReadOnlyCollection <ITargetableActor> allTargets, IStandardActionGenerator standardActionGenerator) { yield break; }
/// <summary> /// Prompt the controller to select an Action from the provided collection randomly. /// </summary> /// <param name="activeEntity">Currently active Actor in the combat sequence.</param> /// <param name="availableActions">All available Actions for the Actor.</param> /// <param name="selectAction">Action to invoke once the Action has been selected, providing the Action's Id.</param> public void SelectAction(IInitiativeActor activeEntity, IReadOnlyDictionary <uint, IAction> availableActions, Action <uint> selectAction) { uint[] actionIds = availableActions .Where(x => x.Value.Available) .Select(x => x.Key) .ToArray(); uint actionId = actionIds[RANDOM.Next(actionIds.Length)]; selectAction(actionId); }
private static IEnumerable <ICombatEventData> ApplyActionCost(IAction selectedAction, IActorChangeCalculator actorChangeCalculator) { // Pre-apply copy IInitiativeActor sourceBeforeCost = selectedAction.Source.Copy(); // Apply selectedAction.Cost.Pay(selectedAction.Source, selectedAction.ActionSource); // Post-apply comparison return(actorChangeCalculator.GetChangeEvents(sourceBeforeCost, selectedAction.Source)); }
/// <summary> /// Calculate all possible targeting permutations. /// </summary> /// <param name="sourceActor">Actor doing the targeting.</param> /// <param name="possibleTargets">All possible targetable Actors.</param> /// <returns>Collection containing all possible targeting group combinations.</returns> public IReadOnlyCollection <IReadOnlyCollection <ITargetableActor> > GetTargetOptions( IInitiativeActor sourceActor, IReadOnlyCollection <ITargetableActor> possibleTargets) { List <IReadOnlyCollection <ITargetableActor> > targetOptions = new List <IReadOnlyCollection <ITargetableActor> >(possibleTargets.Count); if (sourceActor is ITargetableActor targetableSource) { targetOptions.Add(new[] { targetableSource }); } return(targetOptions); }
/// <summary> /// Calculate all possible targeting permutations. /// </summary> /// <param name="sourceActor">Actor doing the targeting.</param> /// <param name="possibleTargets">All possible targetable Actors.</param> /// <returns>Collection containing all possible targeting group combinations.</returns> public IReadOnlyCollection <IReadOnlyCollection <ITargetableActor> > GetTargetOptions( IInitiativeActor sourceActor, IReadOnlyCollection <ITargetableActor> possibleTargets) { List <ITargetableActor> allTargets = new List <ITargetableActor>(possibleTargets.Count); foreach (ITargetableActor possibleTarget in possibleTargets) { if (CanTarget(sourceActor, possibleTarget)) { allTargets.Add(possibleTarget); } } return(new[] { allTargets }); }
private uint LoopUntilCombatComplete() { uint winningParty; while (!_winningPartyCalculator.GetWinningPartyId(_parties, out winningParty)) { // Get Active Entity IInitiativeActor activeEntity = _initiativeQueue.GetNext(); _eventQueue.EnqueueEvent(new ActiveActorChangedEvent(activeEntity.Id)); // Prompt Action if (!activeEntity.IsAlive()) { // Move dead/inactive actor to end of queue _initiativeQueue.Update(activeEntity.Id, _initiativeQueue.InitiativeThreshold); } else { IEnumerable <IAction> actorSelectedActions = _turnController.TakeTurn( activeEntity, _controllerByPartyId[activeEntity.Party], _allTargets, _standardActionGenerator); foreach (IAction selectedAction in actorSelectedActions) { // Update active entity's initiative value _initiativeQueue.Update(activeEntity.Id, selectedAction.Speed); // Inform event queue we're applying an action _eventQueue.EnqueueEvent(new ActorActionTakenEvent(activeEntity.Id, selectedAction)); // Pay cost(s) for Action _eventQueue.EnqueueEvents(ApplyActionCost(selectedAction, _actorChangeCalculator)); // Apply effect(s) for Action _eventQueue.EnqueueEvents(ApplyActionEffect(selectedAction, _actorChangeCalculator)); } } // Rest for a bit Thread.Sleep(ACTOR_CHANGE_SLEEP_TIME); } return(winningParty); }
public IEnumerable <IAction> TakeTurn( IInitiativeActor activeActor, ICharacterController actorController, IReadOnlyCollection <ITargetableActor> allTargets, IStandardActionGenerator standardActionGenerator) { // Get active actor's action options Dictionary <uint, IAction> actionsForEntity = activeActor .GetAllActions(allTargets) .Concat(standardActionGenerator.GetActions(activeActor)) .ToDictionary(x => x.Id); // Send options and wait for valid response yield return(SendActionsAndWaitForResponse( activeActor, actorController, actionsForEntity)); }
public void max_init_never_exceeds_threshold_by_much() { IInitiativeQueue queue = new InitiativeQueue( 100, new[] { new TestInitActor() { Init = 10 }, new TestInitActor() { Init = 10 }, new TestInitActor() { Init = 10 }, new TestInitActor() { Init = 10 }, new TestInitActor() { Init = 10 } }); float initLimit = 115; for (int i = 0; i < 1000; i++) { foreach ((uint _, float initiative) in queue.GetCurrentQueue()) { Assert.Less(initiative, initLimit); } IInitiativeActor nextActor = queue.GetNext(); queue.Update(nextActor.Id, 100); } }
/// <summary> /// Does the provided Actor meet this Requirement. /// </summary> /// <param name="actor">Actor which will take the Action.</param> /// <returns><c>True</c> if the Requirement is met, otherwise <c>False</c>.</returns> public bool MeetsRequirement(IInitiativeActor actor) { return(true); }
/// <summary> /// Apply this Effect to the target Actors provided. /// </summary> /// <param name="sourceEntity">Actor which originates this Effect.</param> /// <param name="targetActors">Actors which receive this Effect.</param> public void Apply(IInitiativeActor sourceEntity, IReadOnlyCollection <ITargetableActor> targetActors) { // Intentionally left blank }
/// <summary> /// Can the provided Actor currently afford the Cost. /// </summary> /// <param name="entity">Actor to determine Cost for.</param> /// <param name="actionSource">Source object which provides the Action.</param> /// <returns><c>True</c> if the Cost can be paid, otherwise <c>False</c>.</returns> public bool CanAfford(IInitiativeActor entity, IIdTracked actionSource) { return(true); }
/// <summary> /// Pay this Cost with the provided Actor. /// </summary> /// <param name="entity">Actor to pay the Cost with.</param> /// <param name="actionSource">Source object which provided the Action.</param> public void Pay(IInitiativeActor entity, IIdTracked actionSource) { // Intentionally left blank }
public IEnumerable <ICombatEventData> GetChangeEvents(IInitiativeActor before, IInitiativeActor after) { yield break; }
public InitPair(IInitiativeActor actor, float initiative) { Actor = actor; Initiative = initiative; }
/// <summary> /// Get a collection of standard Actions for the supplied Actor. /// </summary> /// <param name="actor">Actor to create standard Actions for.</param> /// <returns>Collection of standard Actions.</returns> public IEnumerable <IAction> GetActions(IInitiativeActor actor) { yield break; }
/// <summary> /// Determines if the given Actor is targetable or not. /// </summary> /// <param name="sourceActor">Actor doing the targeting.</param> /// <param name="targetActor">Actor being targeted.</param> /// <returns><c>True</c> if the Actor can be targeted, otherwise <c>False</c>.</returns> public bool CanTarget(IInitiativeActor sourceActor, ITargetableActor targetActor) { return(sourceActor.Id == targetActor.Id); }
/// <summary> /// Determines if the given Actor is targetable or not. /// </summary> /// <param name="sourceActor">Actor doing the targeting.</param> /// <param name="targetActor">Actor being targeted.</param> /// <returns><c>True</c> if the Actor can be targeted, otherwise <c>False</c>.</returns> public bool CanTarget(IInitiativeActor sourceActor, ITargetableActor targetActor) { return(sourceActor.Party != targetActor.Party && targetActor.IsAlive()); }