protected QueuedAbility(BattleEntityInBattle source, Ability ability, BattleEntityInBattle target, Int32 delayTicks) { Source = source; Ability = ability; Target = target; RemainingDelayTicks = delayTicks; }
public static void QueueAbility(BattleEntityInBattle source, Ability ability, BattleEntityInBattle target) { var queuedAbility = QueuedAbility.New(source, ability, target); _queuedAbilities.Add(queuedAbility); //indicate on the source that that we have queued an ability source.HasQueuedAbility = true; }
public static BattleEntityInBattle[] GetAllTargetsForAbilityStep(BattleEntityInBattle source, BattleEntityInBattle primaryTarget, AbilityTarget abilityTarget, AbilityStep step) { switch (abilityTarget) { case AbilityTarget.AnySingleDefaultFriendly: case AbilityTarget.AnySingleDefaultHostile: case AbilityTarget.SingleFriendlyOnly: case AbilityTarget.SingleHostileOnly: //no additional targets, just return the primary target return new[] { primaryTarget }; case AbilityTarget.AnySingleDefaultFriendlyWithSecondaryTargets: case AbilityTarget.AnySingleDefaultHostileWithSecondaryTargets: case AbilityTarget.SingleFriendlyWithSecondaryTargets: case AbilityTarget.SingleHostileWithSecondaryTargets: //add additional targets var allTargetsWithPrimary = new List<BattleEntityInBattle> { primaryTarget }; allTargetsWithPrimary.AddRange(step.GetSecondaryTargets(source.BattleEntity, primaryTarget.BattleEntity, BattleModel.Instance.BattleData) .Select(GetBattleEntityInBattleByBattleEntity)); return allTargetsWithPrimary.Distinct().ToArray(); case AbilityTarget.AllFriendly: //there is no 'primary' target, just return all alive friendly targets return GetAllAliveFriendlyTargets(source); case AbilityTarget.AllHostile: //there is no 'primary' target, just return all alive hostile targets return GetAllAliveHostileTargets(source); case AbilityTarget.All: //everything is a target! var allTargets = new List<BattleEntityInBattle>(); allTargets.AddRange(GetAllAliveFriendlyTargets(source)); allTargets.AddRange(GetAllAliveHostileTargets(source)); return allTargets.Distinct().ToArray(); //just incase something is added twice } //should never reach this return new[] { primaryTarget }; }
protected void HandleQueuedAbilityActivation(QueuedAbility queuedAbility) { //TODO: When a battle entity dies, reset their HasQueuedAbility flag. //if the source has died since this ability was queued, just bail. We can't do anything else if (queuedAbility.Source.IsAlive == false) { return; } //we will be trying to execute the ability //start by setting the executing ability. This will display the name of the ability, as well as signal that we're busy processing this ability BattleModel.Instance.BattlefieldData.ExecutingAbility = queuedAbility.Ability; //first, we need to queue up the battle entity attack animation, followed by the logic to execute the ability steps var abilityExecutionSteps = new List<DelayedAction> { //start with battle entity attack animation DelayedAction.New(() => { if (queuedAbility.Source is PartyMemberInBattle) { //TODO: Party member step forward? } else { (queuedAbility.Source as EnemyInBattle).SpriteSheet.AttackFlash(); } }, 0) //no delay here, the step animations will handle their individual delay }; //next, we check to see if our target is still valid. If it's not, we'll skip the actual execution of the ability if (BattleUtility.AbilityCanTarget(queuedAbility)) { //we have a source that's alive, and can actually get targets. We need to check the costs of the ability //like does the source have enough energy, or does the item exist in inventory if (queuedAbility.Ability.ExecuteCost(queuedAbility.Source.BattleEntity)) { //we met all of our ability costs, we can execute the steps foreach (var step in queuedAbility.Ability.GetAbilitySteps(queuedAbility.Source.BattleEntity)) { var allTargets = new BattleEntityInBattle[0]; abilityExecutionSteps.Add(DelayedAction.New(() => { //retarget the primary target, if necessary queuedAbility.Target = BattleUtility.GetPrimaryTargetForAbilityStep(queuedAbility.Source, queuedAbility.Target, queuedAbility.Ability.DeadTargetBehavior); //get secondary targets based on new primary target allTargets = BattleUtility.GetAllTargetsForAbilityStep(queuedAbility.Source, queuedAbility.Target, queuedAbility.Ability.Target, step); }, 1)); //handle ability animations (in parallel) abilityExecutionSteps.Add(DelayedAction.New( () => BattleUtility.HandleAbilityStepAnimations(queuedAbility.Source, allTargets, step.Animation), step.GetTotalFrames(GameState.InBattle))); //apply effects to targets, long enough to display damage/battle status abilityExecutionSteps.Add(DelayedAction.New( () => step.ApplyEffectToTargets(queuedAbility.Source.BattleEntity, allTargets.Select(x => x.BattleEntity).ToArray()), Constants.BATTLE_TIMING_DISPLAY_DAMAGE_TICKS)); } } else { //TODO: Do we want to somehow signal that we couldn't execute a prerequesite? (no energy, or no item in inventory, etc...) //TODO: BATTLE_TIMING_ABILITY_FAILED_COST_WAIT_TICKS may be reudced (or completely eliminted) when I get the step forward / step back animations taken care of. abilityExecutionSteps.Add(DelayedAction.New(() => { }, Constants.BATTLE_TIMING_ABILITY_FAILED_COST_WAIT_TICKS)); //just delay for a few seconds } } //TODO: We need to handle the situation of the party member stepping back as well //finally, queue up finish queued ability logic abilityExecutionSteps.Add(DelayedAction.New( () => Bus.Broadcast(BattleQueuedAbilityFinishedMessage.New(queuedAbility)), 0)); //TODO: Also, do we want actions for things such as checking to see if the battle is won/lost, if things have died, etc... TimerUtility.ExecuteChainOfActions(abilityExecutionSteps); }
public static QueuedAbility WithModifiedDelay(BattleEntityInBattle source, Ability ability, BattleEntityInBattle target, Int32 delayTicks) { return new QueuedAbility(source, ability, target, delayTicks); }
public static QueuedAbility New(BattleEntityInBattle source, Ability ability, BattleEntityInBattle target) { //TODO: Reduce the activation delay by source's speed? return new QueuedAbility(source, ability, target, ability.ActivationDelayTicks); }
public static BattleEntityInBattle GetRandomAliveHostileTarget(BattleEntityInBattle source) { return RandomUtility.GetOneRandomly(GetAllAliveHostileTargets(source)); }
public static BattleEntityDiedMessage New(BattleEntityInBattle entity) { return new BattleEntityDiedMessage(entity); }
public static BattleEntityDodgedMessage New(BattleEntityInBattle entity, LuckyEvent luckyEvent) { return new BattleEntityDodgedMessage(entity, luckyEvent); }
public static BattleEntityTookDamageMessage New(BattleEntityInBattle entity, Dictionary<DamageType, Int32> damageByType, LuckyEvent crit) { return new BattleEntityTookDamageMessage(entity, damageByType, crit); }
protected BattleEntityTookDamageMessage(BattleEntityInBattle entity, Dictionary<DamageType, Int32> damageByType, LuckyEvent crit) { Entity = entity; DamageByType = damageByType; Crit = crit; }
//TODO: Check for muddle status on the below methods? public static BattleEntityInBattle[] GetAllHostileTargets(BattleEntityInBattle source) { var data = BattleModel.Instance.BattleData; return source is EnemyInBattle ? (BattleEntityInBattle[])data.AllPartyMembersInBattle : data.AllEnemiesInBattle; }
public static BattleEntityInBattle[] GetAllAliveHostileTargets(BattleEntityInBattle source) { return GetAllHostileTargets(source).Where(x => x.IsAlive).ToArray(); }
public static void HandleAbilityStepAnimations(BattleEntityInBattle source, BattleEntityInBattle[] targets, AbilityAnimation animation) { //all of these need to be fired off in parallel var sourceAsPartyMember = source as PartyMemberInBattle; if (sourceAsPartyMember != null) { TimerUtility.ExecuteAfterDelay(DelayedAction.New( () => animation.PartyMemberAnimation(sourceAsPartyMember.SpriteSheet), animation.PartyMemberAnimationDelay)); } //TODO: AbilityStepAnimation InBattle OverSource/UnderSource/OverTarget/UnderTarget/OverBattlefield/UnderBattlefield Visuals if (animation.OverSourceVisual != AbilityVisual.None) { } if (animation.UnderSourceVisual != AbilityVisual.None) { } if (animation.OverTargetVisual != AbilityVisual.None) { foreach (var target in targets) { } } if (animation.UnderTargetVisual != AbilityVisual.None) { foreach (var target in targets) { } } if (animation.OverBattlefieldVisual != AbilityVisual.None) { } if (animation.UnderBattlefieldVisual != AbilityVisual.None) { } }
protected EnemyAction(String abilityId, BattleEntityInBattle target) { _abilityId = abilityId; Target = target; }
public static EnemyAction New(String abilityId, BattleEntityInBattle target) { return new EnemyAction(abilityId, target); }
public static BattleEntityTookHealingMessage New(BattleEntityInBattle entity, HealingModel healingModel) { return new BattleEntityTookHealingMessage(entity, healingModel); }
protected BattleEntityDodgedMessage(BattleEntityInBattle entity, LuckyEvent luckyEvent) { Entity = entity; LuckyEvent = luckyEvent; }
protected BattleEntityTookHealingMessage(BattleEntityInBattle entity, HealingModel healingModel) { Entity = entity; HealingModel = healingModel; }
protected BattleEntityDiedMessage(BattleEntityInBattle entity) { Entity = entity; }
public static BattleEntityInBattle GetPrimaryTargetForAbilityStep(BattleEntityInBattle source, BattleEntityInBattle originalPrimaryTarget, DeadTargetBehavior deadTargetBehavior) { if (originalPrimaryTarget.IsAlive) { return originalPrimaryTarget; //no retargeting, the primary target is still alive } //the original primary target is dead switch (deadTargetBehavior) { case DeadTargetBehavior.ChooseNewRandomFriendlyTarget: return GetRandomAliveFriendlyTarget(source); case DeadTargetBehavior.ChooseNewRandomHostileTarget: return GetRandomAliveHostileTarget(source); } //we've fallen through, our dead target behavior is either apply it to dead target, or abandon //in the case of applying to dead target, we pass the original, and we don't care if he's dead //in the case of abandoning, we would have abandoned on activation if the target was dead, so we must be in a subsequent ability step //just keep beating a dead horse return originalPrimaryTarget; }