public override void QueueAbility(AbilityItemModel abilityItemModel, int[] combatantIDs) { if (castingAbilityItemModel != null || nextAbilityItemModel != null) { // Ignore if ability is already queued Logging.Log.Info("An ability is already queued for player with combatant ID: " + ownerCombatantModel.ID + ", ignoring."); return; } nextAbilityItemModel = abilityItemModel; int abilityIndex = ownerCombatantModel.GetAbilityIndex(abilityItemModel.ID); nextTargetCombatants.Clear(); for (int i = 0; i < combatantIDs.Length; i++) { CombatantModel targetCombatantModel = null; if (combatantIDsToModels.TryGetValue(combatantIDs[i], out targetCombatantModel)) { // Ignore if ability is still cooling down if (ownerCombatantModel.GetStatModifiedAbilityCoolDownTurnsRemaining(abilityIndex) > 0) { Logging.Log.Info("Ability with ID: " + abilityItemModel.ID + " is cooling down, ignoring."); continue; } // Ignore if combatant is already dead if (!targetCombatantModel.IsAlive()) { Logging.Log.Info("Specified target with combatant ID: " + targetCombatantModel.ID + " is dead, ignoring"); continue; } bool isAbilityFriendly = abilityItemModel.IsFriendly(); // Ignore if attempting to cast friendly ability on enemy if (isAbilityFriendly && !allies.Contains(targetCombatantModel)) { Logging.Log.Info("Can't apply friendly ability with ID " + abilityItemModel.ID + " to an enemy, ignoring."); continue; } // Ignore if attempting to cast offensive ability on ally if (!isAbilityFriendly && !enemies.Contains(targetCombatantModel)) { Logging.Log.Info("Can't apply offensive ability with ID " + abilityItemModel.ID + " to an ally, ignoring."); continue; } nextTargetCombatants.Add(targetCombatantModel); } else { // Ignore if invalid combatant ID specified. Logging.Log.Info("Invalid combatant ID specified: " + combatantIDs[i] + ", ignoring."); continue; } } }
private void onTurnElapsed() { // Update combatActionsCollection turn number combatActionsCollection.TurnNumber = TurnDispatcher.TurnNumber; Logging.Log.Info(">>>>>>> CoOpInstanceCombatController START TURN " + combatActionsCollection.TurnNumber); // If we're delaying, decrement delay turns and return if (delayStartTurns > 0) { Logging.Log.Info("CoOpInstanceCombatController delaying start for " + delayStartTurns + " turns..."); delayStartTurns--; return; } // Clear all CombatActionModels for all combatants for (int c = 0; c < allCombatants.Count; c++) { combatActionsCollection[allCombatants[c].ID].Clear(); } foreach (var kvp in teamIDToCombatantModel) { List <CombatantModel> teamCombatants = kvp.Value; for (int i = 0; i < teamCombatants.Count; i++) { CombatantModel combatantModel = teamCombatants[i]; if (!combatantModel.IsAlive()) { continue; } // Advance and apply regen duration abilities and keep track of CombatActionModels combatantModel.RegenOverTimeAbilityDurations.AdvanceAbilityDurations(tickedRegenAbilities); for (int r = 0; r < tickedRegenAbilities.Length && tickedRegenAbilities[r] != null; r++) { AbilityDurationData tickedRegenAbilityDuration = tickedRegenAbilities[r]; combatActionsCollection[combatantModel.ID].Add( applyRegenAbilityEffectToCombatant( tickedRegenAbilityDuration.AbilityEffectType, tickedRegenAbilityDuration.OriginAbilityID, tickedRegenAbilityDuration.OwnerCombatantID, tickedRegenAbilityDuration.PerTickAmount, combatantModel, tickedRegenAbilityDuration.NumTurnsRemaining, tickedRegenAbilityDuration.IsCrit)); } // Clean up any expired regen abilities and keep track of CombatActionModels combatantModel.RegenOverTimeAbilityDurations.RemoveAndResetExpired(expiredRegenAbilities); for (int rx = 0; rx < expiredRegenAbilities.Length && expiredRegenAbilities[rx] != null; rx++) { combatActionsCollection[combatantModel.ID].Add(new CombatActionModel(expiredRegenAbilities[rx].OwnerCombatantID, CombatActionType.ExpireAbility, expiredRegenAbilities[rx].OriginAbilityID, combatantModel.ID, 0, 0, false)); } // Advance and apply damage duration abilities and keep track of CombatActionModels combatantModel.DamageOverTimeAbilityDurations.AdvanceAbilityDurations(tickedDamageAbilities); for (int d = 0; d < tickedDamageAbilities.Length && tickedDamageAbilities[d] != null; d++) { AbilityDurationData tickedDamageAbilityDuration = tickedDamageAbilities[d]; combatActionsCollection[combatantModel.ID].Add( applyDamageAbilityEffectToCombatant( tickedDamageAbilityDuration.AbilityEffectType, tickedDamageAbilityDuration.OriginAbilityID, tickedDamageAbilityDuration.OwnerCombatantID, tickedDamageAbilityDuration.PerTickAmount, combatantModel, tickedDamageAbilityDuration.NumTurnsRemaining, tickedDamageAbilityDuration.IsCrit)); // If target has died then append CombatActionModel and break; if (!combatantModel.IsAlive()) { combatActionsCollection[combatantModel.ID].Add(new CombatActionModel(tickedDamageAbilities[d].OwnerCombatantID, CombatActionType.Death, tickedDamageAbilities[d].OriginAbilityID, combatantModel.ID, 0, 0, false)); break; } } if (!combatantModel.IsAlive()) { continue; } // Clean up any expired damage abilities and keep track of CombatActionModels combatantModel.DamageOverTimeAbilityDurations.RemoveAndResetExpired(expiredDamageAbilities); for (int dx = 0; dx < expiredDamageAbilities.Length && expiredDamageAbilities[dx] != null; dx++) { combatActionsCollection[combatantModel.ID].Add(new CombatActionModel(expiredDamageAbilities[dx].OwnerCombatantID, CombatActionType.ExpireAbility, expiredDamageAbilities[dx].OriginAbilityID, combatantModel.ID, 0, 0, false)); } // Advance stat modifier duration abilities // NOTE: no need to output per tick amount as stat modifiers only report their delta on application. combatantModel.StatModifierAbilityDurations.AdvanceAbilityDurations(tickedStatAbilities); // Expire and remove any stat modifier abilities and keep track of CombatActionModels combatantModel.StatModifierAbilityDurations.RemoveAndResetExpired(expiredStatAbilities); for (int sx = 0; sx < expiredStatAbilities.Length && expiredStatAbilities[sx] != null; sx++) { combatantModel.StatModifiersDeltas.RemoveModifier( expiredStatAbilities[sx].PerTickAmount, expiredStatAbilities[sx].AbilityEffectType); combatActionsCollection[combatantModel.ID].Add( new CombatActionModel(expiredStatAbilities[sx].OwnerCombatantID, CombatActionType.ExpireAbility, expiredStatAbilities[sx].OriginAbilityID, combatantModel.ID, 0, 0, false)); } // Process Combatant's behaviour processCombatantBehaviour(combatantModel); } } if (combatActionsCollection.HasEntries()) { CombatTurnCompleted(combatActionsCollection); } }
public CombatActionModel GetCombatAction(out AbilityItemModel abilityItemModel, ref List <CombatantModel> targets) { if (!ownerCombatantModel.IsAlive()) { throw new Exception("Dead CombatantModel can't perform actions."); } // If already casting. if (castingAbilityItemModel != null) { castingAbilityItemModel.NumCastTurnsElapsed++; abilityItemModel = castingAbilityItemModel; bool allTargetsDead = true; for (int a = 0; a < targetCombatantModels.Count; a++) { if (targetCombatantModels[a].IsAlive()) { allTargetsDead = false; break; } } // If all targets died during cast, then cancel cast if (allTargetsDead) { castingAbilityItemModel.NumCastTurnsElapsed = 0; castingAbilityItemModel = null; return(new CombatActionModel( ownerCombatantModel.ID, CombatActionType.CancelCastingAbility, abilityItemModel.ID, -1, 0, 0, false)); } // Determine number of total cast turns considering cast time stat modifiers int modifiedTotalCastTurns = ownerCombatantModel.GetStatModifiedAbilityTotalCastTurns(castingAbilityIndex); // Handle cast completion (>= insteand of == as expired cast time debuffs can cause user to cast beyond the unmodified amount) if (castingAbilityItemModel.NumCastTurnsElapsed >= modifiedTotalCastTurns) { // Reset ability cast turns and clear reference castingAbilityItemModel.NumCastTurnsElapsed = 0; castingAbilityItemModel = null; // Output alive targets only. for (int t = 0; t < targetCombatantModels.Count; t++) { if (targetCombatantModels[t].IsAlive()) { targets.Add(targetCombatantModels[t]); } } int abilityDurationTurnsRemaining = abilityItemModel.AbilityDurationType == AbilityDurationType.Immediate ? 0 : abilityItemModel.AbilityDurationData.NumTurnsRemaining; // -1 target id signifies multiple targets. return(new CombatActionModel( ownerCombatantModel.ID, CombatActionType.ApplyAbility, abilityItemModel.ID, targets.Count == 1 ? targets[0].ID : -1, 0, abilityDurationTurnsRemaining, false)); } // Still casting, so perform wait. return(new CombatActionModel(ownerCombatantModel.ID, CombatActionType.Wait, -1, -1, 0, 0, false)); } // Determine which abilities are available (not on cooldown) bool abilityAvailable = false; for (int i = 0; i < ownerCombatantModel.AbilityItemModels.Length; i++) { if (ownerCombatantModel.GetStatModifiedAbilityCoolDownTurnsRemaining(i) > 0) { availableAbilities[i] = null; } else { abilityAvailable = true; availableAbilities[i] = ownerCombatantModel.AbilityItemModels[i]; } } // If all abilities on cooldown, return wait. if (!abilityAvailable) { abilityItemModel = null; return(new CombatActionModel(ownerCombatantModel.ID, CombatActionType.Wait, -1, -1, 0, 0, false)); } targetCombatantModels.Clear(); castingAbilityIndex = -1; determineAbilityAndTargets(availableAbilities, ref castingAbilityItemModel, ref castingAbilityIndex, ref targetCombatantModels); abilityItemModel = castingAbilityItemModel; // If we were unable to determine an AbilityItemModel or find valid targets this turn, perform wait. if (castingAbilityItemModel == null || targetCombatantModels.Count == 0) { return(new CombatActionModel(ownerCombatantModel.ID, CombatActionType.Wait, -1, -1, 0, 0, false)); } // Start casting return(new CombatActionModel(ownerCombatantModel.ID, CombatActionType.StartCastingAbility, abilityItemModel.ID, targetCombatantModels.Count == 1 ? targetCombatantModels[0].ID : -1, 0, abilityItemModel.CastTurns, false)); }