예제 #1
0
 /*
 ====================================================================
 CreateAction a unit by passing a Unit struct with the following stuff set:
   x, y, str, entr, exp, delay, orient, nation, player.
 This function will use the passed values to create a Unit struct
 with all values set then.
 ====================================================================
 */
 public static Unit CreateUnit(Unit_Lib_Entry prop, Unit_Lib_Entry trsp_prop, Unit unit_base)
 {
     Unit unit;
     if (prop == null) return null;
     unit = new Unit();
     /* shallow copy of properties */
     unit.prop = prop;
     unit.sel_prop = unit.prop;
     unit.embark = UnitEmbarkTypes.EMBARK_NONE;
     /* assign the passed transporter without any check */
     if (trsp_prop != null && (prop.flags & UnitFlags.FLYING) != UnitFlags.FLYING && (prop.flags & UnitFlags.SWIMMING) != UnitFlags.SWIMMING)
     {
         unit.trsp_prop = trsp_prop;
         /* a sea/air ground transporter is active per default */
         if ((trsp_prop.flags & UnitFlags.SWIMMING) == UnitFlags.SWIMMING)
         {
             unit.embark = UnitEmbarkTypes.EMBARK_SEA;
             unit.sel_prop = unit.trsp_prop;
         }
         if ((trsp_prop.flags & UnitFlags.FLYING) == UnitFlags.FLYING)
         {
             unit.embark = UnitEmbarkTypes.EMBARK_AIR;
             unit.sel_prop = unit.trsp_prop;
         }
     }
     /* copy the base values */
     unit.delay = unit_base.delay;
     unit.x = unit_base.x; unit.y = unit_base.y;
     unit.str = unit_base.str; unit.entr = unit_base.entr;
     unit.player = unit_base.player;
     unit.nation = unit_base.nation;
     unit.name = unit_base.name;
     unit.AddExperience(unit_base.exp_level * 100);
     unit.orient = unit_base.orient;
     unit.AdjustIcon();
     unit.unused = true;
     unit.supply_level = 100;
     unit.cur_ammo = unit.prop.ammo;
     unit.cur_fuel = unit.prop.fuel;
     if ((unit.cur_fuel == 0) && (unit.trsp_prop != null) &&
         (!string.IsNullOrEmpty(unit.trsp_prop.id)) && unit.trsp_prop.fuel > 0)
         unit.cur_fuel = unit.trsp_prop.fuel;
     unit.tag = unit_base.tag;
     /* update life bar properties */
     update_bar(unit);
     /* allocate backup mem */
     unit.backup = new Unit();
     return unit;
 }
예제 #2
0
        int x, y; /* position that is this group's center of interest */

        #endregion Fields

        #region Methods

        public static void ai_group_add_unit(AI_Group group, Unit unit)
        {
            /* sort unit into units list in the order in which units are
             * supposed to move. artillery fire is first but moves last
             * thus artillery comes last in this list. that it fires first
             * is handled by ai_group_handle_next_unit. list order is:
             * bombers, ground units, fighters, artillery */
            if (((unit.prop.flags & UnitFlags.ARTILLERY) == UnitFlags.ARTILLERY) ||
                 ((unit.prop.flags & UnitFlags.AIR_DEFENSE) == UnitFlags.AIR_DEFENSE))
                group.units.Add(unit);
            else
                if (unit.prop.unit_class == 9 || unit.prop.unit_class == 10)
                {
                    /* tactical and high level bomber */
                    group.units.Insert(0, unit);
                    group.bomber_count++;
                }
                else
                    if ((unit.prop.flags & UnitFlags.FLYING) == UnitFlags.FLYING)
                    {
                        /* airborne ground units are not in this section ... */
                        group.units.Insert(group.bomber_count + group.ground_count, unit);
                        group.aircraft_count++;
                    }
                    else
                    {
                        /* everything else: ships and ground units */
                        group.units.Insert(group.bomber_count, unit);
                        group.ground_count++;
                    }
            /* HACK: set hold_pos flag for those units that should not move due
               to high entrenchment or being close to artillery and such; but these
               units will attack, too and may move if's really worth it */
            if ((unit.sel_prop.flags & UnitFlags.FLYING) != UnitFlags.FLYING)
                if ((unit.sel_prop.flags & UnitFlags.SWIMMING) != UnitFlags.SWIMMING)
                {
                    int i, nx, ny;
                    bool no_move = false;
                    if (Engine.map.map[unit.x, unit.y].obj) no_move = true;
                    if (group.order < 0)
                    {
                        if (Engine.map.map[unit.x, unit.y].nation != null) no_move = true;
                        if (unit.entr >= 6) no_move = true;
                        if ((unit.sel_prop.flags & UnitFlags.ARTILLERY) == UnitFlags.ARTILLERY) no_move = true;
                        if ((unit.sel_prop.flags & UnitFlags.AIR_DEFENSE) == UnitFlags.AIR_DEFENSE) no_move = true;
                        for (i = 0; i < 6; i++)
                            if (Misc.get_close_hex_pos(unit.x, unit.y, i, out nx, out ny))
                                if (Engine.map.map[nx, ny].g_unit != null)
                                {
                                    if ((Engine.map.map[nx, ny].g_unit.sel_prop.flags & UnitFlags.ARTILLERY) == UnitFlags.ARTILLERY)
                                    {
                                        no_move = true;
                                        break;
                                    }
                                    if ((Engine.map.map[nx, ny].g_unit.sel_prop.flags & UnitFlags.AIR_DEFENSE) == UnitFlags.AIR_DEFENSE)
                                    {
                                        no_move = true;
                                        break;
                                    }
                                }
                    }
                    if (no_move)
                        unit.cur_mov = 0;
                }
        }
