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;
                }
        }
        /*
        ====================================================================
        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);
        }
 /*
 ====================================================================
 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
     }
 }
 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;
 }
 /*
 ====================================================================
 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
     }
 }
        /*
        ====================================================================
        Handle next unit of a group to follow order. Stores all nescessary
        unit actions. If group is completely handled, it returns False.
        ====================================================================
        */
        public static bool ai_group_handle_next_unit(AI_Group group)
        {
            Unit unit = null;
            if (group_units_pos < group.units.Count)
                unit = group.units[group_units_pos++];

            if (unit == null)
            {
                if (group.state == GS.GS_MOVE)
                    return false;
                else
                {
                    group.state = GS.GS_MOVE;
                    group_units_pos = 0;
                    if (group_units_pos < group.units.Count)
                        unit = group.units[group_units_pos++];
                    else
                        return false;
                }
            }
            if (unit == null)
            {
                Console.WriteLine("ERROR: ai_group_handle_next_unit: null unit detected");
                return false;
            }
            /* Unit is dead? Can only be attacker that was killed by defender */
            if (unit.killed != 0)
            {
                Console.WriteLine("Removingkilled attacker %s(%d,%d) from group\n", unit.name, unit.x, unit.y);
                ai_group_delete_unit(group, unit);
                return ai_group_handle_next_unit(group);
            }
            if (group.state == GS.GS_ART_FIRE)
            {
                if ((unit.sel_prop.flags & UnitFlags.ATTACK_FIRST) == UnitFlags.ATTACK_FIRST)
                    ai_fire_artillery(unit, group); /* does not check optimal
                                                 movement but simply
                                                 fires */
            }
            else
                ai_handle_unit(unit, group); /* checks to the full tactical
                                          extend */
            return true;
        }
        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--;
                    }
        }
 public static void ai_group_delete(AI_Group ptr)
 {
     AI_Group group = ptr;
     if (group != null)
     {
         if (group.units != null)
             group.units.Clear();
     }
 }
 /*
 ====================================================================
 PUBLICS
 ====================================================================
 */
 /*
 ====================================================================
 CreateAction/Delete a group
 ====================================================================
 */
 public static AI_Group ai_group_create(int order, int x, int y)
 {
     AI_Group group = new AI_Group();
     group.state = GS.GS_ART_FIRE;
     group.order = order;
     group.x = x; group.y = y;
     group.units = new List<Unit>();
     return group;
 }
