///////////// // Publics // ///////////// /// <summary> /// Get the result of one monster attacking another /// </summary> public static AttackResult AttackMonster(Monster actor, Monster target) { // Catch them errors first and foremost if (actor == null) { throw new ArgumentNullException("Invalid actor supplied"); } if (target == null) { throw new ArgumentNullException("Invalid target supplied"); } // Get overall attack score. If actor has beneficial AURA stacks, add +20 to attack int attackScore = actor.Strength; for (int i = 0; i < actor.GetBuffStacks(Buff.Aura); i++) { // Ignore Undead unless bug fixed if (!Globals.BUG_FIXES && i >= 7) { break; } // Attack bonus only applies once for a family match if (target.Families.Contains((MonsterFamily)i)) { attackScore += AURA_BONUS; break; } } // Determine total successful hits and overall damage. Apply status effects if necessary. List <string> results = new List <string>(); int totalHits = GetTotalHits(actor, target); if (totalHits == 0) { return(new AttackResult(0, 0, new List <string>())); } int damage = 0; bool critFlag = false; for (int i = 0; i < totalHits; i++) { // Get damage and add critical bonus damage if rolled int dmgRoll = Globals.rnd.Next(attackScore, attackScore * 2 + 1) - target.Defense; damage += dmgRoll > 0 ? dmgRoll : 0; if (Globals.rnd.Next(100) < CRIT_RATE) { damage += attackScore; // Only add the crit message once if (!critFlag) { critFlag = true; results.Add(CRIT_MESSAGE); } } } // Apply HP and MP drain effects based on totalHits if (actor.AttackEffects.Contains("Drain HP")) { int hpAmt = target.HPMax / 16; int dranAmt = hpAmt * totalHits; // Opposite effect vs. Undead if (target.Families.Contains(MonsterFamily.Undead)) { actor.DamageHP(dranAmt); // TODO: This is awkward here if (actor.IsDead()) { MonoMonster m = (MonoMonster)actor; m.IsFading = true; } target.HealHP(dranAmt); } else { actor.HealHP(dranAmt); target.DamageHP(dranAmt); } } if (actor.AttackEffects.Contains("Drain MP")) { int mpAmt = target.MPMax / 16; int asplAmt = mpAmt * totalHits; // Opposite effect vs. Undead if (target.Families.Contains(MonsterFamily.Undead)) { actor.DamageMP(asplAmt); target.HealMP(asplAmt); } else { actor.HealMP(asplAmt); target.DamageMP(asplAmt); } } // Apply actor's attack effect(s), if any, to the target if (actor.AttackEffects.Count > 0) { // Target rolls magic blocks against totalHits. If any hit makes it through, apply all status effects int statusHits = totalHits - target.RollMagicBlocks(); if (statusHits > 0) { foreach (string effect in actor.AttackEffects) { if (Enum.TryParse <PermStatus>(effect, out PermStatus permStat)) { target.AddPermStatus(permStat); results.Add(permStatusMessageDict[permStat]); continue; } if (Enum.TryParse <TempStatus>(effect, out TempStatus tempStat)) { target.AddTempStatus(tempStat); results.Add(tempStatusMessageDict[tempStat]); continue; } } } } // Apply the damage and return the overall results target.DamageHP(damage); if (target.IsDead()) { results.Add(target.Name + " fell"); } return(new AttackResult(totalHits, damage, results)); }
///////////// // Helpers // ///////////// /// <summary> /// Return the total number of successful hits by the acting monster against a target /// </summary> private static int GetTotalHits(Monster actor, Monster target) { int totalHits = actor.RollHits() - target.RollBlocks(); return(totalHits > 0 ? totalHits : 0); }