예제 #3
0
 /*
 ====================================================================
 Evaluate a unit's attack against target.
   score_base: basic score for attacking
   score_rugged: score added for each rugged def point (0-100)
                 of target
   score_kill: score unit receives for each (expected) point of
               strength damage done to target
   score_loss: score that is substracted per strength point
               unit is expected to loose
 The final score is stored to 'result' and True if returned if the
 attack may be performed else False.
 ====================================================================
 */
 static bool unit_evaluate_attack(Unit unit, Unit target, int score_base, int score_rugged, int score_kill, int score_loss, out int result)
 {
     int unit_dam = 0, target_dam = 0, rugged_def = 0;
     result = 0;
     if (!unit.CheckAttack(  target, Unit.UNIT_ATTACK.UNIT_ACTIVE_ATTACK)) return false;
     Unit.GetExpectedLosses(unit, target, out unit_dam, out target_dam);
     if (unit.CheckRuggedDefense( target))
         rugged_def = unit.GetRuggedDefenseChance( target);
     if (rugged_def < 0) rugged_def = 0;
     result = score_base + rugged_def * score_rugged + target_dam * score_kill + unit_dam * score_loss;
     #if DEBUG
     Console.WriteLine("  {0}: {1}: bas:{2}, rug:{3}, kil:{4}, los: {5} = {6}", unit.name, target.name,
     score_base,
     rugged_def * score_rugged,
     target_dam * score_kill,
     unit_dam * score_loss, result);
     #endif
     /* if target is a df unit give a small bonus */
     if (((target.sel_prop.flags & UnitFlags.ARTILLERY) == UnitFlags.ARTILLERY) ||
         ((target.sel_prop.flags & UnitFlags.AIR_DEFENSE) == UnitFlags.AIR_DEFENSE))
         result += score_kill;
     return true;
 }
예제 #4
0
 /*
 ====================================================================
 Get the best target and attack by range. Do not try to move the
 unit yet. If there is no target at all do nothing.
 ====================================================================
 */
 static void ai_fire_artillery(Unit unit, AI_Group group)
 {
     AI_Eval eval = ai_create_eval(unit, group, unit.x, unit.y);
     if (ai_evaluate_hex(ref eval) && eval.target != null)
     {
         Action.action_queue_attack(unit, eval.target);
     #if DEBUG
         Console.WriteLine("{0} attacks first {1}", unit.name, eval.target.name);
     #endif
     }
 }
예제 #5
0
        /*
        ====================================================================
        Get the best target for unit if any.
        ====================================================================
        */
        static bool ai_get_best_target(Unit unit, int x, int y, AI_Group group, out Unit target, out int score)
        {
            int old_x = unit.x, old_y = unit.y;
            int pos_targets;
            List<Unit> targets;
            int score_atk_base, score_rugged, score_kill, score_loss;

            /* scores */
            score_atk_base = 20 + group.order * 10;
            score_rugged = -1;
            score_kill = (group.order + 3) * 10;
            score_loss = (2 - group.order) * -10;

            unit.x = x; unit.y = y;
            target = null;
            score = -999999;
            /* if the transporter is needed attacking is suicide */
            if (Engine.map.mask[x, y].mount != 0 && !string.IsNullOrEmpty(unit.trsp_prop.id))
                return false;
            /* gather targets */
            targets = ai_gather_targets(unit, x, y);
            /* evaluate all attacks */
            if (targets != null)
            {
                foreach (Unit entry in targets)
                    if (!AI_Group.unit_evaluate_attack(unit, entry, score_atk_base, score_rugged, score_kill, score_loss, out entry.target_score))
                        targets.Remove(entry); /* erroneous entry: can't be attacked */
                /* check whether any positive targets exist */
                pos_targets = 0;
                foreach (Unit entry in targets)
                    if (entry.target_score > 0)
                    {
                        pos_targets = 1;
                        break;
                    }
                /* get best target */
                foreach (Unit entry in targets)
                {
                    /* if unit is on an objective or center of interest give a bonus
                    as this tile must be captured by all means. but only do so if there
                    is no other target with a positive value */
                    if (pos_targets == 0)
                        if ((entry.x == group.x && entry.y == group.y) || Engine.map.map[entry.x, entry.y].obj)
                        {
                            entry.target_score += 100;
            #if DEBUG
                            Console.WriteLine("    + 100 for {0}: capture by all means", entry.name);
            #endif
                        }

                    if (entry.target_score > score)
                    {
                        target = entry;
                        score = entry.target_score;
                    }
                }
                targets.Clear();
            }
            unit.x = old_x;
            unit.y = old_y;
            return (target != null);
        }
예제 #6
0
 public static void action_queue_split(Unit unit, int str, int x, int y, Unit partner)
 {
     Action action = CreateAction(EngineActionsTypes.ACTION_SPLIT);
     action.unit = unit;
     action.target = partner;
     action.x = x; action.y = y;
     action.str = str;
     action_queue(action);
 }
예제 #7
0
 static void ai_count_df_units(Unit unit, int x, int y, out int result)
 {
     DefCtx ctx = new DefCtx();
     ctx.unit = unit;
     ctx.count = 0;
     result = 0;
     if ((unit.sel_prop.flags & UnitFlags.ARTILLERY) == UnitFlags.ARTILLERY)
         return;
     AI.ai_eval_hexes(x, y, 3, new AI.eval_func_delegate(hex_df_unit), ctx);
     /* only three defenders are taken in account */
     if (result > 3)
         result = 3;
 }
예제 #8
0
 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;
 }
예제 #9
0
 public bool CheckAttack(Unit target, UNIT_ATTACK type)
 {
     if (target == null || this == target) return false;
     if (Player.player_is_ally(this.player, target.player)) return false;
     if (((this.sel_prop.flags & UnitFlags.FLYING) == UnitFlags.FLYING) &&
         (target.sel_prop.flags & UnitFlags.FLYING) != UnitFlags.FLYING)
         if (this.sel_prop.rng == 0)
             if (this.x != target.x || this.y != target.y)
                 return false; /* range 0 means above unit for an aircraft */
     /* if the target flys and the unit is ground with a range of 0 the aircraft
        may only be harmed when above unit */
     if (((this.sel_prop.flags & UnitFlags.FLYING) != UnitFlags.FLYING) &&
         (target.sel_prop.flags & UnitFlags.FLYING) == UnitFlags.FLYING)
         if (this.sel_prop.rng == 0)
             if (this.x != target.x || this.y != target.y)
                 return false;
     /* only destroyers may harm submarines */
     if (((target.sel_prop.flags & UnitFlags.DIVING) == UnitFlags.DIVING) &&
         (this.sel_prop.flags & UnitFlags.DESTROYER) != UnitFlags.DESTROYER) return false;
     if ((Engine.terrain.weatherTypes[Scenario.cur_weather].flags & WEATHER_FLAGS.NO_AIR_ATTACK) == WEATHER_FLAGS.NO_AIR_ATTACK)
     {
         if ((this.sel_prop.flags & UnitFlags.FLYING) == UnitFlags.FLYING) return false;
         if ((target.sel_prop.flags & UnitFlags.FLYING) == UnitFlags.FLYING) return false;
     }
     if (type == UNIT_ATTACK.UNIT_ACTIVE_ATTACK)
     {
         /* agressor */
         if (this.cur_ammo <= 0) return false;
         if (this.sel_prop.atks[target.sel_prop.trgt_type] <= 0) return false;
         if (this.cur_atk_count == 0) return false;
         if (!this.IsClose(target) && Misc.get_dist(this.x, this.y, target.x, target.y) > this.sel_prop.rng) return false;
     }
     else
         if (type == UNIT_ATTACK.UNIT_DEFENSIVE_ATTACK)
         {
             /* defensive fire */
             if (this.sel_prop.atks[target.sel_prop.trgt_type] <= 0) return false;
             if (this.cur_ammo <= 0) return false;
             if ((this.sel_prop.flags & (UnitFlags.INTERCEPTOR | UnitFlags.ARTILLERY | UnitFlags.AIR_DEFENSE)) == 0) return false;
             if ((target.sel_prop.flags & (UnitFlags.ARTILLERY | UnitFlags.AIR_DEFENSE | UnitFlags.SWIMMING)) != 0) return false;
             if ((this.sel_prop.flags & UnitFlags.INTERCEPTOR) == UnitFlags.INTERCEPTOR)
             {
                 /* the interceptor is propably not beside the attacker so the range check is different
                  * can't be done here because the unit the target attacks isn't passed so
                  *  GetDefensiveFireUnits() must have a look itself
                  */
             }
             else
                 if (Misc.get_dist(this.x, this.y, target.x, target.y) > this.sel_prop.rng) return false;
         }
         else
         {
             /* counter-attack */
             if (this.cur_ammo <= 0) return false;
             if (!this.IsClose(target) && Misc.get_dist(this.x, this.y, target.x, target.y) > this.sel_prop.rng) return false;
             if (this.sel_prop.atks[target.sel_prop.trgt_type] == 0) return false;
             /* artillery may only defend against close units */
             if ((this.sel_prop.flags & UnitFlags.ARTILLERY) == UnitFlags.ARTILLERY)
                 if (!this.IsClose(target))
                     return false;
             /* you may defend against artillery only when close */
             if ((target.sel_prop.flags & UnitFlags.ARTILLERY) == UnitFlags.ARTILLERY)
                 if (!this.IsClose(target))
                     return false;
         }
     return true;
 }
