Results 1 to 14 of 14

Thread: Class Extensions - GamePlay

  1. #1
    Team Fusion Salmo's Avatar
    Join Date
    Nov 2011
    Posts
    2,332
    Post Thanks / Like
    Total Downloaded
    191.25 MB

    Class Extensions - GamePlay

    The GamePlay class is probably to most used class in the game, but it lacks important functionality so I extended it. This is a work-in-progress so please feel free to offer feedback or add additional functionality that I may have missed.

    How to use the Extensions class? - Just drop the entire extensions class file below (not inside) your mission script file. You will then have each of these goodies available as extension methods (no need for in-line functions or additional coding):

    GAMEPLAY CLASS EXTENSIONS

    GamePlay.gpAircraftTypes(birthplace); – Returns the AircraftTypes for the specified birthplace.
    GamePlay.gpAirGroups(); – Returns all game airgroups as an airgroup array. Optionally can specify an army.
    GamePlay.gpAirports(); – Returns all the game airports as an AiAirport array. Optionally can specify an army.
    GamePlay.gpAllAiAircraft(); – Returns all Ai controlled (no humans aboard) aircraft as an array. Optionally can specify an army.
    GamePlay.gpAllAircraft(); –Returns all the game aircraft (Ai & human) as an array. Optionally can specify an army.
    GamePlay.gpAllPlayers(); – Returns all human players in the specified army as a player array. Optionally can specify an army.
    GamePlay.gpAllPlayerAircraft(); – Returns all the aircraft with human players as an array. Optionally can specify an army.
    GamePlay.gpBirthPlaces(); – Returns all birthplaces/spawnbases in the game as a birthplace array. Optionally can specify an army.
    GamePlay.gpCreateBirthplace(point, aircrafttype, name, army, spawnheight, maxplanes, parachute, parked, country, heirarchy, regiment); – Creates a new birthplace/spawnbase at the specified point.
    GamePlay.gpCreateCityFire(x, y, duration); – Spawns a fire at specified map location if that location is in a city. Fires in a city have a chance to spread throughout the city.
    GamePlay.gpCreateEffect(effect, x, y, duration); – Spawns the specified game effect (smoke, fires, craters) at the specified map location for specified number of seconds.
    GamePlay.gpFlightFromTo(pointA, pointB); – Set the airgroup to fly from pointA to pointB.
    GamePlay.gpFlightLandAt(airgroup, airport); – Set the airgroup to land at the specified airport.
    GamePlay.gpFlightTakeoffFrom(airgroup, airport); – Set the airgroup to takeoff from the specified airport.
    GamePlay.gpGetArmyAtPoint(x, y); – Returns the army number at the specified map point (x, y).
    GamePlay.gpGetGroundActorPoints(type); - Returns an array of 3d point locations for all the specified groundactor types. Optionally can specify an army.
    GamePlay.gpGetActorAtPoint(); - Returns the AiActor located at the specified point. Returns null if no actor at the specified point.
    GamePlay.gpGetActors(); – Returns an array of all AiActors in the game. Optionally can specify an army.
    GamePlay.gpGetGroundActors(); – Returns an array of all AiGroundActors in the game. Optionally can specify an army.
    GamePlay.gpGetGroundStationaryAtPoint(point); – Returns the groundstationary object located at the specified point. Returns null if no stationary object at the point.
    GamePlay.gpGetNearestAirfield(point); – Returns the nearest airfield to the specified point.
    GamePlay.gpGetNearestBirthplace(point); – Returns the nearest birthplace to the specified point.
    GamePlay.gpGetNearestFriendlyAirfield(object); – Returns the nearest friendly airfield to the specified game object.
    GamePlay.gpGetNearestEnemyAirfield(object); – Returns the nearest enemy airfield to the specified game object.
    GamePlay.gpGetNearestAircraft(actor); – Returns the nearest aircraft to the specified actor.
    GamePlay.gpGetNearestEnemyAircraft(actor); – Returns the nearest enemy aircraft to the specified actor.
    GamePlay.gpGetNearestFriendlyAircraft(actor); – Returns the nearest friendly aircraft to the specified actor.
    GamePlay.gpGetNearestPlayer(actor); – Returns the nearest human player to the specified actor. Excludes other humans in the same AiACart (aircraft).
    GamePlay.gpGetNearestPlayer(person); – Returns the nearest human player to the specified person. Excludes other humans in the same AiACart (aircraft).
    GamePlay.gpGetNearestPlayer(player); – Returns the nearest human player to the specified player. Excludes other humans in the same AiACart (aircraft).
    GamePlay.gpGetObjectRotation(filename, x, y); – Returns the rotation angle of the object located at at the specified x, y coordinates.
    GamePlay.gpGetRandomAirfield(); – Returns a random game airport. Optionally can specify an army.
    GamePlay.gpGetRandomAirfield(centre, radius); – Returns a random game airport within a circle with centre point & radius.
    GamePlay.gpGetRandomBirthplace(); – Returns a random birthplace/spawnbase. Optionally can specify an army.
    GamePlay.gpGetRandomBirthplace(centre, radius); – Returns a random birthplace/spawnbase within a circle with centre point & radius.
    GamePlay.gpHasAircraftType(birthplace, aircrafttype); – Returns true if the specified birthplace planeset has the specified aircrafttype.
    GamePlay.gpHasAntiairDefence(airport); – Returns true if the specified airport has anti-aircraft defences.
    GamePlay.gpHasAntiairDefence(centre, radius); – Returns true if there are antiaircraft defences within circle with specified centre & radius.
    GamePlay.gpIsBirthPlace(airport); – Returns true if the specified airport is a birthplace/spawnbase.
    GamePlay.gpIsLandTypeCity(point); – Returns true if the specified point is in/over a city.
    GamePlay.gpIsLandTypeWater(point); – Returns true if the specified point is in/over water.
    GamePlay.gpMakeFlightPath(airgroup, ways); – Set the airgroup with the specified air waypoints. AirWaypoints can be an array or a list or air waypoints arrays.
    GamePlay.gpNumberOfPlayers(); – Returns the number of human players in the game. Optionally can specify an army.
    GamePlay.gpPlaneTypesInBirthplaceLabels(); – Adds available plane types to all spawn-base map labels. Optionally can specify single birthplace/spawnbase to rename.
    GamePlay.gpRegiments(); – Returns all the regiments in the game as a Regiment array. Optionally can specify an army.
    GamePlay.gpRemoveGroundStationarys( x, y, radius); – Removes ground stationary objects within a circle with centre x, y and radius from the game.
    GamePlay.gpRenameBirthplace(birthplace, name); – Renames the specified birthplace/spawnpoint to the specified name.
    GamePlay.gpSayEnemyPlanes(airgroup, army); – Voice announcement of specified airgroup to specified army.
    GamePlay.gpSayToArmy(army, msg); – Voice-over of specified voice message to specified army.
    GamePlay.gpSendChatMessageTo(army, msg); – Send specified chat message to all players in specified army.


    Code:
        public static class TF_GamePlay : IGamePlay
        {
            public static Point3d[] gpGetGroundActorPoints(this IGamePlay GP, AiGroundActorType type)
            {   // Purpose: Returns an array of 3d point locations for all the specified
                //          groundactor types.
                // Use: GamePlay.GetGroundActorPoints(type);
                List<Point3d> result = new List<Point3d>();
                List<AiGroundActor> groundactors = new List<AiGroundActor>(GP.gpGetGroundActors());
                for (int i = 0; i < groundactors.Count; i++)
                {
                    if (groundactors[i].Type() == type) result.Add(groundactors[i].Pos());
                }
                return result.ToArray();
            }
    
            public static Point3d[] gpGetGroundActorPoints(this IGamePlay GP, AiGroundActorType type, int army)
            {   // Purpose: Returns an array of 3d point locations for all the specified
                //          groundactor types in the specified army.
                // Use: GamePlay.GetGroundActorPoints(type, army);
                List<Point3d> result = new List<Point3d>();
                List<AiGroundActor> groundactors = new List<AiGroundActor>(GP.gpGetGroundActors());
                for (int i = 0; i < groundactors.Count; i++)
                {
                    if (groundactors[i].Type() == type && groundactors[i].Army() == army)
                        result.Add(groundactors[i].Pos());
                }
                return result.ToArray();
            }
    
            public static AiActor gpGetActorAtPoint(this IGamePlay GP, Point3d point)
            {   // Purpose: Returns the AiActor located at the specified point.
                //          Returns null if no actor at the point.
                // Use: GamePlay.gpGetActorAtPoint(point);
                AiActor result = null;
                List<AiActor> actors = new List<AiActor>(GP.gpGetActors());
                for (int i = 0; i < actors.Count; i++)
                {
                    if (actors[i].Pos().distance(ref point) < 3) return actors[i] as AiActor;
                }
                return result;
            }
    
            public static GroundStationary gpGetGroundStationaryAtPoint(this IGamePlay GP, Point2d point)
            {   // Purpose: Returns the groundstationary object located at the specified point.
                //          Returns null if no stationary object at the point.
                // Use: GamePlay.gpGetActorAtPoint(point);
                GroundStationary result = null;
                List<GroundStationary> stationaries = new List<GroundStationary>(GP.gpGroundStationarys());
                for (int i = 0; i < stationaries.Count; i++)
                {
                    Point2d P = new Point2d(stationaries[i].pos.x, stationaries[i].pos.y);
                    if (P.distance(ref point) < 3) 
                        return stationaries[i] as GroundStationary;
                }
                return result;
            }
    
            public static AiActor[] gpGetActors(this IGamePlay GP)
            {   // Purpose: Returns an array of all the AiActors in the game.
                // Use: GamePlay.gpGetAllAiActors();
                List<AiActor> result = new List<AiActor>();
                List<int> armies = new List<int>(GP.gpArmies());
                for (int i = 0; i < armies.Count; i++)
                {
                    // ground actors
                    List<AiGroundGroup> gg = new List<AiGroundGroup>(GP.gpGroundGroups(armies[i]));
                    for (int j = 0; j < gg.Count; j++)
                    {
                        List<AiActor> act = new List<AiActor>(gg[j].GetItems());
                        for (int k = 0; k < act.Count; k++)
                            result.Add(act[k] as AiActor);
                    }
                    // air actors
                    List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(armies[i]));
                    for (int j = 0; j < airgroups.Count; j++)
                    {
                        List<AiActor> act = new List<AiActor>(airgroups[j].GetItems());
                        for (int k = 0; k < act.Count; k++)
                            result.Add(act[k] as AiActor);
                    }
                }
                return result.ToArray();
            }
    
            public static AiActor[] gpGetActors(this IGamePlay GP, int army)
            {   // Purpose: Returns an array of all the AiActors in the 
                //          specified army in the game.
                // Use: GamePlay.gpGetAllAiActors();
                List<AiActor> result = new List<AiActor>();
                List<int> armies = new List<int>(GP.gpArmies());
                for (int i = 0; i < armies.Count; i++)
                {
                    // ground actors
                    List<AiGroundGroup> gg = new List<AiGroundGroup>(GP.gpGroundGroups(armies[i]));
                    for (int j = 0; j < gg.Count; j++)
                    {
                        List<AiActor> act = new List<AiActor>(gg[j].GetItems());
                        for (int k = 0; k < act.Count; k++)
                        {
                            if (act[k].Army() == army) result.Add(act[k] as AiActor);
                        }
                    }
                    // air actors
                    List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(armies[i]));
                    for (int j = 0; j < airgroups.Count; j++)
                    {
                        List<AiActor> act = new List<AiActor>(airgroups[j].GetItems());
                        for (int k = 0; k < act.Count; k++)
                        {
                            if (act[k].Army() == army) result.Add(act[k] as AiActor);
                        }
                    }
                }
                return result.ToArray();
            }
    
            public static AiGroundActor[] gpGetGroundActors(this IGamePlay GP)
            {   // Purpose: Returns an array of all ground actors in the game.
                // Use: GamePlay.gpGroundActors();
                List<AiGroundActor> result = new List<AiGroundActor>();
                List<int> armies = new List<int>(GP.gpArmies());
                for (int i = 0; i < armies.Count; i++)
                {
                    List<AiGroundGroup> gg = new List<AiGroundGroup>(GP.gpGroundGroups(armies[i]));
                    for (int j = 0; j < gg.Count; j++)
                    {
                        List<AiActor> act = new List<AiActor>(gg[j].GetItems());
                        for (int k = 0; k < act.Count; k++) 
                            result.Add(act[k] as AiGroundActor);
                    }
                }
                return result.ToArray();
            }
    
            public static AiGroundActor[] gpGetGroundActors(this IGamePlay GP, int army)
            {   // Purpose: Returns an array of all ground actors in the game.
                // Use: GamePlay.gpGroundActors();
                List<AiGroundActor> result = new List<AiGroundActor>();
                List<int> armies = new List<int>(GP.gpArmies());
                for (int i = 0; i < armies.Count; i++)
                {
                    List<AiGroundGroup> gg = new List<AiGroundGroup>(GP.gpGroundGroups(armies[i]));
                    for (int j = 0; j < gg.Count; j++)
                    {
                        List<AiActor> act = new List<AiActor>(gg[j].GetItems());
                        for (int k = 0; k < act.Count; k++)
                        {
                            if (act[k].Army() == army) result.Add(act[k] as AiGroundActor);
                        }
                    }
                }
                return result.ToArray();
            }
    
            public static double gpGetObjectRotation(this IGamePlay GP, string missionfile, double x, double y)
            {   // Purpose: Returns the rotation angle of the object located at
                //          at the specified x, y coordinates
                // Use: GamePlay.gpGetObjectRotation(missionfile, x, y);
                double rotation = -1;
                Point2d P = new Point2d(x, y);
                Point2d C = new Point2d();
                System.IO.StreamReader file = new System.IO.StreamReader(missionfile);  // open the mission file for processing
                string line;
                while ((line = file.ReadLine()) != null)    // read mission file one line at a time
                {
                    line = line.Trim();
                    if (line.StartsWith("Static"))
                    {
                        string[] part = line.Split(' ');
                        rotation = Convert.ToDouble(part[5]);
                        C = new Point2d(Convert.ToDouble(part[3]), Convert.ToDouble(part[4]));
                        if (P.distance(ref C) <= 1) return rotation;
                    }
                }
                file.Close();   
                return rotation;
            }
    
            public static Regiment[] gpRegiments(this IGamePlay GamePlay)
            {   // Purpose: Returns all the regiments in the game as a Regiment array.
                // Use: GamePlay.gpRegiments();
                List<Regiment> result = new List<Regiment>();
                List<AiBirthPlace> BPs = new List<AiBirthPlace>(GamePlay.gpBirthPlaces());
                for (int i = 0; i < BPs.Count; i++) result.Add(BPs[i].Regiment());
                return result.ToArray();
            }
    
            public static Regiment[] gpRegiments(this IGamePlay GamePlay, int army)
            {   // Purpose: Returns all the regiments for specified army as a Regiment array..
                // Use: GamePlay.gpRegiments(army);
                List<Regiment> result = new List<Regiment>();
                List<AiBirthPlace> BPs = new List<AiBirthPlace>(GamePlay.gpBirthPlaces(army));
                for (int i = 0; i < BPs.Count; i++) result.Add(BPs[i].Regiment());
                return result.ToArray();
            }
    
            public static string[] gpArmyNames(this IGamePlay GamePlay)
            {   // Purpose: Returns the names of all the armies in the game as a string array.
                // Use: GamePlay.gpArmyNames();
                List<string> result = new List<string>();
                List<int> Armies = new List<int>(GamePlay.gpArmies());
                for (int i = 0; i < Armies.Count; i++)
                {
                    result.Add(GamePlay.gpArmyName(1));
                }
                return result.ToArray();
            }
    
            public static AiBirthPlace[] gpBirthPlaces(this IGamePlay GamePlay)
            {   // Purpose: Returns all birthplaces/spawnbases in the game as a birthplace array.
                // Use: GamePlay.gpBirthPlaces();
                List<AiBirthPlace> result = new List<AiBirthPlace>();
                List<AiBirthPlace> birthplaces = new List<AiBirthPlace>(GamePlay.gpBirthPlaces());
                for (int j = 0; j < birthplaces.Count; j++)
                    result.Add(birthplaces[j]);
                return result.ToArray();
            }
    
            public static AiBirthPlace[] gpBirthPlaces(this IGamePlay GamePlay, int army)
            {   // Purpose: Returns all birthplaces/spawnbases for the specified army as a birthplace array.
                // Use: GamePlay.gpBirthPlaces(army);
                List<AiBirthPlace> result = new List<AiBirthPlace>();
                List<AiBirthPlace> birthplaces = new List<AiBirthPlace>(GamePlay.gpBirthPlaces());
                for (int j = 0; j < birthplaces.Count; j++)
                {
                    if (birthplaces[j].Army() == army) result.Add(birthplaces[j]);
                }
                return result.ToArray();
            }
    
            public static int gpNumberOfPlayers(this IGamePlay GamePlay)
            {   // Purpose: Returns the number of human players in the game.
                // Use: GamePlay.NumberOfPlayers(); 
                int result = 0;
                // on Dedi the server:
                if (GamePlay.gpPlayer() != null)
                {
                    result = 1;
                }
                else
                {   //rest of the crowd
                    if (GamePlay.gpRemotePlayers() != null || GamePlay.gpRemotePlayers().Length > 0)
                    {
                        return GamePlay.gpRemotePlayers().ToList().Count;
                    }
                }
                return result;
            }
    
            public static int gpNumberOfPlayers(this IGamePlay GamePlay, int army)
            {   // Purpose: Returns the number of human players in the game in the 
                //          specified army.
                // Use: GamePlay.NumberOfPlayers(army); 
                int result = 0;
                // on Dedi the server:
                if (GamePlay.gpPlayer() != null)
                {
                    if (GamePlay.gpPlayer().Army() == army) return 1;
                    result = 0;
                }
                else
                {   //rest of the crowd
                    if (GamePlay.gpRemotePlayers() != null || GamePlay.gpRemotePlayers().Length > 0)
                    {
                        List<Player> players = new List<Player>(GamePlay.gpRemotePlayers());
                        for (int i = 0; i < players.Count; i++)
                        {
                            if (players[i].Army() == army) result += 1;
                        }
                    }
                }
                return result;
            }
    
            public static void gpSendChatMessageTo(this IGamePlay GamePlay, int army, string msg, object[] parms)
            {   // Purpose: Send specified chat message to all players in specified army.
                // Use: GamePlay.sendChatMessageTo(); 
                List<Player> Players = new List<Player>();
                // on Dedi the server:
                if (GamePlay.gpPlayer() != null)
                {
                    if (GamePlay.gpPlayer().Army() == army || army == -1)
                        Players.Add(GamePlay.gpPlayer());
                } //rest of the crowd
                if (GamePlay.gpRemotePlayers() != null || GamePlay.gpRemotePlayers().Length > 0)
                {
                    foreach (Player p in GamePlay.gpRemotePlayers())
                    {
                        if (p.Army() == army || army == -1)
                            Players.Add(p);
                    }
                }
                if (Players != null && Players.Count > 0)
                    GamePlay.gpLogServer(Players.ToArray(), msg, parms);
            }
    
            public static void gpSayToArmy(this IGamePlay IG, int army, string msg)
            {   // Purpose: Specified voice message to specified army. 
                // Use: GamePlay.gpSayToArmy();
                try
                {
                    foreach (AiAirGroup g in IG.gpAirGroups(army))
                    {
                        bool SaidToGroup = false;
                        foreach (AiActor a in g.GetItems())
                        {
                            if (SaidToGroup == true) break; // speech already said to group
                            SaidToGroup = true;
                            (a as AiAircraft).SayToGroup((a as AiAircraft).AirGroup(), msg);
                        }
                    }
                }
                catch { }
            }
    
            public static void gpShowAircraftIcons(this IGamePlay IG)
            {   // Purpose: Show aircraft icons on the mini-map. 
                // Use: GamePlay.gpShowAircraftIcons();
                List<AiAirGroup> airgroups = new List<AiAirGroup>(IG.gpAllGameAirgroups());
                List<GPUserLabel> labels = new List<GPUserLabel>();
                for (int i = 0; i < airgroups.Count; i++)
                {
                    Point2d P = new Point2d(airgroups[i].Pos().x, airgroups[i].Pos().y);
                    string I = airgroups[i].ID().ToString();
                    string T = Convert.ToDouble(IG.gpTime()).ToString("HH:mm");
                    string N = airgroups[i].NOfAirc.ToString();
                    string A = airgroups[i].Altitude().ToString();
                    string H = airgroups[i].Heading().ToString();
                    string text = string.Concat("[", I,"] N:", N, " Alt:", A, " Hdg:", H);
    
                    GPUserLabel l;
                    l.pos = P;
                    l.Text = text;
                    l.time = Convert.ToDouble(IG.gpTime());
                    l.type = (int)GPUserIconType.Plane;
                    labels.Add(l);
                }
    
            List<Player> players = new List<Player>(IG.gpAllPlayers()); 
            for (int i = 0; i < players.Count; i++)
            {
                for (int j = 0; j < labels.Count; j++)
                {
                    GPUserLabel label = labels[j];
                    GPUserLabel lbl = IG.gpMakeUserLabel(label.pos, players[i], label.Text, label.time, 
                        (int)GPUserIconType.Plane);
                }
            }
        }
    
            public static void gpSayEnemyPlanes(this IGamePlay IG, AiAirGroup airgroup, int army)
            {   // Purpose: Voice announcement of specified airgroup to specified army. 
                // Use: GamePlay.gpSayEnemyPlanes(army, airgroup);
                try
                {
                    Random random = new Random(Guid.NewGuid().GetHashCode());
                    //Random random = new Random(Guid.NewGuid().GetHashCode());
                    // number of aircraft in groups of 5
                    IG.gpSayToArmy(army, (airgroup.NOfAirc).ToString()); // announce aircraft count
                    List<string> EnemyPlanesVoice = new List<string>
                    {
                        "Aicraft_a",
                        "Enemy_planes"
                    };
                    int i = random.Next(EnemyPlanesVoice.Count);
                    IG.gpSayToArmy(army, (EnemyPlanesVoice[i]).ToString()); // announce aircraft
                }
                catch { }
            }
    
            public static bool gpIsBirthPlace(this IGamePlay IG, AiAirport airport)
            {   // Purpose: Returns true if the specified airport is a birthplace/spawnbase.
                // Use: GamePlay.IsBirthPlace(airport);
                bool result = false;
                foreach (AiBirthPlace bp in IG.gpBirthPlaces())
                {
                    Point3d p = bp.Pos();
                    if (airport.Pos().distance(ref p) <= airport.CoverageR() ||
                        airport.Pos().distance(ref p) <= airport.FieldR())
                        result = true;
                }
                return result;
            }
    
            public static int gpGetArmyAtPoint(this IGamePlay GP, double x, double y)
            {   // Purpose: Returns the army number at the specified map point (x, y).
                // Use: GamePlay.GetArmyAtPoint(x, y);
                int result = -1;
                result = GP.gpFrontArmy(x, y);
                return result;
            }
    
            public static AiAirGroup[] gpAirGroups(this IGamePlay GP)
            {   // Purpose: Returns all game airgroups as an airgroup array.
                // Use: GamePlay.gpAllGameAirgroups();
                List<AiAirGroup> result = new List<AiAirGroup>();
                List<int> armies = new List<int>(GP.gpArmies());
                for (int i = 0; i < armies.Count; i++)
                {
                    List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(i));
                    result.AddRange(airgroups);
                }
                return result.ToArray();
            }
    
            public static bool gpIsLandTypeWater(this IGamePlay IG, Point3d point)
            {   // Purpose: Returns true if the specified point is in/over water.
                // Use: GamePlay.gpIsLandTypeWater();
                return (IG.gpLandType(point.x, point.y) == LandTypes.WATER);
            }
    
            public static bool gpIsLandTypeCity(this IGamePlay IG, Point3d point)
            {   // Purpose: Returns true if the specified point is in/over a city.
                // Use: GamePlay.gpIsLandTypeCity();
                return (IG.gpLandType(point.x, point.y) == LandTypes.CITY);
            }
    
            public static Player[] gpAllPlayers(this IGamePlay GP)
            {   // Purpose: Returns all humans players as a player array.
                // Use: GamePlay.gpAllPlayers();
                return GP.gpRemotePlayers();
            }
    
            public static Player[] gpAllPlayers(this IGamePlay GP, int army)
            {   // Purpose: Returns all human players in the specified army as a player array.
                // Use: GamePlay.gpAllPlayers();
                List<Player> result = new List<Player>();
                List<Player> players = new List<Player>(GP.gpRemotePlayers());
                for (int i = 0; i < players.Count; i++)
                {
                    if (players[i].Army() == army) result.Add(players[i]);
                }
                return result.ToArray();
            }
    
            public static AiAircraft[] gpAllAiAircraft(this IGamePlay GP)
            {   // Purpose: Returns all Ai controlled (no humans aboard) aircraft as an array.
                // Use: GamePlay.gpAllAiAiAircraft();
                List<AiAircraft> result = new List<AiAircraft>();
                List<int> armies = new List<int>(GP.gpArmies());
                for (int i = 0; i < armies.Count; i++)
                {
                    List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(i));
                    for (int j = 0; j < airgroups.Count; j++)
                    {
                        List<AiActor> actors = new List<AiActor>(airgroups[j].GetItems());
                        for (int k = 0; k < actors.Count; k++)
                        {
                            AiAircraft ac = actors[k] as AiAircraft;
                            if (ac != null && ac.IsAiControlled()) result.Add(ac);
                        }
                    }
                }
                return result.ToArray();
            }
    
            public static AiAircraft[] gpAllAiAircraft(this IGamePlay GP, int army)
            {   // Purpose: Returns all Ai controlled (no humans aboard) aircraft as an array.
                // Use: GamePlay.gpAllAiAiAircraft();
                List<AiAircraft> result = new List<AiAircraft>();
                List<int> armies = new List<int>(GP.gpArmies());
                for (int i = 0; i < armies.Count; i++)
                {
                    List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(i));
                    for (int j = 0; j < airgroups.Count; j++)
                    {
                        List<AiActor> actors = new List<AiActor>(airgroups[j].GetItems());
                        for (int k = 0; k < actors.Count; k++)
                        {
                            AiAircraft ac = actors[k] as AiAircraft;
                            if (ac != null && ac.IsAiControlled() && ac.Army() == army)
                                result.Add(ac);
                        }
                    }
                }
                return result.ToArray();
            }
    
            public static AiAircraft[] gpAllPlayerAircraft(this IGamePlay GP)
            {   // Purpose: Returns all the aircraft with human players as an array.
                // Use: GamePlay.gpAllPlayerAircraft();
                List<AiAircraft> result = new List<AiAircraft>();
                List<int> armies = new List<int>(GP.gpArmies());
                for (int i = 0; i < armies.Count; i++)
                {
                    List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(i));
                    for (int j = 0; j < airgroups.Count; j++)
                    {
                        List<AiActor> actors = new List<AiActor>(airgroups[j].GetItems());
                        for (int k = 0; k < actors.Count; k++)
                        {
                            AiAircraft ac = actors[k] as AiAircraft;
                            if (ac != null && !ac.IsAiControlled()) result.Add(ac);
                        }
                    }
                }
                return result.ToArray();
            }
    
            public static AiAircraft[] gpAllPlayerAircraft(this IGamePlay GP, int army)
            {   // Purpose: Returns all the aircraft with human players as an array.
                // Use: GamePlay.gpAllPlayerAircraft();
                List<AiAircraft> result = new List<AiAircraft>();
                List<int> armies = new List<int>(GP.gpArmies());
                for (int i = 0; i < armies.Count; i++)
                {
                    List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(i));
                    for (int j = 0; j < airgroups.Count; j++)
                    {
                        List<AiActor> actors = new List<AiActor>(airgroups[j].GetItems());
                        for (int k = 0; k < actors.Count; k++)
                        {
                            AiAircraft ac = actors[k] as AiAircraft;
                            if (ac != null && !ac.IsAiControlled() && ac.Army() == army) 
                                result.Add(ac);
                        }
                    }
                }
                return result.ToArray();
            }
    
            public static AiAircraft[] gpAllAircraft(this IGamePlay GP)
            {   // Purpose: Returns all the game aircraft as an array.
                // Use: GamePlay.gpAllGameAircraft();
                List<AiAircraft> result = new List<AiAircraft>();
                List<int> armies = new List<int>(GP.gpArmies());
                for (int i = 0; i < armies.Count; i++)
                {
                    List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(i));
                    for (int j = 0; j < airgroups.Count; j++)
                    {
                        List<AiActor> actors = new List<AiActor>(airgroups[j].GetItems());
                        for (int k = 0; k < actors.Count; k++)
                        {
                            AiAircraft ac = actors[k] as AiAircraft;
                            if (ac != null) result.Add(ac);
                        }
                    }
                }
                return result.ToArray();
            }
    
            public static AiAircraft[] gpAllAircraft(this IGamePlay GP, int army)
            {   // Purpose: Returns all the game aircraft (Ai & human) as an array.
                // Use: GamePlay.gpAllGameAircraft();
                List<AiAircraft> result = new List<AiAircraft>();
                List<int> armies = new List<int>(GP.gpArmies());
                for (int i = 0; i < armies.Count; i++)
                {
                    List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(i));
                    for (int j = 0; j < airgroups.Count; j++)
                    {
                        List<AiActor> actors = new List<AiActor>(airgroups[j].GetItems());
                        for (int k = 0; k < actors.Count; k++)
                        {
                            AiAircraft ac = actors[k] as AiAircraft;
                            if (ac != null && ac.Army() == army) result.Add(ac);
                        }
                    }
                }
                return result.ToArray();
            }
    
            public static void gpSayEnemyGroupAltitude(this IGamePlay GP, AiAirGroup airgroup, string msg)
            {   // Purpose: Stated voice announcement to specified airgroup. 
                // Use: GamePlay.gpSayEnemyGroupAltitude(airgroup, message);
                double alt = airgroup.Altitude();
                List<int> armies = new List<int>(GP.gpArmies());
                for (int i = 0; i < armies.Count; i++)
                {
                    foreach (AiAirGroup g in GP.gpAirGroups(i))
                    {
                        bool SaidToGroup = false;
                        foreach (AiActor a in g.GetItems())
                        {
                            if (SaidToGroup == true) break; // speech already said to group
                            SaidToGroup = true;
                            (a as AiAircraft).SayToGroup((a as AiAircraft).AirGroup(), msg);
                        }
                    }
                }
            }
    
            public static AiAirport[] gpAirports(this IGamePlay GP, int army)
            {   // Purpose: Returns all the game airports as an AiAirport array. 
                //          Optionally can specify an army.
                //          Use: GamePlay.gpAirports(army)
                List<AiAirport> result = new List<AiAirport>();
                List<AiAirport> BP = new List<AiAirport>(GP.gpAirports());
                for (int i = 0; i < BP.Count; i++)
                {
                    if (BP[i].Army() == army) result.Add(BP[i]);
                }
                return result.ToArray();
            }
    
            public static AiAirport gpGetRandomAirfield(this IGamePlay GP)
            {   // Purpose: Returns a random airport. 
                //          Use: GamePlay.gpGetRandomAirfield()
                Random random = new Random(Guid.NewGuid().GetHashCode());
                List<AiAirport> AP = new List<AiAirport>(GP.gpAirports());
                return AP[random.Next(AP.Count)];
            }
    
            public static AiAirport gpGetRandomAirfield(this IGamePlay GP, int army)
            {
                Random random = new Random(Guid.NewGuid().GetHashCode());
                List<AiAirport> BP = new List<AiAirport>(GP.gpAirports(army));
                AiAirport B = BP[random.Next(BP.Count)];
                int n = 0;
                while (B.Army() != army)
                {
                    B = BP[random.Next(BP.Count)];
                    n += 1;
                }
                if (n >= 1000) throw new Exception(string.Format(
                    "gpRandomAirfield: No airport found for army {0}",
                    new object[] { army.ToString() }));
                {
    
                };
                return B;
            }
    
            public static AiAirport gpGetRandomAirfield(this IGamePlay GP, Point2d centre, double radius)
            {
                Random random = new Random(Guid.NewGuid().GetHashCode());
                List<AiAirport> BP = new List<AiAirport>(GP.gpAirports());
                AiAirport B = BP[random.Next(BP.Count)];
                Point2d P = new Point2d(B.Pos().x, B.Pos().y);
                Point2d C = new Point2d(centre.x, centre.y);
                int n = 0;
                while ((C.distance(ref P) > radius) || n < 1000)
                {
                    B = BP[random.Next(BP.Count)];
                    n += 1;
                }
                if (n >= 1000) throw new Exception(string.Format(
                    "gpRandomAirfield: No airfield found within {0} of point {1}-{2}",
                    new object[] { radius.ToString(), centre.x.ToString(), centre.y.ToString() }));
                {
    
                };
                return B;
            }
    
            public static AiAirport gpGetRandomAirfield(this IGamePlay GP, Point3d centre, double radius)
            {
                return GP.gpGetRandomAirfield(new Point2d(centre.x, centre.y), radius);
            }
    
            public static AiBirthPlace gpGetRandomBirthplace(this IGamePlay GP)
            {
                Random random = new Random(Guid.NewGuid().GetHashCode());
                List<AiBirthPlace> BP = new List<AiBirthPlace>(GP.gpBirthPlaces());
                return BP[random.Next(BP.Count)];
            }
    
            public static AiBirthPlace gpGetRandomBirthplace(this IGamePlay GP, int army)
            {
                Random random = new Random(Guid.NewGuid().GetHashCode());
                List<AiBirthPlace> BP = new List<AiBirthPlace>(GP.gpBirthPlaces());
                AiBirthPlace B = BP[random.Next(BP.Count)];
                int n = 0;
                while (B.Army() != army)
                {
                    B = BP[random.Next(BP.Count)];
                    n += 1;
                }
                if (n >= 1000) throw new Exception(string.Format(
                    "gpRandomBirthplace: No birthplace found for army {0}",
                    new object[] {army.ToString()}));
                {
                   
                };
                return B;
            }
    
            public static AiBirthPlace gpGetRandomBirthplace(this IGamePlay GP, Point2d centre, double radius)
            {
                Random random = new Random(Guid.NewGuid().GetHashCode());
                List<AiBirthPlace> BP = new List<AiBirthPlace>(GP.gpBirthPlaces());
                AiBirthPlace B = BP[random.Next(BP.Count)];
                Point2d P = new Point2d(B.Pos().x, B.Pos().y);
                int n = 0;
                while ((centre.distance(ref P) > radius) || n < 1000)
                {
                    B = BP[random.Next(BP.Count)];
                    n += 1;
                }
                if (n >= 1000) throw new Exception(string.Format(
                    "gpRandomBirthplace: No birthplace found within {0} of point {1}-{2}",
                    new object[] { radius.ToString(), centre.x.ToString(), centre.y.ToString() }));
                {
                   
                };
                return B;
            }
    
            public static AiBirthPlace gpGetNearestBirthplace(this IGamePlay GP, Point3d point)
            {   // Purpose: Returns the nearest birthplace to the specified point. 
                // Use: GamePlay.gpNearestBirthplace(location);
                AiBirthPlace NearestBirthplace = null;
                AiBirthPlace[] BirthPlaces = GP.gpBirthPlaces();
    
                if (BirthPlaces != null)
                {
                    foreach (AiBirthPlace bp in BirthPlaces)
                    {
                        if (NearestBirthplace != null)
                        {
                            if (bp.Pos().distance(ref point) < NearestBirthplace.Pos().distance(ref point))
                                NearestBirthplace = bp;
                        }
                        else
                        {
                            NearestBirthplace = bp;
                        }
                    }
                }
                return NearestBirthplace;
            }
    
            public static AiAirport gpGetNearestAirfield(this IGamePlay GP, Point3d point)
            {   // Purpose: Returns the nearest airfield to the specified actor. 
                // Use: GamePlay.gpNearestAirfield(actor);
                AiAirport NearestAirfield = null;
                List<AiAirport> airports = new List<AiAirport>(GP.gpAirports());
                for (int i = 0; i < airports.Count; i++)
                {
                    AiAirport ThisAirport = airports[i];
                    if (ThisAirport.Pos().distance(ref point) < NearestAirfield.Pos().distance(ref point))
                        NearestAirfield = ThisAirport;
                }
                return NearestAirfield;
            }
    
            public static AiAirport gpGetNearestAirfield(this IGamePlay GP, Point2d point)
            {   // Purpose: Returns the nearest airfield to the specified actor. 
                // Use: GamePlay.gpNearestAirfield(point);
                return GP.gpGetNearestAirfield(new Point3d(point.x, point.y, 0));
            }
    
            public static AiAirport gpGetNearestFriendlyAirfield(this IGamePlay GP, object obj)
            {   // Purpose: Returns the nearest friendly airfield to the specified game object. 
                // Use: GamePlay.gpNearestFriendlyAirfield(actor);
                AiAirport NearestAirfield = null;
                Point3d P = new Point3d();
                int TheArmy = -1;
                if (obj is AiActor)
                {
                    TheArmy = (obj as AiActor).Army();
                    P = (obj as AiActor).Pos();
                }
                else if (obj is AiAircraft)
                {
                    TheArmy = (obj as AiAircraft).Army();
                    P = (obj as AiAircraft).Pos();
                }
                else if (obj is AiGroundActor)
                {
                    TheArmy = (obj as AiGroundActor).Army();
                    P = (obj as AiGroundActor).Pos();
                }
                else if (obj is GroundStationary)
                {
                    TheArmy = (obj as GroundStationary).Army();
                    P = (obj as GroundStationary).pos;
                }
                else { };
    
                List<AiAirport> airports = new List<AiAirport>(GP.gpAirports(TheArmy));
                for (int i = 0; i < airports.Count; i++)
                {
                    AiAirport ThisAirport = airports[i];
                    if (ThisAirport.Pos().distance(ref P) < NearestAirfield.Pos().distance(ref P))
                        NearestAirfield = ThisAirport;
                }
                return NearestAirfield;
            }
    
            public static AiAirport gpGetNearestEnemyAirfield(this IGamePlay GP, object obj)
            {   // Purpose: Returns the nearest enemy airfield to the specified actor. 
                // Use: GamePlay.gpNearestEnemyAirfield(actor);
                AiAirport NearestAirfield = null;
                Point3d P = new Point3d();
                int TheArmy = -1;
                if (obj is AiActor)
                {
                    TheArmy = (obj as AiActor).Army();
                    P = (obj as AiActor).Pos();
                }
                else if (obj is AiAircraft)
                {
                    TheArmy = (obj as AiAircraft).Army();
                    P = (obj as AiAircraft).Pos();
                }
                else if (obj is AiGroundActor)
                {
                    TheArmy = (obj as AiGroundActor).Army();
                    P = (obj as AiGroundActor).Pos();
                }
                else if (obj is GroundStationary)
                {
                    TheArmy = (obj as GroundStationary).Army();
                    P = (obj as GroundStationary).pos;
                }
                else { };
    
                List<AiAirport> airports = new List<AiAirport>(GP.gpAirports());
                for (int i = 0; i < airports.Count; i++)
                {
                    AiAirport ThisAirport = airports[i];
                    if (ThisAirport.Army() != TheArmy)
                    {
                        if (ThisAirport.Pos().distance(ref P) < NearestAirfield.Pos().distance(ref P))
                            NearestAirfield = ThisAirport;
                    }
                }
                return NearestAirfield;
            }
    
            public static Player gpGetNearestPlayer(this IGamePlay GP, AiActor actor)
            {   // Purpose: Returns the nearest human player to the specified actor.
                //          Excludes other humans in the same AiCart (aircraft)
                // Use: GamePlay.gpNearestPlayer(actor);
                Player NearestPlayer = null;
                Point3d P = actor.Pos();
                AiCart ActorCart = (actor as AiPerson).Cart();
                List<Player> players = new List<Player>(GP.gpRemotePlayers());
                for (int i = 0; i < players.Count; i++)
                {
                    Player ThisPlayer = players[i];
                    AiCart ThisPlayerCart = (ThisPlayer as AiPerson).Cart();
                    if (ThisPlayer.Place().Pos().distance(ref P) < NearestPlayer.Place().Pos().distance(ref P)
                        && ThisPlayer.Place() != actor
                        && ThisPlayer != null
                        && ActorCart != ThisPlayerCart)
                        NearestPlayer = ThisPlayer;
                }
                return NearestPlayer;
            }
    
            public static Player gpGetNearestPlayer(this IGamePlay GP, AiPerson person)
            {   // Purpose: Returns the nearest human player to the specified person. 
                // Use: GamePlay.gpNearestPlayer(person);
                return GP.gpGetNearestPlayer(person.Player().Place());
            }
    
            public static Player gpGetNearestPlayer(this IGamePlay GP, Player player)
            {   // Purpose: Returns the nearest human player to the specified player. 
                // Use: GamePlay.gpNearestPlayer(player);
                return GP.gpGetNearestPlayer(player.Place());
            }
    
            public static AiAircraft gpGetNearestAircraft(this IGamePlay GP, AiActor actor)
            {   // Purpose: Returns the nearest aircraft to the specified point. 
                // Use: GamePlay.gpNearestAircraft(point);
                AiAircraft NearestAircraft = null;
                Point3d P = actor.Pos();
                List<AiAircraft> aircrafts = new List<AiAircraft>(GP.gpAllAiAircraft());
                for (int i = 0; i < aircrafts.Count; i++)
                {
                    AiAircraft aircraft = aircrafts[i] as AiAircraft;
                    if (aircraft != null)
                    {
                        if (NearestAircraft == null) NearestAircraft = aircraft;
                        if (aircraft.Pos().distance(ref P) < NearestAircraft.Pos().distance(ref P))
                            NearestAircraft = aircraft;
                    }
                }
                return NearestAircraft;
            }
    
            public static AiAircraft gpGetNearestFriendlyAircraft(this IGamePlay GP, AiActor actor)
            {   // Purpose: Returns the nearest frinedly aircraft to the specified actor. 
                // Use: GamePlay.gpNearestFriendlyAircraft(actor);
                AiAircraft NearestAircraft = null;
                Point3d P = actor.Pos();
                List<int> armies = new List<int>(GP.gpArmies());
                for (int i = 0; i < armies.Count; i++)
                {
                    List<AiAirGroup> ag = new List<AiAirGroup>(GP.gpAirGroups(armies[i]));
                    for (int j = 0; j < ag.Count; j++)
                    {
                        List<AiActor> actors = new List<AiActor>(ag[j].GetItems());
                        for (int k = 0; k < actors.Count; k++)
                        {
                            AiAircraft aircraft = actors[k] as AiAircraft;
                            if (aircraft != null && aircraft.Army() == actor.Army())
                            {
                                if (aircraft.Pos().distance(ref P) < NearestAircraft.Pos().distance(ref P))
                                    NearestAircraft = aircraft;
                            }
                        }
                    }
                }
                return NearestAircraft;
            }
    
            public static AiAircraft gpGetNearestEnemyAircraft(this IGamePlay GP, AiActor actor)
            {   // Purpose: Returns the nearest enemy aircraft to the specified actor. 
                // Use: GamePlay.gpNearestEnemyAircraft(actor);
                AiAircraft NearestAircraft = null;
                Point3d P = actor.Pos();
                List<int> armies = new List<int>(GP.gpArmies());
                for (int i = 0; i < armies.Count; i++)
                {
                    List<AiAirGroup> ag = new List<AiAirGroup>(GP.gpAirGroups(armies[i]));
                    for (int j = 0; j < ag.Count; j++)
                    {
                        List<AiActor> actors = new List<AiActor>(ag[j].GetItems());
                        for (int k = 0; k < actors.Count; k++)
                        {
                            AiAircraft aircraft = actors[k] as AiAircraft;
                            if (aircraft != null && aircraft.Army() != actor.Army())
                            {
                                if (aircraft.Pos().distance(ref P) < NearestAircraft.Pos().distance(ref P))
                                    NearestAircraft = aircraft;
                            }
                        }
                    }
                }
                return NearestAircraft;
            }
    
            public static void gpPlaneTypesInBirthplaceLabels(this IGamePlay GP)
            {   // Purpose: Adds available plane types in the all spawn-base map labels. 
                // Use: GamePlay.PutPlaneTypesInBirthplaceLabels();
                gpPlaneTypesInBirthplaceLabels(GP, GP.gpBirthPlaces());
            }
    
            public static void gpPlaneTypesInBirthplaceLabels(this IGamePlay GP, AiBirthPlace birthplace)
            {   // Purpose: Puts plane types in the specified spawn-base map labels. 
                // Use: GamePlay.gpPutPlaneTypesInBirthplaceLabels(birthplaces);
                AiBirthPlace[] bps = null;
                bps[0] = birthplace;
                gpPlaneTypesInBirthplaceLabels(GP, bps);
            }
    
            private static void gpPlaneTypesInBirthplaceLabels(this IGamePlay GP, AiBirthPlace[] birthplaces)
            {   // Purpose: Puts plane types in the specified spawn-bases map labels. 
                // Use: GamePlay.gpPutPlaneTypesInBirthplaceLabels(birthplaces);
                foreach (AiBirthPlace bp in birthplaces)
                {
                    string OldMapLabel = bp.Name();
                    string[] Planeset = bp.GetAircraftTypes();
                    string[] words = bp.Name().Trim().Split('(');
                    string MapLabel = words[0] + " (";
                    foreach (string aircraftname in Planeset)
                    {
                        if (aircraftname.Contains("Spit")) MapLabel = MapLabel + "Spitfire, ";
                        if (aircraftname.Contains("Hurr")) MapLabel = MapLabel + "Hurricane, ";
                        if (aircraftname.Contains("Blenheim")) MapLabel = MapLabel + "Blenheim, ";
                        if (aircraftname.Contains("DH82A  ")) MapLabel = MapLabel + "Tiger Moth, ";
                        if (aircraftname.Contains("88")) MapLabel = MapLabel + "Ju88, ";
                        if (aircraftname.Contains("87")) MapLabel = MapLabel + "Ju87, ";
                        if (aircraftname.Contains("109")) MapLabel = MapLabel + "Bf109, ";
                        if (aircraftname.Contains("BR")) MapLabel = MapLabel + "BR20, ";
                        if (aircraftname.Contains("G50")) MapLabel = MapLabel + "G50, ";
                        if (aircraftname.Contains("111")) MapLabel = MapLabel + "He111, ";
                        if (aircraftname.Contains("110")) MapLabel = MapLabel + "Bf110, ";
                        if (aircraftname.Contains("CR")) MapLabel = MapLabel + "CR42, ";
                    }
                    MapLabel = MapLabel.Remove(MapLabel.LastIndexOf(',')) + ")"; ; // get last occurance of , character
                    GP.gpRenameBirthplace(bp, MapLabel);
                }
            }
    
            public static AircraftType[] gpAircraftTypes(this IGamePlay GP, AiBirthPlace birthplace)
            {   // Purpose: Returns the AircraftTypes for the specified birthplace. 
                // Use: GamePlay.gpAircraftTypes(birthplace);
                List<string> types = new List<string>(birthplace.GetAircraftTypes());
                HashSet<AircraftType> result = new HashSet<AircraftType>();
                for (int i = 0; i < types.Count; i++)
                {
                    string aircraftname = types[i];
                    if (aircraftname.Contains("Spit")) result.Add(AircraftType.Fighter);
                    if (aircraftname.Contains("Hurr")) result.Add(AircraftType.Fighter);
                    if (aircraftname.Contains("Blenheim")) result.Add(AircraftType.Bomber);
                    if (aircraftname.Contains("DH82A  ")) result.Add(AircraftType.Fighter);
                    if (aircraftname.Contains("88")) result.Add(AircraftType.DiveBomber);
                    if (aircraftname.Contains("87")) result.Add(AircraftType.DiveBomber);
                    if (aircraftname.Contains("109")) result.Add(AircraftType.Fighter);
                    if (aircraftname.Contains("BR")) result.Add(AircraftType.Bomber);
                    if (aircraftname.Contains("G50")) result.Add(AircraftType.Fighter);
                    if (aircraftname.Contains("111")) result.Add(AircraftType.Bomber);
                    if (aircraftname.Contains("110")) result.Add(AircraftType.HeavyFighter);
                    if (aircraftname.Contains("CR")) result.Add(AircraftType.Fighter);
                }
                return result.ToArray();
            }
    
            public static bool gpHasAircraftType(this IGamePlay GP, AiBirthPlace birthplace, AircraftType type)
            {   // Purpose: Returns true if the specified birthplace planeset has the specified aircrafttype. 
                // Use: GamePlay.gpAircraftTypes(birthplace, aircraftype);
                return GP.gpAircraftTypes(birthplace).Contains(type);
            }
    
    
            public static bool gpHasAntiairDefence(this IGamePlay GP, AiAirport airport)
            {   // Purpose: Returns true if the specified airport has antiaircraft defences. 
                // Use: GamePlay.gpHasAntiairDefence(birthplace);
                List<GroundStationary> gs = new List<GroundStationary>(GP.gpGroundStationarys());
                for (int j = 0; j < gs.Count; j++)
                {
                    Point3d p = airport.Pos();
                    if (gs[j].pos.distance(ref p) < airport.CoverageR() &&
                        (gs[j].Type == AiGroundActorType.AAGun ||
                        gs[j].Type == AiGroundActorType.Balloon))
                        return true;
                }
                return false;
            }
    
            public static bool gpHasAntiairDefence(this IGamePlay GP, Point2d centre, double radius)
            {   // Purpose: Returns true if there are antiaircraft defences with circle with
                //          specified centre & radius. 
                // Use: GamePlay.gpHasAntiairDefence(birthplace);
                List<GroundStationary> gs = new List<GroundStationary>(GP.gpGroundStationarys());
                for (int j = 0; j < gs.Count; j++)
                {
                    Point2d P = new Point2d(gs[j].pos.x, gs[j].pos.y);
                    if (P.distance(ref centre) < radius &&
                        (gs[j].Type == AiGroundActorType.AAGun ||
                        gs[j].Type == AiGroundActorType.Balloon))
                        return true;
                }
                return false;
            }
    
            public static void gpRenameBirthplace(this IGamePlay GP, AiBirthPlace birthplace, string name)
            {   // Purpose: Renames the specified birthplace/spawnpoint. 
                // Use: GamePlay.gpRenameSpawnbase(birthplace, name);
                try
                {
                    ISectionFile f = GP.gpCreateSectionFile();
                    string line = '"' + name + '"' + birthplace.Army().ToString() + " " + birthplace.Pos().x.ToString() + " " +
                        birthplace.Pos().y.ToString() + " 0 " + birthplace.MaxPlanes().ToString() + " " + "1" + " " +
                        "1" + " " + birthplace.Country() + " " + birthplace.Hierarchy() + " " + birthplace.Regiment();
                    f.add("BirthPlace", line, null); // write a line of text to the file
                    string[] planeset = birthplace.GetAircraftTypes();
                    foreach (string aircraft in planeset) f.add("BirthPlace0", aircraft, null);
                    birthplace.destroy();
                    GP.gpPostMissionLoad(f);
                }
                catch (NullReferenceException n)
                { }
                catch (Exception e)
                {
                    Console.WriteLine("ERROR: [gpRenameSpawnbase] " + e.Message);
                }
            }
    
            public static void gpCreateBirthplace(this IGamePlay GP, Point3d point,
                   string[] aircrafttype, string name, int army, double spawnheight,
                   int maxplanes, int parachute, int parked, string country,
                   string heirarchy, Regiment regiment)
            {   // Purpose: Creates a new birthplace/spawnbase at the specified point. 
                // Use: GamePlay.gpRenameSpawnbase(birthplace, name);
                if (heirarchy == "") heirarchy = ".";
                if (country == "De" || country == "DE" || country.Contains("germany") || country.Contains("axis") || country.Contains("blue")) country = "de";
                if (country == "Gb" || country == "GB" || country.Contains("britain") || country.Contains("allie") || country.Contains("red")) country = "gb";
                ISectionFile f = GP.gpCreateSectionFile();
                string line = '"' + name + '"' + army.ToString() + " " + point.x.ToString("000000") + " " +
                    point.y.ToString("000000") + " " + spawnheight.ToString("0000") + " " + maxplanes + " " + parachute + " " +
                    parked + " " + country + " " + heirarchy + " " + regiment;
                f.add("BirthPlace", line, null);
                foreach (string aircraft in aircrafttype) f.add("BirthPlace0", aircraft, null);
                GP.gpPostMissionLoad(f);
            }
    
            public enum Effect
            {
                SmokeSmall,
                SmokeLarge,
                FireSmall,
                FireLarge,
                BombCraterSmall,
                BombCraterMedium,
                BombCraterLarge
            }
    
            public static void gpCreateEffect(this IGamePlay GP, Effect effect, double x, double y, double DurationSecs)
            {   // Purpose: Spawns the specified effect at the specified map location for specified number of seconds.
                // Use: GamePlay.gpCreateEffect(effect, x, y, DurationSecs);
                Random random = new Random(Guid.NewGuid().GetHashCode());
                AMission AM = GP as AMission;
    
                string eff = null;
                switch (effect)
                {
                    case Effect.FireLarge: // fire & grey smoke
                        /*
                          Static3 Smoke.Environment.Smoke2 nn 75862.56 180485.61 0.00 /height 13.00
                          Static2 Smoke.Environment.Smoke1 nn 75900.77 180501.92 0.00 /height 13.00
                          Static1 Smoke.Environment.BuildingFireSmall nn 75954.15 180521.39 0.00 /height 13.00
                          Static0 Smoke.Environment.BuildingFireBig nn 76004.80 180546.89 0.00 /height 13.05
                         * */
                        eff = " Smoke.Environment.BuildingFireBig";
                        break;
                    case Effect.FireSmall: // fire & grey smoke
                        eff = " Smoke.Environment.BuildingFireSmall";
                        break;
                    case Effect.SmokeLarge: // smokeA black smoke & fire
                        eff = " Smoke.Environment.Smoke1";
                        break;
                    case Effect.SmokeSmall:// smokeB black smoke
                        eff = " Smoke.Environment.Smoke2";
                        break;
                    case Effect.BombCraterLarge:
                        // make bomb crater TF 4.0
                        //  Static0 Stationary.Environment.BombCrater_firmSoil_mediumkg nn 251217.50 257427.39 -520.00 
                        // Static2 Stationary.Environment.BombCrater_firmSoil_largekg nn 251211.73 257398.98 -520.00 
                        // Static1 Stationary.Environment.BombCrater_firmSoil_smallkg nn 251256.22 257410.66 -520.00
                        eff = " Stationary.Environment.BombCrater_firmSoil_largekg";
                        break;
                    case Effect.BombCraterMedium:
                        eff = " Stationary.Environment.BombCrater_firmSoil_mediumkg";
                        break;
                    case Effect.BombCraterSmall:
                        eff = " Stationary.Environment.BombCrater_firmSoil_smallkg";
                        break;
                    default:
    
                        break;
                }
    
                if (GP != null && AM != null && !string.IsNullOrEmpty(eff))
                {
                    ISectionFile f = GP.gpCreateSectionFile();
                    // Static0 Smoke.Environment.BuildingFireBig nn 76004.80 180546.89 0.00 /height 13.05
                    string s = string.Concat(eff, " nn ", x.ToString("#0.00"), " ",
                        y.ToString("#0.00"), " ", random.Next(1, 178 + 1).ToString("#0.00"), " /height 13.5");
                    f.add("Stationary", "Static0", s);
                    GP.gpPostMissionLoad(f);
                    if (DurationSecs < double.MaxValue)
                    {
                        AM.Timeout(DurationSecs, () =>
                        {
                            List<GroundStationary> gs = new List<GroundStationary>(GP.gpGroundStationarys(x, y, 2));
                            for (int i = 0; i < gs.Count; i++)
                            {
                                if (gs[i].Title.Contains(eff.Trim().Split('.')[2]))
                                {
                                    Point2d P = new Point2d(gs[i].pos.x, gs[i].pos.y);
                                    GP.gpRemoveGroundStationarys(P, 2);
                                }
                                    
                            }
                        });
                    }
                }
            }
    
            public static void gpCreateCityFire(this IGamePlay GP, double x, double y, double DurationSecs)
            {   // Purpose: Spawns a fire at specified map location if that location is in a city.
                //          Fires in a city have a chance to spread throughout the city.
                // Use: GamePlay.gpCreateCityFire(x, y, DurationSecs);
                Random random = new Random(Guid.NewGuid().GetHashCode());
                AMission AM = GP as AMission;
    
                if (AM != null && GP != null && GP.gpLandType(x, y) == LandTypes.CITY)
                {   // fire spreads in cities
                    GP.gpCreateEffect(Effect.FireLarge, x, y, DurationSecs);
                    List<GroundStationary> gs = new List<GroundStationary>(GP.gpGroundStationarys(x, y, 5000));
                    for (int i = 0; i < gs.Count; i++)
                    {   // find a point 30 - 80m way 
                        Point2d p = new Point2d(
                            (random.NextDouble() * (80 - 30) + 30) * Math.Cos(random.Next()),
                            (random.NextDouble() * (80 - 30) + 30) * Math.Sin(random.Next()));
                        if (gs[i].Title.Contains("BuildingFireBig") &&
                            (random.NextDouble() < 0.50 && GP.gpLandType(p.x, p.y) == LandTypes.CITY))
                        {   // 50% chance of fire to spread
                            AM.Timeout((DurationSecs / 4), () =>
                            {
                                GP.gpCreateCityFire(p.x, p.y, DurationSecs);
                            });
                        }
                    }
                }
            }
    
            public static void gpRemoveGroundStationarys(this IGamePlay IG, Point2d centre, double radius)
            {   // Purpose: Removes ground stationary objects within a circle with centre x, y
                //          and radius from the game.
                // Use: GamePlay.gpRemoveGroundStationarys(x, y, radius);
                foreach (GroundStationary gs in IG.gpGroundStationarys(centre.x, centre.y, radius)) gs.Destroy();
            }
    
            public static AiAirWayPoint[] gpFlightLandAt(this IGamePlay GP, AiAirGroup airgroup, AiAirport airport)
            {   // Purpose: Set the airgroup to land at the specified airport.
                // Use: airgroup.LandAt(airport);
                Random random = new Random(Guid.NewGuid().GetHashCode());
                List<AiWayPoint> ways = new List<AiWayPoint>(airgroup.GetWay());
                List<AiAirWayPoint> NewWays = new List<AiAirWayPoint>();
                double x;
                double y;
    
                // current waypoint
                AiAirWayPoint AWP = null;
                AWP.P = airgroup.Pos();
                AWP.Speed = airgroup.AirSpeedTAS();
                AWP.Action = AiAirWayPointType.NORMFLY;
                NewWays.Add(AWP);
    
                // third last waypoint to maintain aircraft height
                Point3d p = airport.Pos();
                double dist = (airgroup.Pos().distance(ref p));
                double StartDescendDist = 10000;
                if (dist > StartDescendDist)
                {
                    double fi = Math.Atan2(airgroup.Pos().y - airport.Pos().y, airgroup.Pos().x - airport.Pos().x);
                    x = airport.Pos().x + StartDescendDist * Math.Cos(fi);
                    y = airport.Pos().y + StartDescendDist * Math.Sin(fi);
                    AWP.P = new Point3d(x, y, airgroup.Altitude());
                    AWP.Speed = airgroup.AirSpeedTAS();
                    AWP.Action = AiAirWayPointType.NORMFLY;
                    NewWays.Add(AWP);
                }
    
                // second last waypoint over airfield
                x = airport.Pos().x + airport.CoverageR() * Math.Cos(random.Next());
                y = airport.Pos().y + airport.CoverageR() * Math.Sin(random.Next());
                double SecondLastWWayMax = 1200;
                double SecondLastWayMin = 500;
                double z = random.NextDouble() * (SecondLastWWayMax - SecondLastWayMin) + SecondLastWayMin;
                if (airgroup.Altitude() <= SecondLastWayMin) z = airgroup.Altitude();
                AWP.P = new Point3d(x, y, z);
                AWP.Speed = airgroup.AirSpeedTAS();
                AWP.Action = AiAirWayPointType.NORMFLY;
                NewWays.Add(AWP);
    
                // landing waypoint
                AWP.P = new Point3d(airport.Pos().x, airport.Pos().y, airport.Pos().x);
                AWP.Speed = airgroup.AirSpeedTAS();
                AWP.Action = AiAirWayPointType.LANDING;
                NewWays.Add(AWP);
    
                return NewWays.ToArray();
                // give the new paypoints
                //airgroup.SetWay(NewWays.ToArray());
            }
    
            public static AiAirWayPoint[] gpFlightTakeoffFrom(this IGamePlay GP, AiAirGroup airgroup, AiBirthPlace birthplace)
            {   // Purpose: Set the airgroup to takeoff from the specified airport.
                // Use: airgroup.TakeoffFrom(airport);
                Random random = new Random(Guid.NewGuid().GetHashCode());
                //List<AiWayPoint> ways = new List<AiWayPoint>(airgroup.GetWay());
                List<AiAirWayPoint> NewWays = new List<AiAirWayPoint>();
                double x;
                double y;
                Point2d P1 = new Point2d(birthplace.Pos().x, birthplace.Pos().y);
                Point2d P2 = new Point2d();
    
                // takeoff waypoint
                AiAirWayPoint AWP = null;
                AWP.P = birthplace.Pos();
                AWP.Speed = 300;
                AWP.Action = AiAirWayPointType.TAKEOFF;
                NewWays.Add(AWP);
    
                // 2nd waypoint
                double MinDist = 5000;
                double MaxDist = 7500;
                var angle = random.NextDouble() * Math.PI * 2;
                var radius = random.NextDouble() * MaxDist;
                x = birthplace.Pos().x + radius * Math.Cos(angle);
                y = birthplace.Pos().y + radius * Math.Sin(angle);
                P2 = new Point2d(x, y);
                while (P1.distance(ref P2) < MinDist)
                {
                    angle = random.NextDouble() * Math.PI * 2;
                    radius = random.NextDouble() * MaxDist;
                    x = birthplace.Pos().x + radius * Math.Cos(angle);
                    y = birthplace.Pos().y + radius * Math.Sin(angle);
                    P2 = new Point2d(x, y);
                }
                double alt = random.NextDouble() * 3000;
                while (alt < 1000) alt = random.NextDouble() * 3000;
                AWP.P = new Point3d(x, y, alt);
                AWP.Speed = 300;
                AWP.Action = AiAirWayPointType.NORMFLY;
                return NewWays.ToArray();
            }
    
            public enum PathType
            {
                Simple,
                Direct,
                None,
                Complex
            }
    
            public static AiAirWayPoint[] gpFlightFromTo(this IGamePlay GP, AiAirGroup airgroup, Point3d pointA, Point3d pointB, TF_GamePlay.PathType type)
            {   // Purpose: Set the airgroup to fly from pointA to pointB.
                // Use: airgroup.gpFlightFromTo(pointA, pointB);
                List<AiAirWayPoint> NewWays = new List<AiAirWayPoint>();
                AiAirWayPoint AWP = null;
                List<Point3d> ways = new List<Point3d>(LevyFlight(pointA, pointB, type));
                for (int i = 0; i < ways.Count; i++)
                {
                    AWP.P = ways[i];
                    AWP.Speed = 300;
                    AWP.Action = AiAirWayPointType.NORMFLY;
                    NewWays.Add(AWP);
                }
                return NewWays.ToArray();
            }
    
            private static Point3d[] LevyFlight(Point3d pointA, Point3d pointB, PathType type)
            {   // Lévy flight from point A to point B
                // ref: https://en.wikipedia.org/wiki/L%C3%A9vy_flight
                Random random = new Random(Guid.NewGuid().GetHashCode());
                double alpha = 1.5;
                List<Point3d> path = new List<Point3d>();
                Point3d P = pointA;
                path.Add(P);
                bool done = false;
    
                double FinalLeg = 10000;
                switch (type)
                {
                    case PathType.None:
                        FinalLeg = pointB.distance(ref pointA);
                        break;
                    case PathType.Complex:
                        FinalLeg = 10000;
                        break;
                }
    
                while (!done)
                {
                    double theta = random.NextDouble() * 2 * Math.PI;
                    double f = Math.Pow(random.NextDouble(), (-1 / alpha));
                    P = new Point3d(path[path.Count].x + f * Math.Cos(theta),
                                    path[path.Count].y + f * Math.Sin(theta), 
                                    path[path.Count].z);
                    while (pointB.distance(ref P) > path[path.Count].distance(ref pointB))
                    {
                        theta = random.NextDouble() * 2 * Math.PI;
                        f = Math.Pow(random.NextDouble(), (-1 / alpha));
                        P = new Point3d(path[path.Count].x + f * Math.Cos(theta),
                                        path[path.Count].y + f * Math.Sin(theta),
                                        path[path.Count].z);
                    }
                    if (pointB.distance(ref P) > FinalLeg)
                    {
                        path.Add(P);
                    }
                    else
                    {
                        path.Add(pointB);
                        done = true;
                    }
                }
                path.Add(P);
    
                // post hoc altitude interpolation
                double dAlt = pointB.z - pointA.z;
                if (dAlt != 0)
                {
                    for (int i = 0; i < path.Count; i++)
                    {
                        P = path[i];
                        P.z = P.z / dAlt / path.Count;
                        path[i] = P;
                    }
                }
                return path.ToArray();
            }
    
            private double BearingBetweenTwoPoints(Point3d pointA, Point3d pointB)
            {
                double dx = pointB.x - pointA.x;
                double dy = pointB.y - pointA.y;
                return Math.Atan2(dy, dx) * 180.0 / Math.PI;
            }
    
            public static void gpMakeFlightPath(this IGamePlay GP, AiAirGroup airgroup, AiAirWayPoint[] ways)
            {   // Purpose: Set the airgroup with the specified waypoints.
                // Use: airgroup.gpFlightFromTo(pointA, pointB);
                airgroup.SetWay(ways);
            }
    
            public static void gpMakeFlightPath(this IGamePlay GP, AiAirGroup airgroup, List<AiAirWayPoint[]> ways)
            {
                List<AiAirWayPoint> result = new List<AiAirWayPoint>();
                for (int i = 0; i < ways.Count; i++)
                    result.AddRange(new List<AiAirWayPoint>(ways[i]));
                gpMakeFlightPath(GP, airgroup, result.ToArray());
            }
        }
    Last edited by Salmo; Oct-16-2016 at 05:38.

  2. Likes N/A, 1lokos, ATAG_Lolsav, GERMANWOLF liked this post
  3. #2
    Team Fusion Salmo's Avatar
    Join Date
    Nov 2011
    Posts
    2,332
    Post Thanks / Like
    Total Downloaded
    191.25 MB

    Re: Class Extensions - GamePlay

    Some examples ...


    Code:
            public override void  OnBombExplosion(string title, double mass, Point3d pos, AiDamageInitiator initiator, int eventArgInt)
            {
     	        base.OnBombExplosion(title, mass, pos, initiator, eventArgInt);
    
                IGamePlay GP = GamePlay as IGamePlay;
                // fire for 5 minutes if bomb is in city (fire may spread for longer than 5 min)
                GP.gpCreateCityFire(pos.x, pos.y, (5 * 60));
                // smoke for 6 minutes if a ground stationary object is hit
                Point2d P = new Point2d(pos.x, pos.y);
                if (GP.gpGetGroundStationaryAtPoint(P) != null) 
                    GP.gpCreateEffect(TF_GamePlay.Effect.SmokeSmall, pos.x, pos.y, (6 * 60));
            }
    Last edited by Salmo; Oct-17-2016 at 19:12.

  4. #3
    Ace
    Join Date
    May 2013
    Location
    Stamford, Lincs, UK
    Posts
    1,033
    Post Thanks / Like
    Blog Entries
    8
    Total Downloaded
    7.46 MB

    Re: Class Extensions - GamePlay

    Salmo,

    How does the creation of map icons work with server settings set to off? Does it override that, or do you have to have icons on ?

    I can think of some limited use for the map icons for SoW but only if I have total control over what is and isn't created.

  5. #4
    Novice Pilot
    Join Date
    Jul 2013
    Posts
    50
    Post Thanks / Like
    Blog Entries
    2
    Total Downloaded
    78.01 MB

    Re: Class Extensions - GamePlay

    Thanks for making it easier for campaign builders. Looks like You spend some times on libraries. Most of theme can read possibilities of this game in more human language.

    Could see book with readable manual, which we never got.

  6. #5
    Ace
    Join Date
    May 2015
    Location
    Kansas City, Missouri area
    Posts
    515
    Post Thanks / Like
    Total Downloaded
    130.02 MB

    Re: Class Extensions - GamePlay

    Somehow I missed this until just now.

    It looks really amazing. Thank you!

  7. #6
    Ace
    Join Date
    May 2015
    Location
    Kansas City, Missouri area
    Posts
    515
    Post Thanks / Like
    Total Downloaded
    130.02 MB

    Re: Class Extensions - GamePlay

    I put this Class Extension to work yesterday and thought I would share my experiences and what I learned just in case it will help anyone else. Many thanks to Salmo for putting together a really useful and excellent resource.

    Here are the issues I encountered implementing this, and how I addressed them:

    1. On running, received error saying that that "static classes cannot implement interfaces". Re-writing the first line seemed to fix it:

    Code:
    //public static class TF_GamePlay : IGamePlay
    public static class TF_GamePlay
    2. Methods like airgroup.Altitude(); and ac.IsAiControlled()didn't work for me. I don't know if you have some other class extensions going that provide those methods or are maybe using a different ver. of CloD? Anyway, just to get the class to compile I just remarked out the methods that used .Altitude and .IsAiControlled.

    UPDATE! The reason for these errors is that this Class Extension is meant to go together with Salmo's other class extension here: http://theairtacticalassaultgroup.co...ad.php?t=23450

    Just include both Class Extensions in your .cs file (at the bottom as Salmo describes) and all will be well!


    3. Below are my current $references and using blocks:

    Code:
    //$reference parts/core/Strategy.dll
    //$reference parts/core/gamePlay.dll
    //$reference System.Core.dll
    
    using System;
    using System.Collections;
    using maddox.game;
    using maddox.game.world;
    using maddox.GP;
    using part;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using System.ComponentModel;
    using System.Threading;
    using System.Diagnostics;
    using System.Linq;
    I'm not sure that all of these are necessary just for this class extension, but many of them are--System.Linq, the $references, etc.

    4. gpGetNearestFriendlyAircraft and several other similar methods have an issue that returns a NullReferenceException. A class is set to null on the initial run but then a method or property of that class is called, resulting in a runtime error. The fix is very easy:

    Code:
    AiAircraft NearestAircraft = null;
    
     . . . 
    
    //(later in the method)           
    //original: if (aircraft.Pos().distance(ref P) < NearestAircraft.Pos().distance(ref P)) 
                   NearestAircraft = aircraft;
    //Fix: add "if ( NearestAircraft == null ||"
    if (NearestAircraft == null || aircraft.Pos().distance(ref P) < NearestAircraft.Pos().distance(ref P))
    NearestAircraft = aircraft;
    Several other similar methods that return the "nearest" aircraft, airport, etc need a similar small fix.

    FYI here is the current version I'm using in a mission. As I mentioned above, I've temporarily put several methods into block comments just because they have some minor problem or other and I haven't taken the time to fix them, and don't need that method for now.

    Code:
    /* at TOP of c# file:  */
    
    //$reference parts/core/Strategy.dll
    //$reference parts/core/gamePlay.dll
    //$reference System.Core.dll
    
    using System;
    using System.Collections;
    using maddox.game;
    using maddox.game.world;
    using maddox.GP;
    using part;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using System.ComponentModel;
    using System.Threading;
    using System.Diagnostics;
    using System.Linq;
    
    
    /* at BOTTOM of c# file: */
    
    //public static class TF_GamePlay : IGamePlay
    public static class TF_GamePlay
    {
        public static Point3d[] gpGetGroundActorPoints(this IGamePlay GP, AiGroundActorType type)
        {   // Purpose: Returns an array of 3d point locations for all the specified
            //          groundactor types.
            // Use: GamePlay.GetGroundActorPoints(type);
            List<Point3d> result = new List<Point3d>();
            List<AiGroundActor> groundactors = new List<AiGroundActor>(GP.gpGetGroundActors());
            for (int i = 0; i < groundactors.Count; i++)
            {
                if (groundactors[i].Type() == type) result.Add(groundactors[i].Pos());
            }
            return result.ToArray();
        }
    
        public static Point3d[] gpGetGroundActorPoints(this IGamePlay GP, AiGroundActorType type, int army)
        {   // Purpose: Returns an array of 3d point locations for all the specified
            //          groundactor types in the specified army.
            // Use: GamePlay.GetGroundActorPoints(type, army);
            List<Point3d> result = new List<Point3d>();
            List<AiGroundActor> groundactors = new List<AiGroundActor>(GP.gpGetGroundActors());
            for (int i = 0; i < groundactors.Count; i++)
            {
                if (groundactors[i].Type() == type && groundactors[i].Army() == army)
                    result.Add(groundactors[i].Pos());
            }
            return result.ToArray();
        }
    
        public static AiActor gpGetActorAtPoint(this IGamePlay GP, Point3d point)
        {   // Purpose: Returns the AiActor located at the specified point.
            //          Returns null if no actor at the point.
            // Use: GamePlay.gpGetActorAtPoint(point);
            AiActor result = null;
            List<AiActor> actors = new List<AiActor>(GP.gpGetActors());
            for (int i = 0; i < actors.Count; i++)
            {
                if (actors[i].Pos().distance(ref point) < 3) return actors[i] as AiActor;
            }
            return result;
        }
    
        public static GroundStationary gpGetGroundStationaryAtPoint(this IGamePlay GP, Point2d point)
        {   // Purpose: Returns the groundstationary object located at the specified point.
            //          Returns null if no stationary object at the point.
            // Use: GamePlay.gpGetActorAtPoint(point);
            GroundStationary result = null;
            List<GroundStationary> stationaries = new List<GroundStationary>(GP.gpGroundStationarys());
            for (int i = 0; i < stationaries.Count; i++)
            {
                Point2d P = new Point2d(stationaries[i].pos.x, stationaries[i].pos.y);
                if (P.distance(ref point) < 3)
                    return stationaries[i] as GroundStationary;
            }
            return result;
        }
    
        public static AiActor[] gpGetActors(this IGamePlay GP)
        {   // Purpose: Returns an array of all the AiActors in the game.
            // Use: GamePlay.gpGetAllAiActors();
            List<AiActor> result = new List<AiActor>();
            List<int> armies = new List<int>(GP.gpArmies());
            for (int i = 0; i < armies.Count; i++)
            {
                // ground actors
                List<AiGroundGroup> gg = new List<AiGroundGroup>(GP.gpGroundGroups(armies[i]));
                for (int j = 0; j < gg.Count; j++)
                {
                    List<AiActor> act = new List<AiActor>(gg[j].GetItems());
                    for (int k = 0; k < act.Count; k++)
                        result.Add(act[k] as AiActor);
                }
                // air actors
                List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(armies[i]));
                for (int j = 0; j < airgroups.Count; j++)
                {
                    List<AiActor> act = new List<AiActor>(airgroups[j].GetItems());
                    for (int k = 0; k < act.Count; k++)
                        result.Add(act[k] as AiActor);
                }
            }
            return result.ToArray();
        }
    
        public static AiActor[] gpGetActors(this IGamePlay GP, int army)
        {   // Purpose: Returns an array of all the AiActors in the 
            //          specified army in the game.
            // Use: GamePlay.gpGetAllAiActors();
            List<AiActor> result = new List<AiActor>();
            List<int> armies = new List<int>(GP.gpArmies());
            for (int i = 0; i < armies.Count; i++)
            {
                // ground actors
                List<AiGroundGroup> gg = new List<AiGroundGroup>(GP.gpGroundGroups(armies[i]));
                for (int j = 0; j < gg.Count; j++)
                {
                    List<AiActor> act = new List<AiActor>(gg[j].GetItems());
                    for (int k = 0; k < act.Count; k++)
                    {
                        if (act[k].Army() == army) result.Add(act[k] as AiActor);
                    }
                }
                // air actors
                List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(armies[i]));
                for (int j = 0; j < airgroups.Count; j++)
                {
                    List<AiActor> act = new List<AiActor>(airgroups[j].GetItems());
                    for (int k = 0; k < act.Count; k++)
                    {
                        if (act[k].Army() == army) result.Add(act[k] as AiActor);
                    }
                }
            }
            return result.ToArray();
        }
    
        public static AiGroundActor[] gpGetGroundActors(this IGamePlay GP)
        {   // Purpose: Returns an array of all ground actors in the game.
            // Use: GamePlay.gpGroundActors();
            List<AiGroundActor> result = new List<AiGroundActor>();
            List<int> armies = new List<int>(GP.gpArmies());
            for (int i = 0; i < armies.Count; i++)
            {
                List<AiGroundGroup> gg = new List<AiGroundGroup>(GP.gpGroundGroups(armies[i]));
                for (int j = 0; j < gg.Count; j++)
                {
                    List<AiActor> act = new List<AiActor>(gg[j].GetItems());
                    for (int k = 0; k < act.Count; k++)
                        result.Add(act[k] as AiGroundActor);
                }
            }
            return result.ToArray();
        }
    
        public static AiGroundActor[] gpGetGroundActors(this IGamePlay GP, int army)
        {   // Purpose: Returns an array of all ground actors in the game.
            // Use: GamePlay.gpGroundActors();
            List<AiGroundActor> result = new List<AiGroundActor>();
            List<int> armies = new List<int>(GP.gpArmies());
            for (int i = 0; i < armies.Count; i++)
            {
                List<AiGroundGroup> gg = new List<AiGroundGroup>(GP.gpGroundGroups(armies[i]));
                for (int j = 0; j < gg.Count; j++)
                {
                    List<AiActor> act = new List<AiActor>(gg[j].GetItems());
                    for (int k = 0; k < act.Count; k++)
                    {
                        if (act[k].Army() == army) result.Add(act[k] as AiGroundActor);
                    }
                }
            }
            return result.ToArray();
        }
    
        public static double gpGetObjectRotation(this IGamePlay GP, string missionfile, double x, double y)
        {   // Purpose: Returns the rotation angle of the object located at
            //          at the specified x, y coordinates
            // Use: GamePlay.gpGetObjectRotation(missionfile, x, y);
            double rotation = -1;
            Point2d P = new Point2d(x, y);
            Point2d C = new Point2d();
            System.IO.StreamReader file = new System.IO.StreamReader(missionfile);  // open the mission file for processing
            string line;
            while ((line = file.ReadLine()) != null)    // read mission file one line at a time
            {
                line = line.Trim();
                if (line.StartsWith("Static"))
                {
                    string[] part = line.Split(' ');
                    rotation = Convert.ToDouble(part[5]);
                    C = new Point2d(Convert.ToDouble(part[3]), Convert.ToDouble(part[4]));
                    if (P.distance(ref C) <= 1) return rotation;
                }
            }
            file.Close();
            return rotation;
        }
    
        public static Regiment[] gpRegiments(this IGamePlay GamePlay)
        {   // Purpose: Returns all the regiments in the game as a Regiment array.
            // Use: GamePlay.gpRegiments();
            List<Regiment> result = new List<Regiment>();
            List<AiBirthPlace> BPs = new List<AiBirthPlace>(GamePlay.gpBirthPlaces());
            for (int i = 0; i < BPs.Count; i++) result.Add(BPs[i].Regiment());
            return result.ToArray();
        }
    
        public static Regiment[] gpRegiments(this IGamePlay GamePlay, int army)
        {   // Purpose: Returns all the regiments for specified army as a Regiment array..
            // Use: GamePlay.gpRegiments(army);
            List<Regiment> result = new List<Regiment>();
            List<AiBirthPlace> BPs = new List<AiBirthPlace>(GamePlay.gpBirthPlaces(army));
            for (int i = 0; i < BPs.Count; i++) result.Add(BPs[i].Regiment());
            return result.ToArray();
        }
    
        public static string[] gpArmyNames(this IGamePlay GamePlay)
        {   // Purpose: Returns the names of all the armies in the game as a string array.
            // Use: GamePlay.gpArmyNames();
            List<string> result = new List<string>();
            List<int> Armies = new List<int>(GamePlay.gpArmies());
            for (int i = 0; i < Armies.Count; i++)
            {
                result.Add(GamePlay.gpArmyName(1));
            }
            return result.ToArray();
        }
    
        public static AiBirthPlace[] gpBirthPlaces(this IGamePlay GamePlay)
        {   // Purpose: Returns all birthplaces/spawnbases in the game as a birthplace array.
            // Use: GamePlay.gpBirthPlaces();
            List<AiBirthPlace> result = new List<AiBirthPlace>();
            List<AiBirthPlace> birthplaces = new List<AiBirthPlace>(GamePlay.gpBirthPlaces());
            for (int j = 0; j < birthplaces.Count; j++)
                result.Add(birthplaces[j]);
            return result.ToArray();
        }
    
        public static AiBirthPlace[] gpBirthPlaces(this IGamePlay GamePlay, int army)
        {   // Purpose: Returns all birthplaces/spawnbases for the specified army as a birthplace array.
            // Use: GamePlay.gpBirthPlaces(army);
            List<AiBirthPlace> result = new List<AiBirthPlace>();
            List<AiBirthPlace> birthplaces = new List<AiBirthPlace>(GamePlay.gpBirthPlaces());
            for (int j = 0; j < birthplaces.Count; j++)
            {
                if (birthplaces[j].Army() == army) result.Add(birthplaces[j]);
            }
            return result.ToArray();
        }
    
        public static int gpNumberOfPlayers(this IGamePlay GamePlay)
        {   // Purpose: Returns the number of human players in the game.
            // Use: GamePlay.NumberOfPlayers(); 
            int result = 0;
            // on Dedi the server:
            if (GamePlay.gpPlayer() != null)
            {
                result = 1;
            }
            else
            {   //rest of the crowd
                if (GamePlay.gpRemotePlayers() != null || GamePlay.gpRemotePlayers().Length > 0)
                {
                    return GamePlay.gpRemotePlayers().ToList().Count;
                }
            }
            return result;
        }
    
        public static int gpNumberOfPlayers(this IGamePlay GamePlay, int army)
        {   // Purpose: Returns the number of human players in the game in the 
            //          specified army.
            // Use: GamePlay.NumberOfPlayers(army); 
            int result = 0;
            // on Dedi the server:
            if (GamePlay.gpPlayer() != null)
            {
                if (GamePlay.gpPlayer().Army() == army) return 1;
                result = 0;
            }
            else
            {   //rest of the crowd
                if (GamePlay.gpRemotePlayers() != null || GamePlay.gpRemotePlayers().Length > 0)
                {
                    List<Player> players = new List<Player>(GamePlay.gpRemotePlayers());
                    for (int i = 0; i < players.Count; i++)
                    {
                        if (players[i].Army() == army) result += 1;
                    }
                }
            }
            return result;
        }
    
        public static void gpSendChatMessageTo(this IGamePlay GamePlay, int army, string msg, object[] parms)
        {   // Purpose: Send specified chat message to all players in specified army.
            // Use: GamePlay.sendChatMessageTo(); 
            List<Player> Players = new List<Player>();
            // on Dedi the server:
            if (GamePlay.gpPlayer() != null)
            {
                if (GamePlay.gpPlayer().Army() == army || army == -1)
                    Players.Add(GamePlay.gpPlayer());
            } //rest of the crowd
            if (GamePlay.gpRemotePlayers() != null || GamePlay.gpRemotePlayers().Length > 0)
            {
                foreach (Player p in GamePlay.gpRemotePlayers())
                {
                    if (p.Army() == army || army == -1)
                        Players.Add(p);
                }
            }
            if (Players != null && Players.Count > 0)
                GamePlay.gpLogServer(Players.ToArray(), msg, parms);
        }
    
        public static void gpSayToArmy(this IGamePlay IG, int army, string msg)
        {   // Purpose: Specified voice message to specified army. 
            // Use: GamePlay.gpSayToArmy();
            try
            {
                foreach (AiAirGroup g in IG.gpAirGroups(army))
                {
                    bool SaidToGroup = false;
                    foreach (AiActor a in g.GetItems())
                    {
                        if (SaidToGroup == true) break; // speech already said to group
                        SaidToGroup = true;
                        (a as AiAircraft).SayToGroup((a as AiAircraft).AirGroup(), msg);
                    }
                }
            }
            catch { }
        }
    
        /*
        public static void gpShowAircraftIcons(this IGamePlay IG)
        {   // Purpose: Show aircraft icons on the mini-map. 
            // Use: GamePlay.gpShowAircraftIcons();
            List<AiAirGroup> airgroups = new List<AiAirGroup>(IG.gpAirGroups());
            List<GPUserLabel> labels = new List<GPUserLabel>();
            for (int i = 0; i < airgroups.Count; i++)
            {
                Point2d P = new Point2d(airgroups[i].Pos().x, airgroups[i].Pos().y);
                string I = airgroups[i].ID().ToString();
                string T = Convert.ToDouble(IG.gpTime()).ToString("HH:mm");
                string N = airgroups[i].NOfAirc.ToString();
                string A = airgroups[i].Altitude().ToString();
                string H = airgroups[i].Heading().ToString();
                string text = string.Concat("[", I, "] N:", N, " Alt:", A, " Hdg:", H);
    
                GPUserLabel l;
                l.pos = P;
                l.Text = text;
                l.time = Convert.ToDouble(IG.gpTime());
                l.type = (int)GPUserIconType.Plane;
                labels.Add(l);
            }
    
            List<Player> players = new List<Player>(IG.gpAllPlayers());
            for (int i = 0; i < players.Count; i++)
            {
                for (int j = 0; j < labels.Count; j++)
                {
                    GPUserLabel label = labels[j];
                    GPUserLabel lbl = IG.gpMakeUserLabel(label.pos, players[i], label.Text, label.time,
                        (int)GPUserIconType.Plane);
                }
            }
        }
        */
    
        public static void gpSayEnemyPlanes(this IGamePlay IG, AiAirGroup airgroup, int army)
        {   // Purpose: Voice announcement of specified airgroup to specified army. 
            // Use: GamePlay.gpSayEnemyPlanes(army, airgroup);
            try
            {
                Random random = new Random(Guid.NewGuid().GetHashCode());
                //Random random = new Random(Guid.NewGuid().GetHashCode());
                // number of aircraft in groups of 5
                IG.gpSayToArmy(army, (airgroup.NOfAirc).ToString()); // announce aircraft count
                List<string> EnemyPlanesVoice = new List<string>
                    {
                        "Aicraft_a",
                        "Enemy_planes"
                    };
                int i = random.Next(EnemyPlanesVoice.Count);
                IG.gpSayToArmy(army, (EnemyPlanesVoice[i]).ToString()); // announce aircraft
            }
            catch { }
        }
    
        public static bool gpIsBirthPlace(this IGamePlay IG, AiAirport airport)
        {   // Purpose: Returns true if the specified airport is a birthplace/spawnbase.
            // Use: GamePlay.IsBirthPlace(airport);
            bool result = false;
            foreach (AiBirthPlace bp in IG.gpBirthPlaces())
            {
                Point3d p = bp.Pos();
                if (airport.Pos().distance(ref p) <= airport.CoverageR() ||
                    airport.Pos().distance(ref p) <= airport.FieldR())
                    result = true;
            }
            return result;
        }
    
        public static int gpGetArmyAtPoint(this IGamePlay GP, double x, double y)
        {   // Purpose: Returns the army number at the specified map point (x, y).
            // Use: GamePlay.GetArmyAtPoint(x, y);
            int result = -1;
            result = GP.gpFrontArmy(x, y);
            return result;
        }
    
        public static AiAirGroup[] gpAirGroups(this IGamePlay GP)
        {   // Purpose: Returns all game airgroups as an airgroup array.
            // Use: GamePlay.gpAllGameAirgroups();
            List<AiAirGroup> result = new List<AiAirGroup>();
            List<int> armies = new List<int>(GP.gpArmies());
            for (int i = 0; i < armies.Count; i++)
            {
                List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(i));
                result.AddRange(airgroups);
            }
            return result.ToArray();
        }
    
        public static bool gpIsLandTypeWater(this IGamePlay IG, Point3d point)
        {   // Purpose: Returns true if the specified point is in/over water.
            // Use: GamePlay.gpIsLandTypeWater();
            return (IG.gpLandType(point.x, point.y) == LandTypes.WATER);
        }
    
        public static bool gpIsLandTypeCity(this IGamePlay IG, Point3d point)
        {   // Purpose: Returns true if the specified point is in/over a city.
            // Use: GamePlay.gpIsLandTypeCity();
            return (IG.gpLandType(point.x, point.y) == LandTypes.CITY);
        }
    
        public static Player[] gpAllPlayers(this IGamePlay GP)
        {   // Purpose: Returns all humans players as a player array.
            // Use: GamePlay.gpAllPlayers();
            return GP.gpRemotePlayers();
        }
    
        public static Player[] gpAllPlayers(this IGamePlay GP, int army)
        {   // Purpose: Returns all human players in the specified army as a player array.
            // Use: GamePlay.gpAllPlayers();
            List<Player> result = new List<Player>();
            List<Player> players = new List<Player>(GP.gpRemotePlayers());
            for (int i = 0; i < players.Count; i++)
            {
                if (players[i].Army() == army) result.Add(players[i]);
            }
            return result.ToArray();
        }
    
        /*
        public static AiAircraft[] gpAllAiAircraft(this IGamePlay GP)
        {   // Purpose: Returns all Ai controlled (no humans aboard) aircraft as an array.
            // Use: GamePlay.gpAllAiAiAircraft();
            List<AiAircraft> result = new List<AiAircraft>();
            List<int> armies = new List<int>(GP.gpArmies());
            for (int i = 0; i < armies.Count; i++)
            {
                List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(i));
                for (int j = 0; j < airgroups.Count; j++)
                {
                    List<AiActor> actors = new List<AiActor>(airgroups[j].GetItems());
                    for (int k = 0; k < actors.Count; k++)
                    {
                        AiAircraft ac = actors[k] as AiAircraft;
                        if (ac != null && ac.IsAiControlled()) result.Add(ac);
                    }
                }
            }
            return result.ToArray();
        }
        */
        /*
        public static AiAircraft[] gpAllAiAircraft(this IGamePlay GP, int army)
        {   // Purpose: Returns all Ai controlled (no humans aboard) aircraft as an array.
            // Use: GamePlay.gpAllAiAiAircraft();
            List<AiAircraft> result = new List<AiAircraft>();
            List<int> armies = new List<int>(GP.gpArmies());
            for (int i = 0; i < armies.Count; i++)
            {
                List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(i));
                for (int j = 0; j < airgroups.Count; j++)
                {
                    List<AiActor> actors = new List<AiActor>(airgroups[j].GetItems());
                    for (int k = 0; k < actors.Count; k++)
                    {
                        AiAircraft ac = actors[k] as AiAircraft;
                        if (ac != null && ac.IsAiControlled() && ac.Army() == army)
                            result.Add(ac);
                    }
                }
            }
            return result.ToArray();
        }
        */
        /*
          public static AiAircraft[] gpAllPlayerAircraft(this IGamePlay GP)
          {   // Purpose: Returns all the aircraft with human players as an array.
              // Use: GamePlay.gpAllPlayerAircraft();
              List<AiAircraft> result = new List<AiAircraft>();
              List<int> armies = new List<int>(GP.gpArmies());
              for (int i = 0; i < armies.Count; i++)
              {
                  List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(i));
                  for (int j = 0; j < airgroups.Count; j++)
                  {
                      List<AiActor> actors = new List<AiActor>(airgroups[j].GetItems());
                      for (int k = 0; k < actors.Count; k++)
                      {
                          AiAircraft ac = actors[k] as AiAircraft;
                          if (ac != null && !ac.IsAiControlled()) result.Add(ac);
                      }
                  }
              }
              return result.ToArray();
          }
          */
        /*
        public static AiAircraft[] gpAllPlayerAircraft(this IGamePlay GP, int army)
        {   // Purpose: Returns all the aircraft with human players as an array.
            // Use: GamePlay.gpAllPlayerAircraft();
            List<AiAircraft> result = new List<AiAircraft>();
            List<int> armies = new List<int>(GP.gpArmies());
            for (int i = 0; i < armies.Count; i++)
            {
                List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(i));
                for (int j = 0; j < airgroups.Count; j++)
                {
                    List<AiActor> actors = new List<AiActor>(airgroups[j].GetItems());
                    for (int k = 0; k < actors.Count; k++)
                    {
                        AiAircraft ac = actors[k] as AiAircraft;
                        if (ac != null && !ac.IsAiControlled() && ac.Army() == army)
                            result.Add(ac);
                    }
                }
            }
            return result.ToArray();
        }
        */
    
        public static AiAircraft[] gpAllAircraft(this IGamePlay GP)
        {   // Purpose: Returns all the game aircraft as an array.
            // Use: GamePlay.gpAllGameAircraft();
            List<AiAircraft> result = new List<AiAircraft>();
            List<int> armies = new List<int>(GP.gpArmies());
            for (int i = 0; i < armies.Count; i++)
            {
                List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(i));
                for (int j = 0; j < airgroups.Count; j++)
                {
                    List<AiActor> actors = new List<AiActor>(airgroups[j].GetItems());
                    for (int k = 0; k < actors.Count; k++)
                    {
                        AiAircraft ac = actors[k] as AiAircraft;
                        if (ac != null) result.Add(ac);
                    }
                }
            }
            return result.ToArray();
        }
    
        public static AiAircraft[] gpAllAircraft(this IGamePlay GP, int army)
        {   // Purpose: Returns all the game aircraft (Ai & human) as an array.
            // Use: GamePlay.gpAllGameAircraft();
            List<AiAircraft> result = new List<AiAircraft>();
            List<int> armies = new List<int>(GP.gpArmies());
            for (int i = 0; i < armies.Count; i++)
            {
                List<AiAirGroup> airgroups = new List<AiAirGroup>(GP.gpAirGroups(i));
                for (int j = 0; j < airgroups.Count; j++)
                {
                    List<AiActor> actors = new List<AiActor>(airgroups[j].GetItems());
                    for (int k = 0; k < actors.Count; k++)
                    {
                        AiAircraft ac = actors[k] as AiAircraft;
                        if (ac != null && ac.Army() == army) result.Add(ac);
                    }
                }
            }
            return result.ToArray();
        }
        /*
        public static void gpSayEnemyGroupAltitude(this IGamePlay GP, AiAirGroup airgroup, string msg)
        {   // Purpose: Stated voice announcement to specified airgroup. 
            // Use: GamePlay.gpSayEnemyGroupAltitude(airgroup, message);
            double alt = airgroup.Altitude();
            List<int> armies = new List<int>(GP.gpArmies());
            for (int i = 0; i < armies.Count; i++)
            {
                foreach (AiAirGroup g in GP.gpAirGroups(i))
                {
                    bool SaidToGroup = false;
                    foreach (AiActor a in g.GetItems())
                    {
                        if (SaidToGroup == true) break; // speech already said to group
                        SaidToGroup = true;
                        (a as AiAircraft).SayToGroup((a as AiAircraft).AirGroup(), msg);
                    }
                }
            }
        }
        */
        public static AiAirport[] gpAirports(this IGamePlay GP, int army)
        {   // Purpose: Returns all the game airports as an AiAirport array. 
            //          Optionally can specify an army.
            //          Use: GamePlay.gpAirports(army)
            List<AiAirport> result = new List<AiAirport>();
            List<AiAirport> BP = new List<AiAirport>(GP.gpAirports());
            for (int i = 0; i < BP.Count; i++)
            {
                if (BP[i].Army() == army) result.Add(BP[i]);
            }
            return result.ToArray();
        }
    
        public static AiAirport gpGetRandomAirfield(this IGamePlay GP)
        {   // Purpose: Returns a random airport. 
            //          Use: GamePlay.gpGetRandomAirfield()
            Random random = new Random(Guid.NewGuid().GetHashCode());
            List<AiAirport> AP = new List<AiAirport>(GP.gpAirports());
            return AP[random.Next(AP.Count)];
        }
    
        public static AiAirport gpGetRandomAirfield(this IGamePlay GP, int army)
        {
            Random random = new Random(Guid.NewGuid().GetHashCode());
            List<AiAirport> BP = new List<AiAirport>(GP.gpAirports(army));
            AiAirport B = BP[random.Next(BP.Count)];
            int n = 0;
            while (B.Army() != army)
            {
                B = BP[random.Next(BP.Count)];
                n += 1;
            }
            if (n >= 1000) throw new Exception(string.Format(
                "gpRandomAirfield: No airport found for army {0}",
                new object[] { army.ToString() }));
            {
    
            };
            return B;
        }
    
        public static AiAirport gpGetRandomAirfield(this IGamePlay GP, Point2d centre, double radius)
        {
            Random random = new Random(Guid.NewGuid().GetHashCode());
            List<AiAirport> BP = new List<AiAirport>(GP.gpAirports());
            AiAirport B = BP[random.Next(BP.Count)];
            Point2d P = new Point2d(B.Pos().x, B.Pos().y);
            Point2d C = new Point2d(centre.x, centre.y);
            int n = 0;
            while ((C.distance(ref P) > radius) || n < 1000)
            {
                B = BP[random.Next(BP.Count)];
                n += 1;
            }
            if (n >= 1000) throw new Exception(string.Format(
                "gpRandomAirfield: No airfield found within {0} of point {1}-{2}",
                new object[] { radius.ToString(), centre.x.ToString(), centre.y.ToString() }));
            {
    
            };
            return B;
        }
    
        public static AiAirport gpGetRandomAirfield(this IGamePlay GP, Point3d centre, double radius)
        {
            return GP.gpGetRandomAirfield(new Point2d(centre.x, centre.y), radius);
        }
    
        public static AiBirthPlace gpGetRandomBirthplace(this IGamePlay GP)
        {
            Random random = new Random(Guid.NewGuid().GetHashCode());
            List<AiBirthPlace> BP = new List<AiBirthPlace>(GP.gpBirthPlaces());
            return BP[random.Next(BP.Count)];
        }
    
        public static AiBirthPlace gpGetRandomBirthplace(this IGamePlay GP, int army)
        {
            Random random = new Random(Guid.NewGuid().GetHashCode());
            List<AiBirthPlace> BP = new List<AiBirthPlace>(GP.gpBirthPlaces());
            AiBirthPlace B = BP[random.Next(BP.Count)];
            int n = 0;
            while (B.Army() != army)
            {
                B = BP[random.Next(BP.Count)];
                n += 1;
            }
            if (n >= 1000) throw new Exception(string.Format(
                "gpRandomBirthplace: No birthplace found for army {0}",
                new object[] { army.ToString() }));
            {
    
            };
            return B;
        }
    
        public static AiBirthPlace gpGetRandomBirthplace(this IGamePlay GP, Point2d centre, double radius)
        {
            Random random = new Random(Guid.NewGuid().GetHashCode());
            List<AiBirthPlace> BP = new List<AiBirthPlace>(GP.gpBirthPlaces());
            AiBirthPlace B = BP[random.Next(BP.Count)];
            Point2d P = new Point2d(B.Pos().x, B.Pos().y);
            int n = 0;
            while ((centre.distance(ref P) > radius) || n < 1000)
            {
                B = BP[random.Next(BP.Count)];
                n += 1;
            }
            if (n >= 1000) throw new Exception(string.Format(
                "gpRandomBirthplace: No birthplace found within {0} of point {1}-{2}",
                new object[] { radius.ToString(), centre.x.ToString(), centre.y.ToString() }));
            {
    
            };
            return B;
        }
    
        public static AiBirthPlace gpGetNearestBirthplace(this IGamePlay GP, Point3d point)
        {   // Purpose: Returns the nearest birthplace to the specified point. 
            // Use: GamePlay.gpNearestBirthplace(location);
            AiBirthPlace NearestBirthplace = null;
            AiBirthPlace[] BirthPlaces = GP.gpBirthPlaces();
    
            if (BirthPlaces != null)
            {
                foreach (AiBirthPlace bp in BirthPlaces)
                {
                    if (NearestBirthplace != null)
                    {
                        if (bp.Pos().distance(ref point) < NearestBirthplace.Pos().distance(ref point))
                            NearestBirthplace = bp;
                    }
                    else
                    {
                        NearestBirthplace = bp;
                    }
                }
            }
            return NearestBirthplace;
        }
    
        public static AiAirport gpGetNearestAirfield(this IGamePlay GP, Point3d point)
        {   // Purpose: Returns the nearest airfield to the specified actor. 
            // Use: GamePlay.gpNearestAirfield(actor);
            AiAirport NearestAirfield = null;
            List<AiAirport> airports = new List<AiAirport>(GP.gpAirports());
            for (int i = 0; i < airports.Count; i++)
            {
                AiAirport ThisAirport = airports[i];
                if (ThisAirport.Pos().distance(ref point) < NearestAirfield.Pos().distance(ref point))
                    NearestAirfield = ThisAirport;
            }
            return NearestAirfield;
        }
    
        public static AiAirport gpGetNearestAirfield(this IGamePlay GP, Point2d point)
        {   // Purpose: Returns the nearest airfield to the specified actor. 
            // Use: GamePlay.gpNearestAirfield(point);
            return GP.gpGetNearestAirfield(new Point3d(point.x, point.y, 0));
        }
    
        /*
        public static AiAirport gpGetNearestFriendlyAirfield(this IGamePlay GP, object obj)
        {   // Purpose: Returns the nearest friendly airfield to the specified game object. 
            // Use: GamePlay.gpNearestFriendlyAirfield(actor);
            AiAirport NearestAirfield = null;
            Point3d P = new Point3d();
            int TheArmy = -1;
            if (obj is AiActor)
            {
                TheArmy = (obj as AiActor).Army();
                P = (obj as AiActor).Pos();
            }
            else if (obj is AiAircraft)
            {
                TheArmy = (obj as AiAircraft).Army();
                P = (obj as AiAircraft).Pos();
            }
            else if (obj is AiGroundActor)
            {
                TheArmy = (obj as AiGroundActor).Army();
                P = (obj as AiGroundActor).Pos();
            }
            else if (obj is GroundStationary)
            {
                TheArmy = (obj as GroundStationary).Army();
                P = (obj as GroundStationary).pos;
            }
            else { };
    
            List<AiAirport> airports = new List<AiAirport>(GP.gpAirports(TheArmy));
            for (int i = 0; i < airports.Count; i++)
            {
                AiAirport ThisAirport = airports[i];
                if (ThisAirport.Pos().distance(ref P) < NearestAirfield.Pos().distance(ref P))
                    NearestAirfield = ThisAirport;
            }
            return NearestAirfield;
        }
        */
        /*
        public static AiAirport gpGetNearestEnemyAirfield(this IGamePlay GP, object obj)
        {   // Purpose: Returns the nearest enemy airfield to the specified actor. 
            // Use: GamePlay.gpNearestEnemyAirfield(actor);
            AiAirport NearestAirfield = null;
            Point3d P = new Point3d();
            int TheArmy = -1;
            if (obj is AiActor)
            {
                TheArmy = (obj as AiActor).Army();
                P = (obj as AiActor).Pos();
            }
            else if (obj is AiAircraft)
            {
                TheArmy = (obj as AiAircraft).Army();
                P = (obj as AiAircraft).Pos();
            }
            else if (obj is AiGroundActor)
            {
                TheArmy = (obj as AiGroundActor).Army();
                P = (obj as AiGroundActor).Pos();
            }
            else if (obj is GroundStationary)
            {
                TheArmy = (obj as GroundStationary).Army();
                P = (obj as GroundStationary).pos;
            }
            else { };
    
            List<AiAirport> airports = new List<AiAirport>(GP.gpAirports());
            for (int i = 0; i < airports.Count; i++)
            {
                AiAirport ThisAirport = airports[i];
                if (ThisAirport.Army() != TheArmy)
                {
                    if (ThisAirport.Pos().distance(ref P) < NearestAirfield.Pos().distance(ref P))
                        NearestAirfield = ThisAirport;
                }
            }
            return NearestAirfield;
        }
        */
    
        public static Player gpGetNearestPlayer(this IGamePlay GP, AiActor actor)
        {   // Purpose: Returns the nearest human player to the specified actor.
            //          Excludes other humans in the same AiCart (aircraft)
            // Use: GamePlay.gpNearestPlayer(actor);
            Player NearestPlayer = null;
            Point3d P = actor.Pos();
            AiCart ActorCart = (actor as AiPerson).Cart();
            List<Player> players = new List<Player>(GP.gpRemotePlayers());
            for (int i = 0; i < players.Count; i++)
            {
                Player ThisPlayer = players[i];
                AiCart ThisPlayerCart = (ThisPlayer as AiPerson).Cart();
                if (ThisPlayer.Place().Pos().distance(ref P) < NearestPlayer.Place().Pos().distance(ref P)
                    && ThisPlayer.Place() != actor
                    && ThisPlayer != null
                    && ActorCart != ThisPlayerCart)
                    NearestPlayer = ThisPlayer;
            }
            return NearestPlayer;
        }
    
        public static Player gpGetNearestPlayer(this IGamePlay GP, AiPerson person)
        {   // Purpose: Returns the nearest human player to the specified person. 
            // Use: GamePlay.gpNearestPlayer(person);
            return GP.gpGetNearestPlayer(person.Player().Place());
        }
    
        public static Player gpGetNearestPlayer(this IGamePlay GP, Player player)
        {   // Purpose: Returns the nearest human player to the specified player. 
            // Use: GamePlay.gpNearestPlayer(player);
            return GP.gpGetNearestPlayer(player.Place());
        }
    
        public static AiAircraft gpGetNearestAircraft(this IGamePlay GP, AiActor actor)
        {   // Purpose: Returns the nearest aircraft to the specified point. 
            // Use: GamePlay.gpNearestAircraft(point);
            AiAircraft NearestAircraft = null;
            Point3d P = actor.Pos();
            List<AiAircraft> aircrafts = new List<AiAircraft>(GP.gpAllAircraft());
            for (int i = 0; i < aircrafts.Count; i++)
            {
                AiAircraft aircraft = aircrafts[i] as AiAircraft;
                if (aircraft != null)
                {
                    if (NearestAircraft == null) NearestAircraft = aircraft;
                    if (aircraft.Pos().distance(ref P) < NearestAircraft.Pos().distance(ref P))
                        NearestAircraft = aircraft;
                }
            }
            return NearestAircraft;
        }
    
        public static AiAircraft gpGetNearestFriendlyAircraft(this IGamePlay GP, AiActor actor)
        {   // Purpose: Returns the nearest frinedly aircraft to the specified actor. 
            // Use: GamePlay.gpNearestFriendlyAircraft(actor);
            if (actor == null) return null;
            AiAircraft NearestAircraft = null;
            Point3d P = actor.Pos();
            if (GP.gpArmies() == null) return null;
            List<int> armies = new List<int>(GP.gpArmies());
    
            foreach (int army in armies)
            {
                if (GP.gpAirGroups(army) == null) continue;
                List<AiAirGroup> ag = new List<AiAirGroup>(GP.gpAirGroups(army));
                return null;
                foreach (AiAirGroup a in ag)
                {
                    if (a == null || a.GetItems() == null) continue;
                    List<AiActor> actors = new List<AiActor>(a.GetItems());
                    foreach (AiActor act in actors)
                    {
                        AiAircraft aircraft = act as AiAircraft;
                        if (aircraft != null && aircraft.Army() == actor.Army())
                        {
                            //need "if ( NearestAircraft == null ||" or it fails @ first time through the loop                        
                            if (NearestAircraft == null || aircraft.Pos().distance(ref P) < NearestAircraft.Pos().distance(ref P))
                                NearestAircraft = aircraft;
                        }
                    }
                }
            }
            return NearestAircraft;
        }
    
        public static AiAircraft gpGetNearestEnemyAircraft(this IGamePlay GP, AiActor actor)
        {   // Purpose: Returns the nearest enemy aircraft to the specified actor. 
            // Use: GamePlay.gpNearestEnemyAircraft(actor);
            AiAircraft NearestAircraft = null;
            Point3d P = actor.Pos();
            List<int> armies = new List<int>(GP.gpArmies());
            for (int i = 0; i < armies.Count; i++)
            {
                List<AiAirGroup> ag = new List<AiAirGroup>(GP.gpAirGroups(armies[i]));
                for (int j = 0; j < ag.Count; j++)
                {
                    List<AiActor> actors = new List<AiActor>(ag[j].GetItems());
                    for (int k = 0; k < actors.Count; k++)
                    {
                        AiAircraft aircraft = actors[k] as AiAircraft;
                        if (aircraft != null && aircraft.Army() != actor.Army())
                        {
                            if (aircraft.Pos().distance(ref P) < NearestAircraft.Pos().distance(ref P))
                                NearestAircraft = aircraft;
                        }
                    }
                }
            }
            return NearestAircraft;
        }
    
        public static void gpPlaneTypesInBirthplaceLabels(this IGamePlay GP)
        {   // Purpose: Adds available plane types in the all spawn-base map labels. 
            // Use: GamePlay.PutPlaneTypesInBirthplaceLabels();
            gpPlaneTypesInBirthplaceLabels(GP, GP.gpBirthPlaces());
        }
    
        public static void gpPlaneTypesInBirthplaceLabels(this IGamePlay GP, AiBirthPlace birthplace)
        {   // Purpose: Puts plane types in the specified spawn-base map labels. 
            // Use: GamePlay.gpPutPlaneTypesInBirthplaceLabels(birthplaces);
            AiBirthPlace[] bps = null;
            bps[0] = birthplace;
            gpPlaneTypesInBirthplaceLabels(GP, bps);
        }
    
        private static void gpPlaneTypesInBirthplaceLabels(this IGamePlay GP, AiBirthPlace[] birthplaces)
        {   // Purpose: Puts plane types in the specified spawn-bases map labels. 
            // Use: GamePlay.gpPutPlaneTypesInBirthplaceLabels(birthplaces);
            foreach (AiBirthPlace bp in birthplaces)
            {
                string OldMapLabel = bp.Name();
                string[] Planeset = bp.GetAircraftTypes();
                string[] words = bp.Name().Trim().Split('(');
                string MapLabel = words[0] + " (";
                foreach (string aircraftname in Planeset)
                {
                    if (aircraftname.Contains("Spit")) MapLabel = MapLabel + "Spitfire, ";
                    if (aircraftname.Contains("Hurr")) MapLabel = MapLabel + "Hurricane, ";
                    if (aircraftname.Contains("Blenheim")) MapLabel = MapLabel + "Blenheim, ";
                    if (aircraftname.Contains("DH82A  ")) MapLabel = MapLabel + "Tiger Moth, ";
                    if (aircraftname.Contains("88")) MapLabel = MapLabel + "Ju88, ";
                    if (aircraftname.Contains("87")) MapLabel = MapLabel + "Ju87, ";
                    if (aircraftname.Contains("109")) MapLabel = MapLabel + "Bf109, ";
                    if (aircraftname.Contains("BR")) MapLabel = MapLabel + "BR20, ";
                    if (aircraftname.Contains("G50")) MapLabel = MapLabel + "G50, ";
                    if (aircraftname.Contains("111")) MapLabel = MapLabel + "He111, ";
                    if (aircraftname.Contains("110")) MapLabel = MapLabel + "Bf110, ";
                    if (aircraftname.Contains("CR")) MapLabel = MapLabel + "CR42, ";
                }
                MapLabel = MapLabel.Remove(MapLabel.LastIndexOf(',')) + ")"; ; // get last occurance of , character
                GP.gpRenameBirthplace(bp, MapLabel);
            }
        }
    
        public static AircraftType[] gpAircraftTypes(this IGamePlay GP, AiBirthPlace birthplace)
        {   // Purpose: Returns the AircraftTypes for the specified birthplace. 
            // Use: GamePlay.gpAircraftTypes(birthplace);
            List<string> types = new List<string>(birthplace.GetAircraftTypes());
            HashSet<AircraftType> result = new HashSet<AircraftType>();
            for (int i = 0; i < types.Count; i++)
            {
                string aircraftname = types[i];
                if (aircraftname.Contains("Spit")) result.Add(AircraftType.Fighter);
                if (aircraftname.Contains("Hurr")) result.Add(AircraftType.Fighter);
                if (aircraftname.Contains("Blenheim")) result.Add(AircraftType.Bomber);
                if (aircraftname.Contains("DH82A  ")) result.Add(AircraftType.Fighter);
                if (aircraftname.Contains("88")) result.Add(AircraftType.DiveBomber);
                if (aircraftname.Contains("87")) result.Add(AircraftType.DiveBomber);
                if (aircraftname.Contains("109")) result.Add(AircraftType.Fighter);
                if (aircraftname.Contains("BR")) result.Add(AircraftType.Bomber);
                if (aircraftname.Contains("G50")) result.Add(AircraftType.Fighter);
                if (aircraftname.Contains("111")) result.Add(AircraftType.Bomber);
                if (aircraftname.Contains("110")) result.Add(AircraftType.HeavyFighter);
                if (aircraftname.Contains("CR")) result.Add(AircraftType.Fighter);
            }
            return result.ToArray();
        }
    
        public static bool gpHasAircraftType(this IGamePlay GP, AiBirthPlace birthplace, AircraftType type)
        {   // Purpose: Returns true if the specified birthplace planeset has the specified aircrafttype. 
            // Use: GamePlay.gpAircraftTypes(birthplace, aircraftype);
            return GP.gpAircraftTypes(birthplace).Contains(type);
        }
    
    
        public static bool gpHasAntiairDefence(this IGamePlay GP, AiAirport airport)
        {   // Purpose: Returns true if the specified airport has antiaircraft defences. 
            // Use: GamePlay.gpHasAntiairDefence(birthplace);
            List<GroundStationary> gs = new List<GroundStationary>(GP.gpGroundStationarys());
            for (int j = 0; j < gs.Count; j++)
            {
                Point3d p = airport.Pos();
                if (gs[j].pos.distance(ref p) < airport.CoverageR() &&
                    (gs[j].Type == AiGroundActorType.AAGun ||
                    gs[j].Type == AiGroundActorType.Balloon))
                    return true;
            }
            return false;
        }
    
        public static bool gpHasAntiairDefence(this IGamePlay GP, Point2d centre, double radius)
        {   // Purpose: Returns true if there are antiaircraft defences with circle with
            //          specified centre & radius. 
            // Use: GamePlay.gpHasAntiairDefence(birthplace);
            List<GroundStationary> gs = new List<GroundStationary>(GP.gpGroundStationarys());
            for (int j = 0; j < gs.Count; j++)
            {
                Point2d P = new Point2d(gs[j].pos.x, gs[j].pos.y);
                if (P.distance(ref centre) < radius &&
                    (gs[j].Type == AiGroundActorType.AAGun ||
                    gs[j].Type == AiGroundActorType.Balloon))
                    return true;
            }
            return false;
        }
    
        public static void gpRenameBirthplace(this IGamePlay GP, AiBirthPlace birthplace, string name)
        {   // Purpose: Renames the specified birthplace/spawnpoint. 
            // Use: GamePlay.gpRenameSpawnbase(birthplace, name);
            try
            {
                ISectionFile f = GP.gpCreateSectionFile();
                string line = '"' + name + '"' + birthplace.Army().ToString() + " " + birthplace.Pos().x.ToString() + " " +
                    birthplace.Pos().y.ToString() + " 0 " + birthplace.MaxPlanes().ToString() + " " + "1" + " " +
                    "1" + " " + birthplace.Country() + " " + birthplace.Hierarchy() + " " + birthplace.Regiment();
                f.add("BirthPlace", line, null); // write a line of text to the file
                string[] planeset = birthplace.GetAircraftTypes();
                foreach (string aircraft in planeset) f.add("BirthPlace0", aircraft, null);
                birthplace.destroy();
                GP.gpPostMissionLoad(f);
            }
            catch (NullReferenceException n)
            { }
            catch (Exception e)
            {
                Console.WriteLine("ERROR: [gpRenameSpawnbase] " + e.Message);
            }
        }
    
        public static void gpCreateBirthplace(this IGamePlay GP, Point3d point,
               string[] aircrafttype, string name, int army, double spawnheight,
               int maxplanes, int parachute, int parked, string country,
               string heirarchy, Regiment regiment)
        {   // Purpose: Creates a new birthplace/spawnbase at the specified point. 
            // Use: GamePlay.gpRenameSpawnbase(birthplace, name);
            if (heirarchy == "") heirarchy = ".";
            if (country == "De" || country == "DE" || country.Contains("germany") || country.Contains("axis") || country.Contains("blue")) country = "de";
            if (country == "Gb" || country == "GB" || country.Contains("britain") || country.Contains("allie") || country.Contains("red")) country = "gb";
            ISectionFile f = GP.gpCreateSectionFile();
            string line = '"' + name + '"' + army.ToString() + " " + point.x.ToString("000000") + " " +
                point.y.ToString("000000") + " " + spawnheight.ToString("0000") + " " + maxplanes + " " + parachute + " " +
                parked + " " + country + " " + heirarchy + " " + regiment;
            f.add("BirthPlace", line, null);
            foreach (string aircraft in aircrafttype) f.add("BirthPlace0", aircraft, null);
            GP.gpPostMissionLoad(f);
        }
    
        public enum Effect
        {
            SmokeSmall,
            SmokeLarge,
            FireSmall,
            FireLarge,
            BombCraterSmall,
            BombCraterMedium,
            BombCraterLarge
        }
    
        public static void gpCreateEffect(this IGamePlay GP, Effect effect, double x, double y, double DurationSecs)
        {   // Purpose: Spawns the specified effect at the specified map location for specified number of seconds.
            // Use: GamePlay.gpCreateEffect(effect, x, y, DurationSecs);
            Random random = new Random(Guid.NewGuid().GetHashCode());
            AMission AM = GP as AMission;
    
            string eff = null;
            switch (effect)
            {
                case Effect.FireLarge: // fire & grey smoke
                                       /*
                                         Static3 Smoke.Environment.Smoke2 nn 75862.56 180485.61 0.00 /height 13.00
                                         Static2 Smoke.Environment.Smoke1 nn 75900.77 180501.92 0.00 /height 13.00
                                         Static1 Smoke.Environment.BuildingFireSmall nn 75954.15 180521.39 0.00 /height 13.00
                                         Static0 Smoke.Environment.BuildingFireBig nn 76004.80 180546.89 0.00 /height 13.05
                                        * */
                    eff = " Smoke.Environment.BuildingFireBig";
                    break;
                case Effect.FireSmall: // fire & grey smoke
                    eff = " Smoke.Environment.BuildingFireSmall";
                    break;
                case Effect.SmokeLarge: // smokeA black smoke & fire
                    eff = " Smoke.Environment.Smoke1";
                    break;
                case Effect.SmokeSmall:// smokeB black smoke
                    eff = " Smoke.Environment.Smoke2";
                    break;
                case Effect.BombCraterLarge:
                    // make bomb crater TF 4.0
                    //  Static0 Stationary.Environment.BombCrater_firmSoil_mediumkg nn 251217.50 257427.39 -520.00 
                    // Static2 Stationary.Environment.BombCrater_firmSoil_largekg nn 251211.73 257398.98 -520.00 
                    // Static1 Stationary.Environment.BombCrater_firmSoil_smallkg nn 251256.22 257410.66 -520.00
                    eff = " Stationary.Environment.BombCrater_firmSoil_largekg";
                    break;
                case Effect.BombCraterMedium:
                    eff = " Stationary.Environment.BombCrater_firmSoil_mediumkg";
                    break;
                case Effect.BombCraterSmall:
                    eff = " Stationary.Environment.BombCrater_firmSoil_smallkg";
                    break;
                default:
    
                    break;
            }
    
            if (GP != null && AM != null && !string.IsNullOrEmpty(eff))
            {
                ISectionFile f = GP.gpCreateSectionFile();
                // Static0 Smoke.Environment.BuildingFireBig nn 76004.80 180546.89 0.00 /height 13.05
                string s = string.Concat(eff, " nn ", x.ToString("#0.00"), " ",
                    y.ToString("#0.00"), " ", random.Next(1, 178 + 1).ToString("#0.00"), " /height 13.5");
                f.add("Stationary", "Static0", s);
                GP.gpPostMissionLoad(f);
                if (DurationSecs < double.MaxValue)
                {
                    AM.Timeout(DurationSecs, () =>
                    {
                        List<GroundStationary> gs = new List<GroundStationary>(GP.gpGroundStationarys(x, y, 2));
                        for (int i = 0; i < gs.Count; i++)
                        {
                            if (gs[i].Title.Contains(eff.Trim().Split('.')[2]))
                            {
                                Point2d P = new Point2d(gs[i].pos.x, gs[i].pos.y);
                                GP.gpRemoveGroundStationarys(P, 2);
                            }
    
                        }
                    });
                }
            }
        }
    
        public static void gpCreateCityFire(this IGamePlay GP, double x, double y, double DurationSecs)
        {   // Purpose: Spawns a fire at specified map location if that location is in a city.
            //          Fires in a city have a chance to spread throughout the city.
            // Use: GamePlay.gpCreateCityFire(x, y, DurationSecs);
            Random random = new Random(Guid.NewGuid().GetHashCode());
            AMission AM = GP as AMission;
    
            if (AM != null && GP != null && GP.gpLandType(x, y) == LandTypes.CITY)
            {   // fire spreads in cities
                GP.gpCreateEffect(Effect.FireLarge, x, y, DurationSecs);
                List<GroundStationary> gs = new List<GroundStationary>(GP.gpGroundStationarys(x, y, 5000));
                for (int i = 0; i < gs.Count; i++)
                {   // find a point 30 - 80m way 
                    Point2d p = new Point2d(
                        (random.NextDouble() * (80 - 30) + 30) * Math.Cos(random.Next()),
                        (random.NextDouble() * (80 - 30) + 30) * Math.Sin(random.Next()));
                    if (gs[i].Title.Contains("BuildingFireBig") &&
                        (random.NextDouble() < 0.50 && GP.gpLandType(p.x, p.y) == LandTypes.CITY))
                    {   // 50% chance of fire to spread
                        AM.Timeout((DurationSecs / 4), () =>
                        {
                            GP.gpCreateCityFire(p.x, p.y, DurationSecs);
                        });
                    }
                }
            }
        }
    
        public static void gpRemoveGroundStationarys(this IGamePlay IG, Point2d centre, double radius)
        {   // Purpose: Removes ground stationary objects within a circle with centre x, y
            //          and radius from the game.
            // Use: GamePlay.gpRemoveGroundStationarys(x, y, radius);
            foreach (GroundStationary gs in IG.gpGroundStationarys(centre.x, centre.y, radius)) gs.Destroy();
        }
    
        /*
        public static AiAirWayPoint[] gpFlightLandAt(this IGamePlay GP, AiAirGroup airgroup, AiAirport airport)
        {   // Purpose: Set the airgroup to land at the specified airport.
            // Use: airgroup.LandAt(airport);
            Random random = new Random(Guid.NewGuid().GetHashCode());
            List<AiWayPoint> ways = new List<AiWayPoint>(airgroup.GetWay());
            List<AiAirWayPoint> NewWays = new List<AiAirWayPoint>();
            double x;
            double y;
    
            // current waypoint
            AiAirWayPoint AWP = null;
            AWP.P = airgroup.Pos();
            AWP.Speed = airgroup.AirSpeedTAS();
            AWP.Action = AiAirWayPointType.NORMFLY;
            NewWays.Add(AWP);
    
            // third last waypoint to maintain aircraft height
            Point3d p = airport.Pos();
            double dist = (airgroup.Pos().distance(ref p));
            double StartDescendDist = 10000;
            if (dist > StartDescendDist)
            {
                double fi = Math.Atan2(airgroup.Pos().y - airport.Pos().y, airgroup.Pos().x - airport.Pos().x);
                x = airport.Pos().x + StartDescendDist * Math.Cos(fi);
                y = airport.Pos().y + StartDescendDist * Math.Sin(fi);
                AWP.P = new Point3d(x, y, airgroup.Altitude());
                AWP.Speed = airgroup.AirSpeedTAS();
                AWP.Action = AiAirWayPointType.NORMFLY;
                NewWays.Add(AWP);
            }
    
            // second last waypoint over airfield
            x = airport.Pos().x + airport.CoverageR() * Math.Cos(random.Next());
            y = airport.Pos().y + airport.CoverageR() * Math.Sin(random.Next());
            double SecondLastWWayMax = 1200;
            double SecondLastWayMin = 500;
            double z = random.NextDouble() * (SecondLastWWayMax - SecondLastWayMin) + SecondLastWayMin;
            if (airgroup.Altitude() <= SecondLastWayMin) z = airgroup.Altitude();
            AWP.P = new Point3d(x, y, z);
            AWP.Speed = airgroup.AirSpeedTAS();
            AWP.Action = AiAirWayPointType.NORMFLY;
            NewWays.Add(AWP);
    
            // landing waypoint
            AWP.P = new Point3d(airport.Pos().x, airport.Pos().y, airport.Pos().x);
            AWP.Speed = airgroup.AirSpeedTAS();
            AWP.Action = AiAirWayPointType.LANDING;
            NewWays.Add(AWP);
    
            return NewWays.ToArray();
            // give the new paypoints
            //airgroup.SetWay(NewWays.ToArray());
        }
        */
    
        public static AiAirWayPoint[] gpFlightTakeoffFrom(this IGamePlay GP, AiAirGroup airgroup, AiBirthPlace birthplace)
        {   // Purpose: Set the airgroup to takeoff from the specified airport.
            // Use: airgroup.TakeoffFrom(airport);
            Random random = new Random(Guid.NewGuid().GetHashCode());
            //List<AiWayPoint> ways = new List<AiWayPoint>(airgroup.GetWay());
            List<AiAirWayPoint> NewWays = new List<AiAirWayPoint>();
            double x;
            double y;
            Point2d P1 = new Point2d(birthplace.Pos().x, birthplace.Pos().y);
            Point2d P2 = new Point2d();
    
            // takeoff waypoint
            AiAirWayPoint AWP = null;
            AWP.P = birthplace.Pos();
            AWP.Speed = 300;
            AWP.Action = AiAirWayPointType.TAKEOFF;
            NewWays.Add(AWP);
    
            // 2nd waypoint
            double MinDist = 5000;
            double MaxDist = 7500;
            var angle = random.NextDouble() * Math.PI * 2;
            var radius = random.NextDouble() * MaxDist;
            x = birthplace.Pos().x + radius * Math.Cos(angle);
            y = birthplace.Pos().y + radius * Math.Sin(angle);
            P2 = new Point2d(x, y);
            while (P1.distance(ref P2) < MinDist)
            {
                angle = random.NextDouble() * Math.PI * 2;
                radius = random.NextDouble() * MaxDist;
                x = birthplace.Pos().x + radius * Math.Cos(angle);
                y = birthplace.Pos().y + radius * Math.Sin(angle);
                P2 = new Point2d(x, y);
            }
            double alt = random.NextDouble() * 3000;
            while (alt < 1000) alt = random.NextDouble() * 3000;
            AWP.P = new Point3d(x, y, alt);
            AWP.Speed = 300;
            AWP.Action = AiAirWayPointType.NORMFLY;
            return NewWays.ToArray();
        }
    
        public enum PathType
        {
            Simple,
            Direct,
            None,
            Complex
        }
    
        public static AiAirWayPoint[] gpFlightFromTo(this IGamePlay GP, AiAirGroup airgroup, Point3d pointA, Point3d pointB, TF_GamePlay.PathType type)
        {   // Purpose: Set the airgroup to fly from pointA to pointB.
            // Use: airgroup.gpFlightFromTo(pointA, pointB);
            List<AiAirWayPoint> NewWays = new List<AiAirWayPoint>();
            AiAirWayPoint AWP = null;
            List<Point3d> ways = new List<Point3d>(LevyFlight(pointA, pointB, type));
            for (int i = 0; i < ways.Count; i++)
            {
                AWP.P = ways[i];
                AWP.Speed = 300;
                AWP.Action = AiAirWayPointType.NORMFLY;
                NewWays.Add(AWP);
            }
            return NewWays.ToArray();
        }
    
        private static Point3d[] LevyFlight(Point3d pointA, Point3d pointB, PathType type)
        {   // Lévy flight from point A to point B
            // ref: https://en.wikipedia.org/wiki/L%C3%A9vy_flight
            Random random = new Random(Guid.NewGuid().GetHashCode());
            double alpha = 1.5;
            List<Point3d> path = new List<Point3d>();
            Point3d P = pointA;
            path.Add(P);
            bool done = false;
    
            double FinalLeg = 10000;
            switch (type)
            {
                case PathType.None:
                    FinalLeg = pointB.distance(ref pointA);
                    break;
                case PathType.Complex:
                    FinalLeg = 10000;
                    break;
            }
    
            while (!done)
            {
                double theta = random.NextDouble() * 2 * Math.PI;
                double f = Math.Pow(random.NextDouble(), (-1 / alpha));
                P = new Point3d(path[path.Count].x + f * Math.Cos(theta),
                                path[path.Count].y + f * Math.Sin(theta),
                                path[path.Count].z);
                while (pointB.distance(ref P) > path[path.Count].distance(ref pointB))
                {
                    theta = random.NextDouble() * 2 * Math.PI;
                    f = Math.Pow(random.NextDouble(), (-1 / alpha));
                    P = new Point3d(path[path.Count].x + f * Math.Cos(theta),
                                    path[path.Count].y + f * Math.Sin(theta),
                                    path[path.Count].z);
                }
                if (pointB.distance(ref P) > FinalLeg)
                {
                    path.Add(P);
                }
                else
                {
                    path.Add(pointB);
                    done = true;
                }
            }
            path.Add(P);
    
            // post hoc altitude interpolation
            double dAlt = pointB.z - pointA.z;
            if (dAlt != 0)
            {
                for (int i = 0; i < path.Count; i++)
                {
                    P = path[i];
                    P.z = P.z / dAlt / path.Count;
                    path[i] = P;
                }
            }
            return path.ToArray();
        }
    
        private static double BearingBetweenTwoPoints(Point3d pointA, Point3d pointB)
        {
            double dx = pointB.x - pointA.x;
            double dy = pointB.y - pointA.y;
            return Math.Atan2(dy, dx) * 180.0 / Math.PI;
        }
    
        public static void gpMakeFlightPath(this IGamePlay GP, AiAirGroup airgroup, AiAirWayPoint[] ways)
        {   // Purpose: Set the airgroup with the specified waypoints.
            // Use: airgroup.gpFlightFromTo(pointA, pointB);
            airgroup.SetWay(ways);
        }
    
        public static void gpMakeFlightPath(this IGamePlay GP, AiAirGroup airgroup, List<AiAirWayPoint[]> ways)
        {
            List<AiAirWayPoint> result = new List<AiAirWayPoint>();
            for (int i = 0; i < ways.Count; i++)
                result.AddRange(new List<AiAirWayPoint>(ways[i]));
            gpMakeFlightPath(GP, airgroup, result.ToArray());
        }
    }
    Altogether, this is some really amazing work and much-needed functionality. Thank you!
    Last edited by TWC_Flug; Jan-29-2017 at 14:02. Reason: Updated with solution to one of the issues

  8. Likes ATAG_Lolsav liked this post
  9. #7
    ATAG Member ATAG_Lolsav's Avatar
    Join Date
    Jun 2012
    Posts
    4,684
    Post Thanks / Like
    Blog Entries
    1
    Total Downloaded
    16.32 MB

    Re: Class Extensions - GamePlay

    A request Flug. Can you translate what appears to be your deeper knowledge of the scripts/maps into a multiplayer mission? That would be neat!

  10. #8
    Team Fusion Salmo's Avatar
    Join Date
    Nov 2011
    Posts
    2,332
    Post Thanks / Like
    Total Downloaded
    191.25 MB

    Re: Class Extensions - GamePlay

    Flug - Thankyou for testing this. I threw it together rather quickly & forgot to handle the null cases. I'm hoping that many of these functions can be added to the game code to enhance the functionality for mission builders.

  11. #9
    Ace SIA_Sp00k's Avatar
    Join Date
    Jun 2014
    Posts
    751
    Post Thanks / Like
    Total Downloaded
    365.25 MB

    Re: Class Extensions - GamePlay

    I had visions of learning how to code. I think I can safely put them all to bed now

    Back to work Flug, that mission server won't build itself! ( insert whip crack here)
    https://imgur.com/VCUiXpY

  12. #10
    Supporting Member BOO's Avatar
    Join Date
    Nov 2015
    Location
    5 mins from the Red Arrows, 1 min from a good pub.
    Posts
    2,667
    Post Thanks / Like
    Blog Entries
    5
    Total Downloaded
    1.01 GB

    Re: Class Extensions - GamePlay

    Code is not for the likes of us Sp00k (goes back to poking a dead badger with stick)
    My Rig: Samsung 40" TV, 5600X on Air, RTX3080 FE, 32GB Gskill Neo 3600 CL16 RAM, M2 Gen 4 Drives, Corsair RM850X PSU, MFG Crosswinds, TM WH Throttle, Virpil Mongoose T50CM w/100mm extension, TIR5, EDtracker pro.


    " Better a thorn on the outside than a prick on the inside"

  13. Likes SIA_Sp00k, SD_MBen liked this post
  14. #11
    Ace
    Join Date
    May 2015
    Location
    Kansas City, Missouri area
    Posts
    515
    Post Thanks / Like
    Total Downloaded
    130.02 MB

    Re: Class Extensions - GamePlay

    Quote Originally Posted by Salmo View Post
    Flug - Thankyou for testing this. I threw it together rather quickly & forgot to handle the null cases. I'm hoping that many of these functions can be added to the game code to enhance the functionality for mission builders.
    No worries--I honestly can't believe you can code it that clean without stopping to test 50 different ways after about each 30 seconds of coding, which is the only way I am capable of coding

    More that I just wanted to a give a bit of a leg-up to anyone who is wanting to use this--which I definitely think they SHOULD!--to understand that there is a bit of finalizing & testing to do before this is ready for a production server.

    Also, I belatedly realized that this is meant to go together with the your other class extension here: http://theairtacticalassaultgroup.co...ad.php?t=23450

    That is the reason for the some of the errors I experienced--simply including both Class Extensions at the bottom of the c# file should solve those errors altogether.
    Last edited by TWC_Flug; Jan-29-2017 at 14:04.

  15. #12
    Combat pilot
    Join Date
    Aug 2020
    Posts
    181
    Post Thanks / Like
    Total Downloaded
    121.31 MB

    Re: Class Extensions - GamePlay

    I have been working hard to get the AI to run better and find targets and the code at the bottom Flug came up with but I am getting an Error I do not understand.

    Any help would be great!

    Code:
    using System;
    using maddox.game;
    using maddox.game.world;
    using maddox.GP;
    using System.Diagnostics;
    using System.Collections;
    
    public class Mission : AMission
    {
    	int InterceptIntervall = 20;
    			
    	bool BlueGroupLimitReached = false;
    /////////////////Settings
    	int maxBlueGroups = 24;
    	
    	Stopwatch MissionTimer1Int = new Stopwatch(); /////////////Intercept
    	Stopwatch MissionTimer1IntA = new Stopwatch();
    	Stopwatch MissionTimer2IntA = new Stopwatch();
    	Stopwatch MissionTimer3IntA = new Stopwatch();
    	Stopwatch MissionTimer4IntA = new Stopwatch();
    	Stopwatch MissionTimer5IntA = new Stopwatch();
    	Stopwatch MissionTimer6IntA = new Stopwatch();
    	Stopwatch MissionTimer7IntA = new Stopwatch();
    	Stopwatch MissionTimer8IntA = new Stopwatch();
    	Stopwatch MissionTimer9IntA = new Stopwatch();
    	Stopwatch MissionTimer10IntA = new Stopwatch();
    	Stopwatch MissionTimer11IntA = new Stopwatch();
    	Stopwatch MissionTimer12IntA = new Stopwatch();
    	Stopwatch MissionTimer13IntA = new Stopwatch();
    	Stopwatch MissionTimer14IntA = new Stopwatch();
    	Stopwatch MissionTimer15IntA = new Stopwatch();
    	Stopwatch MissionTimer16IntA = new Stopwatch();
    	Stopwatch MissionTimer17IntA = new Stopwatch();
    	Stopwatch MissionTimer18IntA = new Stopwatch();
    	Stopwatch MissionTimer19IntA = new Stopwatch();
    	Stopwatch MissionTimer20IntA = new Stopwatch();
    	Stopwatch MissionTimer21IntA = new Stopwatch();
    	Stopwatch MissionTimer22IntA = new Stopwatch();
    
    ////////////////////////////Count AIAirgroup
    	public int CountAIAirGroups(int Army)
        {
            int NumberOfGroupes = 0;
    
            if (GamePlay.gpAirGroups(Army) == null) return 0;
            foreach (AiAirGroup ag in GamePlay.gpAirGroups(Army))
    			{
    			NumberOfGroupes += 1;
    			}
            return NumberOfGroupes;
        }
    
    ///////////////////////OnTick
    	public override void OnTickGame()
    	{
    		base.OnTickGame();
    ////////////////Check if Blue Airgroups under Limit
    		if (CountAIAirGroups(2) >= maxBlueGroups)
    		{BlueGroupLimitReached = true;}
    		else
    		BlueGroupLimitReached = false;
    	
    		if ((MissionTimer1IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))	  			//Unlock the trigger again
    		{
    		MissionTimer1IntA.Reset();
    		GamePlay.gpGetTrigger("Intercept1").Enable = true; 					
    		}
    
    		if ((MissionTimer2IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))	   			//wieder freischalten des Triggers
    		{
    		MissionTimer2IntA.Reset();
    		GamePlay.gpGetTrigger("Intercept2").Enable = true; 							
    		} 
    
    		if ((MissionTimer3IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer3IntA.Reset();					
    		GamePlay.gpGetTrigger("Intercept3").Enable = true;        
    		}
    		
    		if ((MissionTimer4IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer4IntA.Reset();
    		GamePlay.gpGetTrigger("Intercept4").Enable = true; 							
    		} 
    
    		if ((MissionTimer5IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer5IntA.Reset();
    		GamePlay.gpGetTrigger("Intercept5").Enable = true; 							
    	    }
    
    		if ((MissionTimer6IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))
    		{
    		MissionTimer6IntA.Reset();	
    		GamePlay.gpGetTrigger("Intercept6").Enable = true; 	
    		}
    
    		if ((MissionTimer7IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer7IntA.Reset();
    		GamePlay.gpGetTrigger("Intercept7").Enable = true; 							
    		} 
    
    		if ((MissionTimer8IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer8IntA.Reset();					
    		GamePlay.gpGetTrigger("Intercept8").Enable = true;        
    		}
    		
    		if ((MissionTimer9IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer9IntA.Reset();
    		GamePlay.gpGetTrigger("Intercept9").Enable = true; 							
    		} 
    
    		if ((MissionTimer10IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer10IntA.Reset();
    		GamePlay.gpGetTrigger("Intercept10").Enable = true; 							
    	    }
    		
    		if ((MissionTimer11IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))	  			//wieder freischalten des Triggers
    		{
    		MissionTimer11IntA.Reset();	
    		GamePlay.gpGetTrigger("Intercept11").Enable = true; 				
    		}
    
    		if ((MissionTimer12IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer12IntA.Reset();
    		GamePlay.gpGetTrigger("Intercept12").Enable = true; 							
    		} 
    
    		if ((MissionTimer13IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer13IntA.Reset();					
    		GamePlay.gpGetTrigger("Intercept13").Enable = true;        
    		}
    		
    		if ((MissionTimer14IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer14IntA.Reset();
    		GamePlay.gpGetTrigger("Intercept14").Enable = true; 							
    		} 
    
    		if ((MissionTimer15IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer15IntA.Reset();
    		GamePlay.gpGetTrigger("Intercept15").Enable = true; 							
    	    }
    		
    		if ((MissionTimer16IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))	  			//wieder freischalten des Triggers
    		{
    		MissionTimer16IntA.Reset();
    		GamePlay.gpGetTrigger("Intercept16").Enable = true;			
    		}
    
    		if ((MissionTimer17IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer17IntA.Reset();
    		GamePlay.gpGetTrigger("Intercept17").Enable = true; 							
    		} 
    
    		if ((MissionTimer18IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer18IntA.Reset();					
    		GamePlay.gpGetTrigger("Intercept18").Enable = true;        
    		}
    		
    		if ((MissionTimer19IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer19IntA.Reset();
    		GamePlay.gpGetTrigger("Intercept19").Enable = true; 							
    		} 
    
    		if ((MissionTimer20IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer20IntA.Reset();
    		GamePlay.gpGetTrigger("Intercept20").Enable = true; 							
    	    }
    
    		if ((MissionTimer21IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer21IntA.Reset();
    		GamePlay.gpGetTrigger("Intercept21").Enable = true; 							
    	    }
    		
    		if ((MissionTimer22IntA.Elapsed.Minutes >= InterceptIntervall) && (BlueGroupLimitReached == false))  			//wieder freischalten des Triggers
    		{
    		MissionTimer22IntA.Reset();
    		GamePlay.gpGetTrigger("Intercept22").Enable = true; 							
    	    }
    	}
    	
    	public override void OnTrigger(int missionNumber, string shortName, bool active)
    	{
    		base.OnTrigger(missionNumber, shortName, active); 
    
    			if ("Intercept1".Equals(shortName))
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept1");
                    if (action != null)
                    {
                         action.Do();
                    }
    				GamePlay.gpGetTrigger(shortName).Enable = false; 
    			}
    			
    			if ("Intercept2".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept2");
                    if (action != null)
                    {
                         action.Do();
                    }
    				GamePlay.gpGetTrigger(shortName).Enable = false; 				
    			}
    			
    			if ("Intercept3".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept3");
                    if (action != null)
                    {
                         action.Do();
                    }
    				GamePlay.gpGetTrigger(shortName).Enable = false; 				
    			}
    			
    			if ("Intercept4".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept4");
                    if (action != null)
                    {
                         action.Do();
                    }
    				GamePlay.gpGetTrigger(shortName).Enable = false; 				
    			}
    			
    			if ("Intercept5".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept5");
                    if (action != null)
                    {
                         action.Do();
                    }			
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept6".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept6");
                    if (action != null)
                    {
                         action.Do();
                    }			
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}			
    			
    			if ("Intercept7".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept7");
                    if (action != null)
                    {
                         action.Do();
                    }			
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept8".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept8");
                    if (action != null)
                    {
                         action.Do();
                    }			
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept9".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept9");
                    if (action != null)
                    {
                         action.Do();
                    }			
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept10".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept10");
                    if (action != null)
                    {
                         action.Do();
                    }			
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept11".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept11");
                    if (action != null)
                    {
                         action.Do();
                    }				
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept12".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept12");
                    if (action != null)
                    {
                         action.Do();
                    }				
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept13".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept13");
                    if (action != null)
                    {
                         action.Do();
                    }			
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept14".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept14");
                    if (action != null)
                    {
                         action.Do();
                    }			
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept15".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept15");
                    if (action != null)
                    {
                         action.Do();
                    }				
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept16".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept16");
                    if (action != null)
                    {
                         action.Do();
                    }				
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept17".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept17");
                    if (action != null)
                    {
                         action.Do();
                    }				
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept18".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept18");
                    if (action != null)
                    {
                         action.Do();
                    }				
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept19".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept19");
                    if (action != null)
                    {
                         action.Do();
                    }				
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept20".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept20");
                    if (action != null)
                    {
                         action.Do();
                    }			
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept21".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept21");
                    if (action != null)
                    {
                         action.Do();
                    }			
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept22".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept22");
                    if (action != null)
                    {
                         action.Do();
                    }		
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    			
    			if ("Intercept23".Equals(shortName) && active) 
    			{ 
                    AiAction action = GamePlay.gpGetAction("Intercept23");
                    if (action != null)
                    {
                         action.Do();
                    }		
                    GamePlay.gpGetTrigger(shortName).Enable = false;            
    			}
    
    	}
    		
    	Random random = new Random();
    /// 
    /// Improving AI behavior (whole section added by flug)
    /// ---------------------------------------------------------------------------
    
        public static AiAircraft gpGetNearestEnemyAircraft(this IGamePlay GP, AiActor actor)
        {   // Purpose: Returns the nearest enemy aircraft to the specified actor. 
            // Use: GamePlay.gpNearestEnemyAircraft(actor);
            AiAircraft NearestAircraft = null;
            Point3d P = actor.Pos();
            List<int> armies = new List<int>(GP.gpArmies());
            for (int i = 0; i < armies.Count; i++)
            {
                List<AiAirGroup> ag = new List<AiAirGroup>(GP.gpAirGroups(armies[i]));
                for (int j = 0; j < ag.Count; j++)
                {
                    List<AiActor> actors = new List<AiActor>(ag[j].GetItems());
                    for (int k = 0; k < actors.Count; k++)
                    {
                        AiAircraft aircraft = actors[k] as AiAircraft;
                        if (aircraft != null && aircraft.Army() != actor.Army())
                        {
                            if (aircraft.Pos().distance(ref P) < NearestAircraft.Pos().distance(ref P))
                                NearestAircraft = aircraft;
                        }
                    }
                }
            }
            return NearestAircraft;
        }
    
        public override void AiAirGroup getRandomNearbyEnemyAirGroup(AiAirGroup from, double distance_m, double lowAlt_m, double highAlt_m)
        {
            try
            {
                Point3d startPos = from.Pos();
                List<AiAirGroup> airGroups = getNearbyEnemyAirGroups(from, distance_m, lowAlt_m, highAlt_m);
                if (airGroups == null || airGroups.Count == 0) return null;
                int choice = random.Next(airGroups.Count);
                if (airGroups[choice].Pos().distance(ref startPos) <= distance_m / 2) //We'll somewhat favor airgroups closer to the from airgroup
                    choice = random.Next(airGroups.Count);
                return airGroups[choice];
            }
            catch (Exception ex) { Console.WriteLine("getRandomNearbyEnemyAirGroup ERROR: " + ex.ToString()); return null; }
    
        }
    
        //Gets all nearby enemy airgroup within distance_m (meters) and between alt - lowAlt_m & alt-highAlt_m altitude of the target
        public List<AiAirGroup> getNearbyEnemyAirGroups(AiAirGroup from, double distance_m, double lowAlt_m, double highAlt_m)
        {
            try
            {
                if (GamePlay == null) return null;
                if (from == null) return null;
                List<AiAirGroup> returnAirGroups = new List<AiAirGroup>() { };
                AiAirGroup[] Airgroups;
                Point3d StartPos = from.Pos();
    
                Airgroups = GamePlay.gpAirGroups((from.Army() == 1) ? 2 : 1);
    
                if (Airgroups != null)
                {
                    foreach (AiAirGroup airGroup in Airgroups)
                    {
                        if (airGroup == null || airGroup.GetItems().Length == 0) continue;
                        //AiAircraft a = airGroup.GetItems()[0] as AiAircraft;
    
                        if (airGroup.Pos().z > StartPos.z - lowAlt_m && airGroup.Pos().z < StartPos.z + highAlt_m && airGroup.Pos().distance(ref StartPos) <= distance_m)
                            returnAirGroups.Add(airGroup);
    
                    }
                    return returnAirGroups;
                }
                else
                    return new List<AiAirGroup>() { };
            }
            catch (Exception ex) { Console.WriteLine("getNearbyEnemyAirGroups ERROR: " + ex.ToString()); return new List<AiAirGroup>() { }; }
    
        }
    
        public System.Threading.Timer changeTargetToDifferentNearbyAircraftTimer;
    
        //Experiment with changingn the airgroup target to player, seeing if they will attack better
        //Not actually used for now
        public override void changeTargetToDifferentNearbyAircraft_recurs()
        {
            changeTargetToDifferentNearbyAircraftTimer = new System.Threading.Timer(
                new TimerCallback(changeTargetToDifferentNearbyAircraft),
                null,
                30000, //wait time @ startup
                52340); //periodically call the callback at this interval
        }
    
        public override void changeTargetToDifferentNearbyAircraft(object o)
        {
            try
            {
                if (GamePlay != null && GamePlay.gpArmies() != null && GamePlay.gpArmies().Length > 0)
                {
                    foreach (int army in GamePlay.gpArmies())
                    {
    
                        if (GamePlay.gpAirGroups(army) != null && GamePlay.gpAirGroups(army).Length > 0)
                        {
                            foreach (AiAirGroup airGroup in GamePlay.gpAirGroups(army))
                            {
                                if (airGroup != null && airGroup.GetItems() != null && airGroup.GetItems().Length > 0)
                                {
                                    foreach (AiActor actor in airGroup.GetItems())
                                    {
                                        if (actor != null && actor is AiAircraft)
                                        {
                                            AiAircraft aircraft = actor as AiAircraft;
                                            if (aircraft != null)
                                            {
                                                {
                                                    AiAirGroup newAG = null;
                                                    if (airGroup.getTask() == AiAirGroupTask.ATTACK_AIR)
                                                        newAG = getRandomNearbyEnemyAirGroup(airGroup, 5000, 1000, 1000);
                                                    else if (airGroup.getTask() == AiAirGroupTask.FLY_WAYPOINT || airGroup.getTask() == AiAirGroupTask.HUNTING)
                                                        newAG = getRandomNearbyEnemyAirGroup(airGroup, 500, 300, 300);
    
                                                    if (newAG == null) break;
    
                                                    airGroup.setTask(AiAirGroupTask.ATTACK_AIR, newAG);
                                                    airGroup.changeGoalTarget(newAG);
                                                    AiAircraft newAircraft = newAG.GetItems()[0] as AiAircraft;
                                                    string playername = newAG.Name();
                                                    if (aircraft != null && aircraft.Player(0) != null) playername = aircraft.Player(0).Name();
                                                    Console.WriteLine("Change Target to different nearby enemy aircraft: " + actor.Name() + " to " + playername);
    
                                                    break; //each airGroup has only one target so no need to do this more than once.
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex) { Console.WriteLine("ChangeTargetToDifferentNearbyAircraft: ERROR! " + ex.ToString()); }
        }
    }
    Last edited by BlacKnight; Mar-16-2023 at 11:59.

  16. #13
    Supporting Member
    Join Date
    Dec 2019
    Posts
    473
    Post Thanks / Like
    Total Downloaded
    22.61 MB

    Re: Class Extensions - GamePlay

    Ooops, ignore me.
    I am Yo-Yo not YoYo (that's someone else)

  17. #14
    Combat pilot
    Join Date
    Oct 2021
    Posts
    113
    Post Thanks / Like
    Total Downloaded
    12.95 MB

    Re: Class Extensions - GamePlay

    Deleted by GANIX.
    Last edited by GANIX; Jun-04-2023 at 06:27.

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •