private void CheckForRegenProc() { turnsSinceRegenProc++; turnsUntilRegenProc = playerTeam.GetNumLivingMembers() + enemyTeam.GetNumLivingMembers(); if (turnsSinceRegenProc >= turnsUntilRegenProc) { turnsSinceRegenProc = 0; Stats stats; for (int i = 0; i < charactersInBattle.Count; i++) { if (!charactersInBattle[i].dead) { stats = charactersInBattle[i].stats; long hpRegen = stats.Get(Stat.hp_regen); long mpRegen = stats.Get(Stat.mp_regen); //if character is magic inept, cancel any mp gain if (charactersInBattle[i].HasSkill(SkillId.MAGIC_INEPT)) { mpRegen = 0; } stats.Augment(Stat.hp, hpRegen); stats.Augment(Stat.mp, mpRegen); //enforcing limits here also takes care of any status effect damage stats.EnforceLimits(); if (hpRegen > 0 || mpRegen > 0) { //broadcast this if at least one stat was regen'd if (ERegenProc != null) { ERegenProc(charactersInBattle[i], hpRegen, mpRegen); } } } } } }
public void ResolveActiveSkillCommand() { SkillResult curResult; CharacterData source = activeSkillCommand.source; CharacterData curTarget; currentSkillResults.Clear(); //tally up damage that was reflected back at the source float accumulatedDamageReflected = 0; //tally up other properties to be used for stats float accumulatedHpChange = 0; float accumulatedDamageAbsorbed = 0; uint totalCrits = 0; uint totalEvades = 0; uint totalBackAttacks = 0; uint totalDefends = 0; //we take special actions for the defend and surrender skills if (activeSkillCommand.skill.id == SkillId.DEFEND) { source.isDefending = true; curResult = PoolManager.poolSkillResults.GetNext(); curResult.Init(activeSkillCommand.skill, source, activeSkillCommand.targets[0]); currentSkillResults.Add(curResult); } else if (activeSkillCommand.skill.id == SkillId.SURRENDER) { //no action taken here; battle will be ended before anything meaningful can happen } else { for (int i = 0; i < activeSkillCommand.targets.Count; i++) { curTarget = activeSkillCommand.targets[i]; curResult = activeSkillCommand.skill.GenerateResultToTarget(source, curTarget); Stats statChanges = curResult.statChanges; //make adjustments to the damage based on battle conditions AND the target's stats/conditions float hpChange = statChanges.Get(Stat.hp); //check this value here before any kind of absorb or defend is applied if (hpChange < 0) { curResult.skillAttemptedToInflictDamage = true; } //REMOVED: add extra damage for huntress kill count, also increase damage if target is "marked" //check for special case where pickaxe deals bonus damage to something turned to stone if (curTarget.HasStatusEffect(StatusEffectType.STONE) && curResult.skill.id == SkillId.PICKAXE) { hpChange *= GameContext.PICKAXE_AGAINST_STONE_MULTIPLIER; } //back attack? can't back attack mineable ore if (curResult.skill.canBackAttack && !curTarget.isOre) { if (curTarget.BackIsTurnedTo(source) && !curTarget.IsImmuneToBackAttackBonus()) { hpChange *= GameContext.BACK_ATTACK_MULTIPLIER; curResult.wasBackAttack = true; totalBackAttacks++; } } //boost magic attack and phys attacks if (curResult.skill.IsType(SkillType.MAGICAL)) { long magBoost = source.stats.Get(Stat.magic_boost); if (magBoost != 0) { float boostAmt = 1f + (magBoost / 100f); hpChange *= boostAmt; } //hard max for group heal: cannot exceed half of target's max hp if (curResult.skill.id == SkillId.GROUP_HEAL) { long max = curTarget.stats.Get(Stat.max_hp) / 2; if (hpChange > max) { hpChange = max; } } } else if (curResult.skill.IsType(SkillType.PHYSICAL)) { long physBoost = source.stats.Get(Stat.phys_boost); if (physBoost != 0) { float boostAmt = 1f + (physBoost / 100f); hpChange *= boostAmt; } } //crit. right now just manually check when using the strike skill. can't crit ore if (curResult.skill.id == SkillId.STRIKE && !curTarget.isOre) { if (activeSkillCommand.source.RollPrimaryCritChance()) { hpChange = activeSkillCommand.source.GetPrimaryCritBonusAppliedToDamage(hpChange); curResult.wasCrit = true; } } if (curResult.wasDoubleCrit) { totalCrits += 2; } else if (curResult.wasCrit) { totalCrits++; } //apply super bonus crit damage? if (curResult.wasCrit || curResult.wasDoubleCrit) { if (adventure.nextCritDamageBoostPercent > 0) { float boostAmt = 1f + (adventure.nextCritDamageBoostPercent / 100f); hpChange *= boostAmt; adventure.nextCritDamageBoostPercent = 0; } } bool sourceMissed = false; bool targetEvaded = false; //additional checks for non-friendly skills: (evade, reduce damage, defend, etc) if (!curResult.skill.isFriendly) { //damage absorb/reduce? long reduction = curTarget.stats.Get(Stat.dmg_reduction); if (curTarget.HasStatusEffect(StatusEffectType.STONE) && curResult.skill.id != SkillId.PICKAXE) { reduction += 50; } if (reduction != 0) { float prevHpChange = hpChange; float reductionAmt = 1f - (reduction / 100f); hpChange *= reductionAmt; accumulatedDamageAbsorbed += (Math.Abs(prevHpChange) - Math.Abs(hpChange)); } //is target defending? cut damage in half if (curTarget.isDefending) { hpChange = hpChange / 2f; totalDefends++; } //if the skill attempted to inflict damage but damage was reduced below 1, make it 1 if (curResult.skillAttemptedToInflictDamage && Math.Abs(hpChange) < 1) { hpChange = -1; } //did the source miss because they were confused? sourceMissed = activeSkillCommand.source.HasStatusEffect(StatusEffectType.CONFUSION) && Utils.PercentageChance(75); //did the target evade successfully? note that they would never evade a friendly attack CharacterTeam targetsTeam = adventure.GetTeamForFaction(curTarget.curFaction); bool targetHasPartyProtection = curTarget.HasSkill(SkillId.PARTY_PROTECTION) && targetsTeam.GetNumLivingMembers() > 1; if (curResult.source.curFaction != curResult.target.curFaction && (curTarget.PassesEvasionCheck() || targetHasPartyProtection)) { targetEvaded = true; totalEvades++; } } //is target an ore? they take damage from the ore bust stat instead (min 1) if (curTarget.isOre && curResult.skillAttemptedToInflictDamage) { //ore bust is a mixture of our existing ore bust stat plus whatever the attack itself specified. it's always at least 1 long oreBust = activeSkillCommand.source.stats.Get(Stat.ore_bust); oreBust += statChanges.Get(Stat.ore_bust); oreBust = Math.Max(oreBust, 1); //ore bust is stored as a positive number but must be converted to negative for damage hpChange = -oreBust; } //finalize damage statChanges.Set(Stat.hp, (long)hpChange); //debug invincible characters if ((GameContext.DEBUG_INVINCIBLE_CHARACTERS && curTarget.curFaction == Faction.PLAYER) || (GameContext.DEBUG_INVINCIBLE_ENEMIES && curTarget.curFaction == Faction.ENEMY)) { if (statChanges.Get(Stat.hp) < 0) { statChanges.Set(Stat.hp, 0); } } //this line will always knock the target down to 1hp //statChanges.Set(Stat.hp, -(curTarget.stats.Get(Stat.max_hp) - 1) ); if (sourceMissed || targetEvaded) { curResult.SetAsEvaded(); } else { curResult.ResolveEffects(adventure); //resolve team effects for the first result (we only do this once because technically all results contain copies of the same team effects if (i == 0) { curResult.ResolveTeamEffects(adventure); } //NOW THAT THE ATTACK CONNECTED AND WAS RESOLVED: //tally it accumulatedHpChange += hpChange; //damage reflect long damageReflect = curTarget.stats.Get(Stat.dmg_reflection); if (damageReflect > 0 && hpChange < 0) { float reflectValue = (hpChange * (damageReflect / 100f)); accumulatedDamageReflected += Math.Abs(reflectValue); } //dark savior if (curTarget.stats.Get(Stat.hp) == 0) { CharacterData savior = curTarget.GetCurTeam().GetMemberWhoCanUseDarkSaviorThatIsnt(curTarget); if (savior != null) { curTarget.stats.Set(Stat.hp, 1); savior.IncrementDarkSaviorUseCount(); curResult.invokedDarkSavior = true; } } //was this a transmute? max one per battle, and must be a common enemy if (curResult.transmutesTarget) { bool isCommonEnemy = false; if (curResult.target is EnemyCharacterData && !curResult.target.isOre) { if (((EnemyCharacterData)curResult.target).rank == EnemyRank.NORM) { isCommonEnemy = true; } } //special exception for the grill which currently counts as a NORM enemy if (curResult.target.id == EnemyId.GRILL) { isCommonEnemy = false; } if (isCommonEnemy && !CurBattleTransmuteLimitReached()) { curBattleTransmuteCount++; CharacterData newOre = CharacterData.Get(adventure.curOreTable.RollSpawn()).GetCopy(); newOre.UnsetFlags(CharacterFlags.DEATH_OPTIONAL); //unset the death optional flag, since the player transmuted them on purpose (it would be weird to instantly end the battle now) curResult.transmuteResult = newOre; curTarget.GetCurTeam().TurnMemberInto(curTarget, newOre); turnQueue.Remove(curTarget); RefreshCharactersInBattleList(); } else { curResult.transmutesTarget = false; curResult.failedATransmute = true; } } } currentSkillResults.Add(curResult); } } //last step is to see if we create an additional result to send reflected damage back to the source if (accumulatedDamageReflected > 0 && !source.HasSkill(SkillId.PARTY_PROTECTION)) { curResult = PoolManager.poolSkillResults.GetNext(); curResult.Init(Skill.GetSkill(SkillId.STRIKE), source, source); curResult.statChanges.Set(Stat.hp, -accumulatedDamageReflected); curResult.ResolveEffects(adventure); currentSkillResults.Add(curResult); } //REMOVED: some local stat reporting to the save file if (ESkillResolved != null) { ESkillResolved(currentSkillResults); } }
//return true if this evals true for any target provided private void EvalCondition(SodaScriptCondition inCondition, SodaScriptComparison inComparison, string inCompareValue, List <CharacterData> inTargets) { CharacterData targ; EnemyCharacterData ecd; PlayerCharacterData pcd; culledTargets.Clear(); if (inCondition == SodaScriptCondition.WEAKEST) { culledTargets.Add(GetLowestHpPercentTarget(inTargets)); } else if (inCondition == SodaScriptCondition.TEAM_ALIVE) { targ = inTargets[0]; CharacterTeam team = targ.IsInFaction(Faction.PLAYER) ? adventure.playerTeam : adventure.enemyTeam; if (EvalNumericComparison(team.GetNumLivingMembers(), int.Parse(inCompareValue), inComparison)) { culledTargets.Add(targ); } } else if (inCondition == SodaScriptCondition.TEAM_HP) { targ = inTargets[0]; CharacterTeam team = targ.IsInFaction(Faction.PLAYER) ? adventure.playerTeam : adventure.enemyTeam; if (EvalNumericComparison(team.GetTeamHPPercent(), int.Parse(inCompareValue), inComparison)) { culledTargets.Add(targ); } } else if (inCondition == SodaScriptCondition.TEAM_NUM_BACK_TURNED) { targ = inTargets[0]; CharacterTeam team = targ.IsInFaction(Faction.PLAYER) ? adventure.playerTeam : adventure.enemyTeam; int numBackTurned = team.GetNumMembersWithBackTurnedTo(activeCharacter); if (EvalNumericComparison(numBackTurned, int.Parse(inCompareValue), inComparison)) { culledTargets.Add(targ); } } else { for (int i = 0; i < inTargets.Count; i++) { targ = inTargets[i]; if (inCondition == SodaScriptCondition.HP) { if (EvalNumericComparison(targ.GetHpPercent(), int.Parse(inCompareValue), inComparison)) { culledTargets.Add(targ); } } else if (inCondition == SodaScriptCondition.MP) { if (EvalNumericComparison(targ.GetMpPercent(), int.Parse(inCompareValue), inComparison)) { culledTargets.Add(targ); } } else if (inCondition == SodaScriptCondition.POSITION) { if (EvalNumericComparison(targ.originalOrderInTeam + 1, int.Parse(inCompareValue), inComparison)) { culledTargets.Add(targ); } } else if (inCondition == SodaScriptCondition.RANK) { bool foundRank = false; EnemyRank enemyRank = EnemyRank.NORM; EnemyRank parsedRank = (EnemyRank)Enum.Parse(typeof(EnemyRank), inCompareValue); if (targ is EnemyCharacterData) { foundRank = true; ecd = (EnemyCharacterData)targ; enemyRank = ecd.rank; } else if (targ.id == CharId.JANITOR) { foundRank = true; enemyRank = EnemyRank.BOSS; } if (foundRank) { if (EvalNumericComparison((int)enemyRank, (int)parsedRank, inComparison)) { culledTargets.Add(targ); } } } else if (inCondition == SodaScriptCondition.IS_ORE) { if (targ.isOre) { culledTargets.Add(targ); } } else if (inCondition == SodaScriptCondition.ISNT_ORE) { if (!targ.isOre) { culledTargets.Add(targ); } } else if (inCondition == SodaScriptCondition.IS_JANITOR) { if (targ.id == CharId.JANITOR) { culledTargets.Add(targ); } } else if (inCondition == SodaScriptCondition.ISNT_JANITOR) { if (targ.id != CharId.JANITOR) { culledTargets.Add(targ); } } else if (inCondition == SodaScriptCondition.BACK_IS_TURNED) { if (!targ.isOre && targ.BackIsTurnedTo(activeCharacter)) { culledTargets.Add(targ); } } else if (inCondition == SodaScriptCondition.BACK_ISNT_TURNED) { if (!targ.isOre && !targ.BackIsTurnedTo(activeCharacter)) { culledTargets.Add(targ); } } else if (inCondition == SodaScriptCondition.HAS_ESSENCE) { if (targ.hasEssence) { culledTargets.Add(targ); } } else if (inCondition == SodaScriptCondition.NO_ESSENCE) { if (!targ.hasEssence) { culledTargets.Add(targ); } } else if (inCondition == SodaScriptCondition.STATUS) { StatusEffectCategory parsedCategory = (StatusEffectCategory)Enum.Parse(typeof(StatusEffectCategory), inCompareValue); if (parsedCategory == StatusEffectCategory.POSITIVE) { bool hasPos = targ.HasPostiveStatusEffect(); if ((inComparison == SodaScriptComparison.EQUALS && hasPos) || (inComparison == SodaScriptComparison.NOT_EQUALS && !hasPos)) { culledTargets.Add(targ); } } else if (parsedCategory == StatusEffectCategory.NEGATIVE) { bool hasNeg = targ.HasNegativeStatusEffect(); if ((inComparison == SodaScriptComparison.EQUALS && hasNeg) || (inComparison == SodaScriptComparison.NOT_EQUALS && !hasNeg)) { culledTargets.Add(targ); } } else if (parsedCategory == StatusEffectCategory.NONE) { bool hasAny = targ.HasAnyStatusEffect(); if ((inComparison == SodaScriptComparison.EQUALS && !hasAny) || (inComparison == SodaScriptComparison.NOT_EQUALS && hasAny)) { culledTargets.Add(targ); } } } else { throw new Exception("Trigger condition " + inCondition + " is not supported as a condition"); } } //do a final list sort based on the condition? if (inCondition == SodaScriptCondition.HP) { if (inComparison == SodaScriptComparison.GREATER_THAN) { culledTargets = culledTargets.OrderByDescending(t => t.GetHpPercent()).ToList <CharacterData>(); } else if (inComparison == SodaScriptComparison.LESS_THAN) { culledTargets = culledTargets.OrderBy(t => t.GetHpPercent()).ToList <CharacterData>(); } } else if (inCondition == SodaScriptCondition.MP) { if (inComparison == SodaScriptComparison.GREATER_THAN) { culledTargets = culledTargets.OrderByDescending(t => t.GetMpPercent()).ToList <CharacterData>(); } else if (inComparison == SodaScriptComparison.LESS_THAN) { culledTargets = culledTargets.OrderBy(t => t.GetMpPercent()).ToList <CharacterData>(); } } } potentialTargets.Clear(); potentialTargets.AddRange(culledTargets); }