예제 #10
0
 public static void unit_restore(ref Unit unit)
 {
     if (unit.backup != null && !string.IsNullOrEmpty(unit.backup.prop.id))
     {
         unit = (Unit)unit.backup.MemberwiseClone();
         unit.backup = null;
     }
     else
         Console.WriteLine("{0}: can't restore backup: not set", unit.name);
 }
예제 #11
0
 /// <summary>
 /// Apply suppression and damage to unit. Return the remaining 
 /// actual strength.
 /// If attacker is a bomber the suppression is counted as turn
 /// suppression.
 /// </summary>
 /// <param name="unit"></param>
 /// <param name="damage"></param>
 /// <param name="suppr"></param>
 /// <param name="attacker"></param>
 /// <returns></returns>
 public int ApplyDamage(int damage, int suppr, Unit attacker)
 {
     this.str -= damage;
     if (this.str < 0)
     {
         this.str = 0;
         return 0;
     }
     if (attacker != null && ((attacker.sel_prop.flags & UnitFlags.TURN_SUPPR) == UnitFlags.TURN_SUPPR))
     {
         this.turn_suppr += suppr;
         if (this.str - this.turn_suppr - this.suppr < 0)
         {
             this.turn_suppr = this.str - this.suppr;
             return 0;
         }
     }
     else
     {
         this.suppr += suppr;
         if (this.str - this.turn_suppr - this.suppr < 0)
         {
             this.suppr = this.str - this.turn_suppr;
             return 0;
         }
     }
     return unit_get_cur_str(this);
 }
예제 #12
0
 public static void unit_backup(Unit unit)
 {
     unit.backup = (Unit)unit.MemberwiseClone();
 }
