Can't get a list of ALL ground actors in CloD/FMB scripting, and workaround - Flug - 02-23-2020
I'm going to post a few random tips, problems, and solutions relating to scripting in IL2 Cliffs of Dover (CloD), and Full Mission Builder (FMB), here as they come up, in hopes they might be useful to someone else.
The issue here is that in CloD there are ways via scripting to get lists of all possible ground actors (things like ships, artillery, cars--the things on the ground that move and shoot etc, not just sit there and look pretty). But, in fact they do not return a list of ALL possible ground actors, which is a problem.
I think the reason is probably that ground actors are supposed to be gathered together into groundgroups, which have a "Chief" which gives them instructions about what to do, where to drive, etc.
But it is possible & in fact easy to just place ground actors WITHOUT creating the associated Chief and then those don't show up when you get the supposedly full listing of ground actors.
So one solution would be to always create the associated Chief--even for a single actor or things like artillery there are advantages to that.
Here is the other solution.
Typical usage:
Code: //This gets all static ACTORs such as (?) ships, artillery. (?). There is no way to get this full list from CloD that I know of.
if (!diveTarget)
{
try
{
Console.WriteLine("MBT: Trying to find a ground actor");
if (allStaticActors != null)
{
List<AiActor> closeStaticActors = new List<AiActor>(coverCalcs.gpGetAllGroundActorsNear(allStaticActors, pos, maxMove_m).ToList()); //1000?
//Finding actors we're going to range wider 1500. meters IN reality maybe we could look up the objective radius. But actors nearby will be flak, etc etc etc. All helpful.
coverCalcs.Shuffle(closeStaticActors);
foreach (AiActor act in closeStaticActors)
{
if (act.IsAlive())
{
string groundType = "";
if (act as AiGroundActor != null) groundType = (act as AiGroundActor).Type().ToString();
newTarget = act;
Console.WriteLine("MBT: FOUND a ground actor" + newTarget.Name() + " " + groundType);
diveTarget = true;
break;
}
if (diveTarget) break;
}
}
}
catch (Exception ex) { Console.WriteLine("Bomb select #1 ERROR: " + ex.ToString()); }
}
Another couple of typical/useful functions:
Code: if (msg.StartsWith("<lactors") && (admin_privilege_level(player) > 1))
{
Console.WriteLine("Actor list - starting...");
Point3d p = new Point3d(284703, 125257, 0);
double r = 9000;
string[] words = msg_orig.Split(' ');
//Calcs.listStatics(GamePlay, new List<string>() { "smoke", "fire", "crater", "jerry" });
List<AiActor> closeStaticActors = new List<AiActor>(coverCalcs.gpGetAllGroundActorsNear(allStaticActors, p,r).ToList()); //1000?
//Finding actors we're going to range wider 1500. meters IN reality maybe we could look up the objective radius. But actors nearby will be flak, etc etc etc. All helpful.
foreach (AiActor act in closeStaticActors) Console.WriteLine("Actor: {0} {1}", act.Name(), (act as AiCart).InternalTypeName());
}
else if (msg.StartsWith("<glist"))
{
AiActor[] aia = coverCalcs.gpGetGroundActors(this, 1);
//exit;
coverCalcs.listAllGroundActors(GamePlay);
foreach (AiActor a in allStaticActors) Console.WriteLine(a.Name());
}
Utility functions:
Code: public static AiActor[] gpGetGroundActors(CoverMission msn, int army)
{ // Purpose: Returns an array of all the AiActors in the game.
// Use: GamePlay.gpGetActors();
List<AiActor> result = new List<AiActor>();
//List<int> armies = new List<int>(msn.GamePlay.gpArmies());
List<int> armies = new List<int>() { 1, 2 };
for (int i = 0; i < armies.Count; i++)
{
if (i != army) continue;
// ground actors
AiGroundGroup[] agg = msn.GamePlay.gpGroundGroups(armies[i]);
if (agg == null)
{
//Console.WriteLine("# it's nulL!");
return null;
}
//Console.WriteLine("#" + agg.ToString());// + " " + agg.Length.ToString());
//return null;
if (agg == null) return null;
List<AiGroundGroup> gg = new List<AiGroundGroup>(msn.GamePlay.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);
//Console.WriteLine("Actor: " + (act[k] as AiActor).Name());
}
}
/*
// air actors
List<AiAirGroup> airgroups = new List<AiAirGroup>(IG.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 void listAllGroundActors(IGamePlay gp, int missionNumber = -1)
{
//TODO: Make it list only the actors in that mission by prefixing "XX:" if missionNumber is included.
gp.gpLogServer(null, "Listing all ground actors:", new object[] { });
int group_count = 0;
if (gp.gpArmies() != null && gp.gpArmies().Length > 0)
{
foreach (int army in gp.gpArmies())
{
//List a/c in player army if "inOwnArmy" == true; otherwise lists a/c in all armies EXCEPT the player's own army
if (gp.gpGroundGroups(army) != null && gp.gpGroundGroups(army).Length > 0)
{
foreach (AiGroundGroup group in gp.gpGroundGroups(army))
{
group_count++;
if (group.GetItems() != null && group.GetItems().Length > 0)
{
//poscount = group.NOfAirc;
foreach (AiActor actor in group.GetItems())
{
if (actor != null)
{
gp.gpLogServer(null, actor.Name(), new object[] { });
AiGroundGroup actorSubGroup = actor as AiGroundGroup;
if (actorSubGroup != null && (actorSubGroup).GetItems() != null && actorSubGroup.GetItems().Length > 0)
{
foreach (AiActor a in actorSubGroup.GetItems())
{
gp.gpLogServer(null, a.Name(), new object[] { });
}
}
}
}
}
}
}
}
}
}
public static AiActor[] gpGetAllGroundActorsNear(AiActor[] aia, Point3d pos, double radius)
{
List<AiActor> result = new List<AiActor>();
foreach (AiActor a in aia)
{
if (coverCalcs.CalculatePointDistance(a.Pos(), pos) < radius) result.Add(a);
}
return result.ToArray();
}
The recursive function that keeps the allStaticActors array continually updated:
Code: AiActor[] allStaticActors = null;
Dictionary<string, IMissionObjective> SMissionObjectivesList = new Dictionary<string, IMissionObjective>();
private void renewAllStaticActors_recurs()
{
Timeout(3 * 60, () => renewAllStaticActors_recurs());
allStaticActors = coverCalcs.gpGetAllGroundActors(this, stb_lastMissionLoaded);
SMissionObjectivesList = TWCMainMission.SMissionObjectivesList();
}
The function that kicks off the recursive function and loads the initially appearing groundactors:
Code: int stb_lastMissionLoaded = -1;
public override void OnMissionLoaded(int missionNumber)
{
base.OnMissionLoaded(missionNumber);
try
{
stb_lastMissionLoaded = missionNumber;
if (missionNumber == MissionNumber)
{
Timeout(20, () => renewAllStaticActors_recurs());
}
}
catch (Exception ex) { Console.WriteLine("Cover OnMissionLoaded(): " + ex.ToString()); }
}
You could just run the recursive function once every time onMissionLoaded is called, capturing the new actors from each mission as it is loaded (instead of running it recursively every X minutes). But our missions tend to have many small submissions loaded later on so just running it every X minutes seemed just as good a solution.
|