public FIGHT_TYPES Attack(Unit target, UNIT_ATTACK type, bool real, bool force_rugged) { int unit_old_str = this.str;//, target_old_str = target.str; int unit_old_ini = this.sel_prop.ini, target_old_ini = target.sel_prop.ini; int unit_dam = 0, unit_suppr = 0, target_dam = 0, target_suppr = 0; int rugged_chance; bool rugged_def = false; int exp_mod; FIGHT_TYPES ret = FIGHT_TYPES.AR_NONE; /* clear flags */ ATTACK_FLAGS strike; /* check if rugged defense occurs */ if (real && type == UNIT_ATTACK.UNIT_ACTIVE_ATTACK) if (this.CheckRuggedDefense(target) || (force_rugged && this.IsClose(target))) { rugged_chance = this.GetRuggedDefenseChance(target); if (Misc.DICE(100) <= rugged_chance || force_rugged) rugged_def = true; } /* PG's formula for initiative is min { base initiative, terrain max initiative } + ( exp_level + 1 ) / 2 + D3 */ /* against aircrafts the initiative is used since terrain does not matter */ /* target's terrain is used for fight */ if ((this.sel_prop.flags & UnitFlags.FLYING) != UnitFlags.FLYING && (target.sel_prop.flags & UnitFlags.FLYING) != UnitFlags.FLYING) { this.sel_prop.ini = Math.Min(this.sel_prop.ini, target.terrain.max_ini); target.sel_prop.ini = Math.Min(target.sel_prop.ini, target.terrain.max_ini); } this.sel_prop.ini += (this.exp_level + 1) / 2; target.sel_prop.ini += (target.exp_level + 1) / 2; /* special initiative rules: antitank inits attack tank|recon: atk 0, def 99 tank inits attack against anti-tank: atk 0, def 99 defensive fire: atk 99, def 0 submarine attacks: atk 99, def 0 ranged attack: atk 99, def 0 rugged defense: atk 0 air unit attacks air defense: atk = def non-art vs art: atk 0, def 99 */ if ((this.sel_prop.flags & UnitFlags.ANTI_TANK) == UnitFlags.ANTI_TANK) if ((target.sel_prop.flags & UnitFlags.TANK) == UnitFlags.TANK) { this.sel_prop.ini = 0; target.sel_prop.ini = 99; } if (((this.sel_prop.flags & UnitFlags.DIVING) == UnitFlags.DIVING) || ((this.sel_prop.flags & UnitFlags.ARTILLERY) == UnitFlags.ARTILLERY) || ((this.sel_prop.flags & UnitFlags.AIR_DEFENSE) == UnitFlags.AIR_DEFENSE) || type == UNIT_ATTACK.UNIT_DEFENSIVE_ATTACK ) { this.sel_prop.ini = 99; target.sel_prop.ini = 0; } if ((this.sel_prop.flags & UnitFlags.FLYING) == UnitFlags.FLYING) if ((target.sel_prop.flags & UnitFlags.AIR_DEFENSE) == UnitFlags.AIR_DEFENSE) this.sel_prop.ini = target.sel_prop.ini; if (rugged_def) this.sel_prop.ini = 0; if (force_rugged) target.sel_prop.ini = 99; /* the dice is rolled after these changes */ if (real) { this.sel_prop.ini += Misc.DICE(3); target.sel_prop.ini += Misc.DICE(3); } #if DEBUG if (real) { Console.WriteLine("{0} Initiative: {1}", this.name, this.sel_prop.ini); Console.WriteLine("{0} Initiative: {1}", target.name, target.sel_prop.ini); if (this.CheckRuggedDefense(target)) Console.WriteLine("Rugged Defense: {0} ({1})", rugged_def ? "yes" : "no", this.GetRuggedDefenseChance(target)); } #endif /* in a real combat a submarine may evade */ if (real && type == UNIT_ATTACK.UNIT_ACTIVE_ATTACK && ((target.sel_prop.flags & UnitFlags.DIVING) == UnitFlags.DIVING)) { if (Misc.DICE(10) <= 7 + (target.exp_level - this.exp_level) / 2) { strike = ATTACK_FLAGS.ATK_NO_STRIKE; ret |= FIGHT_TYPES.AR_EVADED; } else strike = ATTACK_FLAGS.ATK_UNIT_FIRST; #if DEBUG Console.WriteLine("\nSubmarine Evasion: {0} ({1}%)\n", (strike == ATTACK_FLAGS.ATK_NO_STRIKE) ? "yes" : "no", 10 * (7 + (target.exp_level - this.exp_level) / 2)); #endif } else /* who is first? */ if (this.sel_prop.ini == target.sel_prop.ini) strike = ATTACK_FLAGS.ATK_BOTH_STRIKE; else if (this.sel_prop.ini > target.sel_prop.ini) strike = ATTACK_FLAGS.ATK_UNIT_FIRST; else strike = ATTACK_FLAGS.ATK_TARGET_FIRST; /* the one with the highest initiative begins first if not defensive fire or artillery */ if (strike == ATTACK_FLAGS.ATK_BOTH_STRIKE) { /* both strike at the same time */ GetDamage(this, this, target, type, real, rugged_def, out target_dam, out target_suppr); if (target.CheckAttack(this, UNIT_ATTACK.UNIT_PASSIVE_ATTACK)) GetDamage(this, target, this, UNIT_ATTACK.UNIT_PASSIVE_ATTACK, real, rugged_def, out unit_dam, out unit_suppr); target.ApplyDamage(target_dam, target_suppr, this); this.ApplyDamage(unit_dam, unit_suppr, target); } else if (strike == ATTACK_FLAGS.ATK_UNIT_FIRST) { /* unit strikes first */ GetDamage(this, this, target, type, real, rugged_def, out target_dam, out target_suppr); if (target.ApplyDamage(target_dam, target_suppr, this) != 0) if (target.CheckAttack(this, UNIT_ATTACK.UNIT_PASSIVE_ATTACK) && type != UNIT_ATTACK.UNIT_DEFENSIVE_ATTACK) { GetDamage(this, target, this, UNIT_ATTACK.UNIT_PASSIVE_ATTACK, real, rugged_def, out unit_dam, out unit_suppr); this.ApplyDamage(unit_dam, unit_suppr, target); } } else if (strike == ATTACK_FLAGS.ATK_TARGET_FIRST) { /* target strikes first */ if (target.CheckAttack(this, UNIT_ATTACK.UNIT_PASSIVE_ATTACK)) { GetDamage(this, target, this, UNIT_ATTACK.UNIT_PASSIVE_ATTACK, real, rugged_def, out unit_dam, out unit_suppr); if (this.ApplyDamage(unit_dam, unit_suppr, target) == 0) ret |= FIGHT_TYPES.AR_UNIT_ATTACK_BROKEN_UP; } if (unit_get_cur_str(this) > 0) { GetDamage(this, this, target, type, real, rugged_def, out target_dam, out target_suppr); target.ApplyDamage(target_dam, target_suppr, this); } } /* check return value */ if (this.str == 0) ret |= FIGHT_TYPES.AR_UNIT_KILLED; else if (unit_get_cur_str(this) == 0) ret |= FIGHT_TYPES.AR_UNIT_SUPPRESSED; if (target.str == 0) ret |= FIGHT_TYPES.AR_TARGET_KILLED; else if (unit_get_cur_str(target) == 0) ret |= FIGHT_TYPES.AR_TARGET_SUPPRESSED; if (rugged_def) ret |= FIGHT_TYPES.AR_RUGGED_DEFENSE; if (real) { /* cost ammo */ if (Config.supply) { //if (DICE(10)<=target_old_str) this.cur_ammo--; if (target.CheckAttack(this, UNIT_ATTACK.UNIT_PASSIVE_ATTACK) && target.cur_ammo > 0) //if (DICE(10)<=unit_old_str) target.cur_ammo--; } /* costs attack */ if (this.cur_atk_count > 0) this.cur_atk_count--; /* target: loose entrenchment if damage was taken or with a unit.str*10% chance */ if (target.entr > 0) if (target_dam > 0 || Misc.DICE(10) <= unit_old_str) target.entr--; /* attacker looses entrenchment if it got hurt */ if (this.entr > 0 && unit_dam > 0) this.entr--; /* gain experience */ exp_mod = target.exp_level - this.exp_level; if (exp_mod < 1) exp_mod = 1; this.AddExperience(exp_mod * target_dam + unit_dam); exp_mod = this.exp_level - target.exp_level; if (exp_mod < 1) exp_mod = 1; target.AddExperience(exp_mod * unit_dam + target_dam); if (this.IsClose(target)) { this.AddExperience(10); target.AddExperience(10); } /* adjust life bars */ update_bar(this); update_bar(target); } this.sel_prop.ini = unit_old_ini; target.sel_prop.ini = target_old_ini; return ret; }
/* ==================================================================== Go through a complete battle unit vs. target including known(!) defensive support stuff and with no random modifications. Return the final damage taken by both units. As the terrain may have influence the id of the terrain the battle takes place (defending unit's hex) is provided. ==================================================================== */ public static void GetExpectedLosses(Unit unit, Unit target, out int unit_damage, out int target_damage) { int damage, suppr; List<Unit> df_units = new List<Unit>(); #if DEBUG_ATTACK printf( "***********************\n" ); #endif unit.GetDefensiveFireUnits(target, Scenario.vis_units, ref df_units); unit_backup(unit); unit_backup(target); /* let defensive fire go to work (no chance to defend against this) */ foreach (Unit df in df_units) { GetDamage(unit, df, unit, UNIT_ATTACK.UNIT_DEFENSIVE_ATTACK, false, false, out damage, out suppr); if (unit.ApplyDamage(damage, suppr, null) == 0) break; } /* actual fight if attack has strength remaining */ if (unit_get_cur_str(unit) > 0) unit.Attack(target, UNIT_ATTACK.UNIT_ACTIVE_ATTACK, false, false); /* get done damage */ unit_damage = unit.str; target_damage = target.str; unit_restore(ref unit); unit_restore(ref target); unit_damage = unit.str - unit_damage; target_damage = target.str - target_damage; }