예제 #13
0
 /*
 ====================================================================
 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;
 }
예제 #14
0
 /*
 ====================================================================
 Compute damage/supression the target takes when unit attacks
 the target. No properties will be changed. If 'real' is set
 the dices are rolled else it's a stochastical prediction.
 ====================================================================
 */
 public static void GetDamage(Unit aggressor, Unit unit, Unit target,
                       UNIT_ATTACK type,
                       bool real, bool rugged_def,
                       out int damage, out int suppr)
 {
     int atk_strength, max_roll, min_roll, die_mod;
     int atk_grade, def_grade, diff, result;
     float suppr_chance, kill_chance;
     /* use PG's formula to compute the attack/defense grade*/
     /* basic attack */
     atk_grade = Math.Abs(unit.sel_prop.atks[target.sel_prop.trgt_type]);
     #if DEBUG
     if (real) Console.WriteLine("{0} attacks:", unit.name);
     if (real) Console.WriteLine("  base:   {0}", atk_grade);
     if (real) Console.WriteLine("  exp:    +{0}", unit.exp_level);
     #endif
     /* experience */
     atk_grade += unit.exp_level;
     /* target on a river? */
     if ((target.sel_prop.flags & UnitFlags.FLYING) != UnitFlags.FLYING)
         if ((target.terrain.flags[Scenario.cur_weather] & Terrain_flags.RIVER) == Terrain_flags.RIVER)
         {
             atk_grade += 4;
     #if DEBUG
             if (real) Console.WriteLine("  river:  +4");
     #endif
         }
     /* counterattack of rugged defense unit? */
     if (type == UNIT_ATTACK.UNIT_PASSIVE_ATTACK && rugged_def)
     {
         atk_grade += 4;
     #if DEBUG
         if (real) Console.WriteLine("  rugdef: +4");
     #endif
     }
     #if DEBUG
     if (real) Console.WriteLine("---\n{0} defends:", target.name);
     #endif
     /* basic defense */
     if ((unit.sel_prop.flags & UnitFlags.FLYING) == UnitFlags.FLYING)
         def_grade = target.sel_prop.def_air;
     else
     {
         def_grade = target.sel_prop.def_grnd;
         /* apply close defense? */
         if ((unit.sel_prop.flags & UnitFlags.INFANTRY) == UnitFlags.INFANTRY)
             if ((target.sel_prop.flags & UnitFlags.INFANTRY) != UnitFlags.INFANTRY)
                 if ((target.sel_prop.flags & UnitFlags.FLYING) != UnitFlags.FLYING)
                     if ((target.sel_prop.flags & UnitFlags.SWIMMING) != UnitFlags.SWIMMING)
                     {
                         if (target == aggressor)
                             if ((unit.terrain.flags[Scenario.cur_weather] & Terrain_flags.INF_CLOSE_DEF) == Terrain_flags.INF_CLOSE_DEF)
                                 def_grade = target.sel_prop.def_cls;
                         if (unit == aggressor)
                             if ((target.terrain.flags[Scenario.cur_weather] & Terrain_flags.INF_CLOSE_DEF) == Terrain_flags.INF_CLOSE_DEF)
                                 def_grade = target.sel_prop.def_cls;
                     }
     }
     #if DEBUG
     if (real) Console.WriteLine("  base:   {0}", def_grade);
     if (real) Console.WriteLine("  exp:    +{0}", target.exp_level);
     #endif
     /* experience */
     def_grade += target.exp_level;
     /* attacker on a river or swamp? */
     if ((unit.sel_prop.flags & UnitFlags.FLYING) != UnitFlags.FLYING)
         if ((unit.sel_prop.flags & UnitFlags.SWIMMING) != UnitFlags.SWIMMING)
             if ((target.sel_prop.flags & UnitFlags.FLYING) != UnitFlags.FLYING)
             {
                 if ((unit.terrain.flags[Scenario.cur_weather] & Terrain_flags.SWAMP) == Terrain_flags.SWAMP)
                 {
                     def_grade += 4;
     #if DEBUG
                     if (real) Console.WriteLine("  swamp:  +4");
     #endif
                 }
                 else
                     if ((unit.terrain.flags[Scenario.cur_weather] & Terrain_flags.RIVER) == Terrain_flags.RIVER)
                     {
                         def_grade += 4;
     #if DEBUG
                         if (real) Console.WriteLine("  river:  +4");
     #endif
                     }
             }
     /* rugged defense? */
     if (type == UNIT_ATTACK.UNIT_ACTIVE_ATTACK && rugged_def)
     {
         def_grade += 4;
     #if DEBUG
         if (real) Console.WriteLine("  rugdef: +4");
     #endif
     }
     /* entrenchment */
     if ((unit.sel_prop.flags & UnitFlags.IGNORE_ENTR) == UnitFlags.IGNORE_ENTR)
         def_grade += 0;
     else
     {
         if ((unit.sel_prop.flags & UnitFlags.INFANTRY) == UnitFlags.INFANTRY)
             def_grade += target.entr / 2;
         else
             def_grade += target.entr;
     #if DEBUG
         if (real) Console.WriteLine("  entr:   +{0}",
         ((unit.sel_prop.flags & UnitFlags.INFANTRY) == UnitFlags.INFANTRY) ? target.entr / 2 : target.entr);
     #endif
     }
     /* naval vs ground unit */
     if ((unit.sel_prop.flags & UnitFlags.SWIMMING) != UnitFlags.SWIMMING)
         if ((unit.sel_prop.flags & UnitFlags.FLYING) != UnitFlags.FLYING)
             if ((target.sel_prop.flags & UnitFlags.SWIMMING) == UnitFlags.SWIMMING)
             {
                 def_grade += 8;
     #if DEBUG
                 if (real) Console.WriteLine("  naval: +8");
     #endif
             }
     /* bad weather? */
     if (unit.sel_prop.rng > 0)
         if ((Engine.terrain.weatherTypes[Scenario.cur_weather].flags & WEATHER_FLAGS.BAD_SIGHT) == WEATHER_FLAGS.BAD_SIGHT)
         {
             def_grade += 3;
     #if DEBUG
             if (real) Console.WriteLine("  sight: +3");
     #endif
         }
     /* initiating attack against artillery? */
     if (type == UNIT_ATTACK.UNIT_PASSIVE_ATTACK)
         if ((unit.sel_prop.flags & UnitFlags.ARTILLERY) == UnitFlags.ARTILLERY)
         {
             def_grade += 3;
     #if DEBUG
             if (real) Console.WriteLine("  def vs art: +3");
     #endif
         }
     /* infantry versus anti_tank? */
     if ((target.sel_prop.flags & UnitFlags.ARTILLERY) == UnitFlags.ARTILLERY)
         if ((unit.sel_prop.flags & UnitFlags.ANTI_TANK) == UnitFlags.ANTI_TANK)
         {
             def_grade += 2;
     #if DEBUG
             if (real) Console.WriteLine("  antitnk:+2");
     #endif
         }
     /* no fuel makes attacker less effective */
     if (unit.CheckFuelUsage() && unit.cur_fuel == 0)
     {
         def_grade += 4;
     #if DEBUG
         if (real) Console.WriteLine("  lowfuel:+4");
     #endif
     }
     /* attacker strength */
     atk_strength = unit_get_cur_str(unit);
     #if DEBUG
     if (real && atk_strength != unit_get_cur_str(unit))
         Console.WriteLine("---\n{0} with half strength", unit.name);
     #endif
     /*  PG's formula:
     get difference between attack and defense
     strike for each strength point with
       if ( diff <= 4 )
       D20 + diff
       else
       D20 + 4 + 0.4 * ( diff - 4 )
     suppr_fire flag set: 1-10 miss, 11-18 suppr, 19+ kill
     normal: 1-10 miss, 11-12 suppr, 13+ kill */
     diff = atk_grade - def_grade; if (diff < -7) diff = -7;
     damage = 0;
     suppr = 0;
     #if DEBUG
     if (real)
     {
         Console.WriteLine("---\n{0} x {1} -. {2} x {3}",
                 atk_strength, atk_grade, unit_get_cur_str(target), def_grade);
     }
     #endif
     /* get the chances for suppression and kills (computed here
        to use also for debug info */
     suppr_chance = kill_chance = 0;
     die_mod = (diff <= 4 ? diff : 4 + 2 * (diff - 4) / 5);
     min_roll = 1 + die_mod; max_roll = 20 + die_mod;
     /* get chances for suppression and kills */
     if ((unit.sel_prop.flags & UnitFlags.SUPPR_FIRE) == UnitFlags.SUPPR_FIRE)
     {
         int limit = (type == UNIT_ATTACK.UNIT_DEFENSIVE_ATTACK) ? 20 : 18;
         if (limit - min_roll >= 0)
             suppr_chance = 0.05f * (Math.Min(limit, max_roll) - Math.Max(11, min_roll) + 1);
         if (max_roll > limit)
             kill_chance = 0.05f * (max_roll - Math.Max(limit + 1, min_roll) + 1);
     }
     else
     {
         if (12 - min_roll >= 0)
             suppr_chance = 0.05f * (Math.Min(12, max_roll) - Math.Max(11, min_roll) + 1);
         if (max_roll > 12)
             kill_chance = 0.05f * (max_roll - Math.Max(13, min_roll) + 1);
     }
     if (suppr_chance < 0) suppr_chance = 0; if (kill_chance < 0) kill_chance = 0;
     if (real)
     {
     #if DEBUG
         Console.WriteLine("Roll: D20 + {0} (Kill: {1}, Suppr: {2})",
         diff <= 4 ? diff : 4 + 2 * (diff - 4) / 5,
         (int)(100 * kill_chance), (int)(100 * suppr_chance));
     #endif
         while (atk_strength-- > 0)
         {
             if (diff <= 4)
                 result = Misc.DICE(20) + diff;
             else
                 result = Misc.DICE(20) + 4 + 2 * (diff - 4) / 5;
             if ((unit.sel_prop.flags & UnitFlags.SUPPR_FIRE) == UnitFlags.SUPPR_FIRE)
             {
                 int limit = (type == UNIT_ATTACK.UNIT_DEFENSIVE_ATTACK) ? 20 : 18;
                 if (result >= 11 && result <= limit)
                     suppr++;
                 else
                     if (result >= limit + 1)
                         damage++;
             }
             else
             {
                 if (result >= 11 && result <= 12)
                     suppr++;
                 else
                     if (result >= 13)
                         damage++;
             }
         }
     #if DEBUG
         Console.WriteLine("Kills: {0}, Suppression: {1}", damage, suppr);
     #endif
     }
     else
     {
         suppr = (int)(suppr_chance * atk_strength);
         damage = (int)(kill_chance * atk_strength);
     }
 }