Example #10
0
        /*
        ====================================================================
        Queue next actions (if these actions were handled by the engine
        this function is called again and again until the end_turn
        action is received).
        ====================================================================
        */
        public static bool ai_run()
        {
            bool result = false;
            Unit[] partners = new Unit[Map.MAP_MERGE_UNIT_LIMIT];
            int partner_count;
            int i, j, x = 0, y = 0, dx, dy, dist;
            bool found;
            Unit unit = null;
            Unit best;
            switch (ai_status)
            {
                case AI_STATUS.AI_STATUS_DEPLOY:
                    /* deploy unit? */
                    if (Scenario.avail_units.Count > 0 && avail_unitsIterator.MoveNext() && (unit = avail_unitsIterator.Current) != null)
                    {

                        if (Engine.deploy_turn)
                        {
                            x = unit.x; y = unit.y;
                            //assert(x >= 0 && y >= 0);
                            Engine.map.map_remove_unit(unit);
                            found = true;
                        }
                        else
                        {
                            Engine.map.map_get_deploy_mask(Engine.cur_player, unit, false);
                            Engine.map.map_clear_mask(MAP_MASK.F_AUX);
                            for (i = 0; i < Engine.map.map_w; i++)
                                for (j = 0; j < Engine.map.map_h; j++)
                                    if (Engine.map.mask[i, j].deploy)
                                        if (ai_get_dist(unit, i, j, AI_FIND.AI_FIND_ENEMY_OBJ, out x, out y, out dist))
                                            Engine.map.mask[i, j].aux = dist + 1;
                            dist = 1000; found = false;
                            for (i = 0; i < Engine.map.map_w; i++)
                                for (j = 0; j < Engine.map.map_h; j++)
                                    if (Engine.map.mask[i, j].aux > 0 && Engine.map.mask[i, j].aux < dist)
                                    {
                                        dist = Engine.map.mask[i, j].aux;
                                        x = i; y = j;
                                        found = true; /* deploy close to enemy */
                                    }
                        }
                        if (found)
                        {
                            Action.action_queue_deploy(unit, x, y);
                            avail_unitsIterator = Scenario.avail_units.GetEnumerator();
                            ai_units.Add(unit);
            #if DEBUG
                            Console.WriteLine("{0} deployed to {1},{2}", unit.name, x, y);
            #endif
                            return false;
                        }
                    }
                    else
                    {
                        ai_status = Engine.deploy_turn ? AI_STATUS.AI_STATUS_END : AI_STATUS.AI_STATUS_MERGE;
                        ai_unitsIterator = ai_units.GetEnumerator();
            #if DEBUG
                        Console.WriteLine(Engine.deploy_turn ? "*** END TURN ***" : "*** MERGE ***");
            #endif
                    }
                    break;
                case AI_STATUS.AI_STATUS_SUPPLY:
                    /* get next unit */
                    ai_unitsIterator.MoveNext();
                    unit = ai_unitsIterator.Current;
                    if (unit == null)
                    {
                        ai_status = AI_STATUS.AI_STATUS_GROUP;
                        /* build a group with all units, -1,-1 as destination means it will
                           simply attack/defend the nearest target. later on this should
                           split up into several groups with different target and strategy */
                        ai_group = AI_Group.ai_group_create(Engine.cur_player.strat, -1, -1);
                        ai_unitsIterator = ai_units.GetEnumerator();
                        while (ai_unitsIterator.MoveNext() && (unit = ai_unitsIterator.Current) != null)
                            AI_Group.ai_group_add_unit(ai_group, unit);
            #if DEBUG
                        Console.WriteLine("*** MOVE & ATTACK ***");
            #endif
                    }
                    else
                    {
                        /* check if unit needs supply and remove
                           it from ai_units if so */
                        if ((unit.CheckLowFuel() || unit.CheckLowAmmo()))
                        {
                            if (unit.supply_level > 0)
                            {
                                Action.action_queue_supply(unit);
                                ai_units.Remove(unit);
            #if DEBUG
                                Console.WriteLine("{0} supplies", unit.name);
            #endif
                                break;
                            }
                            else
                            {
            #if DEBUG
                                Console.WriteLine("{0} searches depot", unit.name);
            #endif
                                if (ai_get_dist(unit, unit.x, unit.y, AI_FIND.AI_FIND_DEPOT,
                                                  out dx, out dy, out dist))
                                    if (ai_approximate(unit, dx, dy, out x, out y))
                                    {
                                        Action.action_queue_move(unit, x, y);
                                        ai_units.Remove(unit);
            #if DEBUG
                                        Console.WriteLine("{0} moves to {1},{2}", unit.name, x, y);
            #endif
                                        break;
                                    }
                            }
                        }
                    }
                    break;
                case AI_STATUS.AI_STATUS_MERGE:
                    if (ai_unitsIterator.MoveNext() && (unit = ai_unitsIterator.Current) != null)
                    {
                        Engine.map.map_get_merge_units(unit, out partners, out partner_count);
                        best = null; /* merge with the one that has the most strength points */
                        for (i = 0; i < partner_count; i++)
                            if (best == null)
                                best = partners[i];
                            else
                                if (best.str < partners[i].str)
                                    best = partners[i];
                        if (best != null)
                        {
            #if DEBUG
                            Console.WriteLine("{0} merges with {1}", unit.name, best.name);
            #endif
                            Action.action_queue_merge(unit, best);
                            /* both units are handled now */
                            ai_units.Remove(unit);
                            ai_units.Remove(best);
                        }
                    }
                    else
                    {
                        ai_status = AI_STATUS.AI_STATUS_SUPPLY;
                        ai_unitsIterator = ai_units.GetEnumerator();
            #if DEBUG
                        Console.WriteLine("*** SUPPLY ***");
            #endif
                    }
                    break;
                case AI_STATUS.AI_STATUS_GROUP:
                    if (!AI_Group.ai_group_handle_next_unit(ai_group))
                    {
                        AI_Group.ai_group_delete(ai_group);
                        ai_status = AI_STATUS.AI_STATUS_END;
            #if DEBUG
                        Console.WriteLine("*** END TURN ***");
            #endif
                    }
                    break;
                case AI_STATUS.AI_STATUS_END:
                    Action.action_queue_end_turn();
                    ai_status = AI_STATUS.AI_STATUS_FINALIZE;
                    result = true;
                    break;
            }
            return result;
        }