예제 #15
0
 public static void action_queue_embark_air(Unit unit, int x, int y)
 {
     Action action = CreateAction(EngineActionsTypes.ACTION_EMBARK_AIR);
     action.unit = unit;
     action.x = x; action.y = y;
     action_queue(action);
 }
예제 #16
0
 public static void action_queue_attack(Unit unit, Unit target)
 {
     Action action = CreateAction(EngineActionsTypes.ACTION_ATTACK);
     action.unit = unit;
     action.target = target;
     action_queue(action);
 }
예제 #17
0
 public static void action_queue_disband(Unit unit)
 {
     Action action = CreateAction(EngineActionsTypes.ACTION_DISBAND);
     action.unit = unit;
     action_queue(action);
 }
예제 #18
0
 public static void action_queue_debark_sea(Unit unit, int x, int y)
 {
     Action action = CreateAction(EngineActionsTypes.ACTION_DEBARK_SEA);
     action.unit = unit;
     action.x = x; action.y = y;
     action_queue(action);
 }
예제 #19
0
        public static void ai_group_delete_unit(AI_Group group, Unit unit)
        {
            /* remove unit */
            bool contained_unit = (group.units[group_units_pos] == unit);
            group.units.Remove(unit);
            if (contained_unit) return;

            /* update respective counter */
            if (((unit.prop.flags & UnitFlags.ARTILLERY) == UnitFlags.ARTILLERY) ||
                (unit.prop.flags & UnitFlags.AIR_DEFENSE) == UnitFlags.AIR_DEFENSE)
                /* nothing to be done */
                ;
            else
                if (unit.prop.unit_class == 9 || unit.prop.unit_class == 10)
                {
                    /* tactical and high level bomber */
                    group.bomber_count--;
                }
                else
                    if ((unit.prop.flags & UnitFlags.FLYING) == UnitFlags.FLYING)
                    {
                        /* airborne ground units are not in this section ... */
                        group.aircraft_count--;
                    }
                    else
                    {
                        /* everything else: ships and ground units */
                        group.ground_count--;
                    }
        }
예제 #20
0
 public static void action_queue_deploy(Unit unit, int x, int y)
 {
     Action action = CreateAction(EngineActionsTypes.ACTION_DEPLOY);
     action.unit = unit;
     action.x = x;
     action.y = y;
     action_queue(action);
 }
예제 #21
0
 static AI_Eval ai_create_eval(Unit unit, AI_Group group, int x, int y)
 {
     AI_Eval eval = new AI_Eval();
     eval.unit = unit; eval.group = group;
     eval.x = x; eval.y = y;
     return eval;
 }
예제 #22
0
 public static void action_queue_merge(Unit unit, Unit partner)
 {
     Action action = CreateAction(EngineActionsTypes.ACTION_MERGE);
     action.unit = unit;
     action.target = partner;
     action_queue(action);
 }
예제 #23
0
 static List<Unit> ai_gather_targets(Unit unit, int x, int y)
 {
     GatherCtx ctx = new GatherCtx();
     ctx.unit = unit;
     ctx.targets = new List<Unit>();
     AI.ai_eval_hexes(x, y, unit.sel_prop.rng + 1, new AI.eval_func_delegate(hex_add_targets), ctx);
     return ctx.targets;
 }
예제 #24
0
 public static void action_queue_move(Unit unit, int x, int y)
 {
     Action action = CreateAction(EngineActionsTypes.ACTION_MOVE);
     action.unit = unit;
     action.x = x;
     action.y = y;
     action_queue(action);
 }
예제 #25
0
 /*
 ====================================================================
 Choose and store the best tactical action of a unit (found by use of
 ai_evaluate_hex). If there is none AI_SUPPLY is stored.
 ====================================================================
 */
 static void ai_handle_unit(Unit unit, AI_Group group)
 {
     int x, y, nx, ny, i, action = 0;
     Queue<AI_Eval> list = new Queue<AI_Eval>();
     AI_Eval eval;
     Unit target = null;
     int score = -999999;
     /* get move mask */
     Engine.map.map_get_unit_move_mask(unit);
     x = unit.x;
     y = unit.y;
     target = null;
     /* evaluate all positions */
     list.Enqueue(ai_create_eval(unit, group, unit.x, unit.y));
     while (list.Count > 0)
     {
         eval = list.Dequeue();
         if (ai_evaluate_hex(ref eval))
         {
             /* movement evaluation */
             if (eval.mov_score > score)
             {
                 score = eval.mov_score;
                 target = null;
                 x = eval.x; y = eval.y;
             }
             /* movement + attack evaluation. ignore for attack_first
              * units which already fired */
             if ((unit.sel_prop.flags & UnitFlags.ATTACK_FIRST) != UnitFlags.ATTACK_FIRST)
                 if (eval.target != null && eval.atk_score > score)
                 {
                     score = eval.atk_score;
                     target = eval.target;
                     x = eval.x; y = eval.y;
                 }
         }
         /* store next hex tiles */
         for (i = 0; i < 6; i++)
             if (Misc.get_close_hex_pos(eval.x, eval.y, i, out nx, out ny))
                 if ((Engine.map.mask[nx, ny].in_range != 0 && !Engine.map.mask[nx, ny].blocked) || Engine.map.mask[nx, ny].sea_embark)
                 {
                     Engine.map.mask[nx, ny].in_range = 0;
                     Engine.map.mask[nx, ny].sea_embark = false;
                     list.Enqueue(ai_create_eval(unit, group, nx, ny));
                 }
     }
     list.Clear();
     /* check result and store appropiate action */
     if (unit.x != x || unit.y != y)
     {
         if (Engine.map.map_check_unit_debark(unit, x, y, UnitEmbarkTypes.EMBARK_SEA, false))
         {
             Action.action_queue_debark_sea(unit, x, y); action = 1;
     #if DEBUG
             Console.WriteLine("{0} debarks at {1},{2}", unit.name, x, y);
     #endif
         }
         else
         {
             Action.action_queue_move(unit, x, y); action = 1;
     #if DEBUG
             Console.WriteLine("{0} moves to {1},{2}", unit.name, x, y);
     #endif
         }
     }
     if (target != null)
     {
         Action.action_queue_attack(unit, target); action = 1;
     #if DEBUG
         Console.WriteLine("{0} attacks {1}", unit.name, target.name);
     #endif
     }
     if (action == 0)
     {
         Action.action_queue_supply(unit);
     #if DEBUG
         Console.WriteLine("{0} supplies: {1},{2}", unit.name, unit.cur_ammo, unit.cur_fuel);
     #endif
     }
 }
예제 #26
0
 public static void action_queue_supply(Unit unit)
 {
     Action action = CreateAction(EngineActionsTypes.ACTION_SUPPLY);
     action.unit = unit;
     action_queue(action);
 }
예제 #27
0
 static bool ai_unsafe__mount(Unit unit, int x, int y)
 {
     MountCtx ctx = new MountCtx(unit.player, x, y, false);
     AI.ai_eval_hexes(x, y, 6, new AI.eval_func_delegate(hex_is_safe), ctx);
     return ctx.unsafe_;
 }
예제 #28
0
 public static void action_queue_debark_air(Unit unit, int x, int y, int normal_landing)
 {
     Action action = CreateAction(EngineActionsTypes.ACTION_DEBARK_AIR);
     action.unit = unit;
     action.x = x; action.y = y;
     action.id = normal_landing;
     action_queue(action);
 }
예제 #29
0
 /*
 ====================================================================
 Check the supply level of a unit. (hex tiles with SUPPLY_GROUND
 have 100% supply.
 ====================================================================
 */
 public static void scen_adjust_unit_supply_level(Unit unit)
 {
     unit.supply_level = Engine.map.map_get_unit_supply_level(unit.x, unit.y, unit);
 }
예제 #30
0
 /*
 ====================================================================
 Duplicate the unit.
 ====================================================================
 */
 public Unit Duplicate()
 {
     Unit newUnit = new Unit();
     newUnit = (Unit)this.MemberwiseClone();
     newUnit.SetGenericName(Scenario.units.Count + 1, this.prop.name);
     if (this.sel_prop == this.prop)
         newUnit.sel_prop = newUnit.prop;
     else
         newUnit.sel_prop = newUnit.trsp_prop;
     newUnit.backup = new Unit();
     /* terrain can't be updated here */
     return newUnit;
 }