PDA

View Full Version : Mission Menu and Ambulance


fearlessfrog
09-12-2011, 12:15 AM
I spent a short bit of time looking at the new Beta API for a 'Mission Menu' and figuring out how to direct traffic. When you crash you can call for an ambulance.

Details and test mission download here:
http://simhq.com/forum/ubbthreads.php/topics/3387861/Re_Ambulance_Station_notes_fro.html#Post3387861

The interesting part of the script is here:

else if (menuItemIndex == 6)
{
GamePlay.gpHUDLogCenter("Ambulance Dispatched!");

Player[] all = { player };

AiActor amb = GamePlay.gpActorByName("0_Chief"); // Our ambulance, defined as
first vehicle in mission
if (amb != null)
{
AiGroup g = (AiGroup)amb; // Everything with waypoints is always in groups
AiWayPoint[] waypoints = g.GetWay(); // Get the waypoints it had

foreach (AiWayPoint waypoint in waypoints)
{
//GamePlay.gpLogServer(all, "waypoint: x=" + waypoint.P.x.ToString() + " y=" +
waypoint.P.y.ToString(), null);
x = waypoint.P.x;
y = waypoint.P.y;
z = waypoint.P.z; // Remember this for late, as we need a good height from somewhere
}

Player me = GamePlay.gpPlayer();
AiActor where = me.Place();
Point3d pos = where.Pos(); // Get our position, i.e. where to send the ambulance
Point3d target = new Point3d(pos.x, pos.y, z); // Set our target to be us

//GamePlay.gpLogServer(all, "me: x=" + pos.x.ToString() + " y=" + pos.y.ToString()
+ " z=" + pos.z.ToString(), null);

// Note: Set the z height to be the last waypoint height rather than it's real height,
Pos seems a PoS
Point3d last_known = new Point3d(amb.Pos().x, amb.Pos().y, z);

AiGroundWayPoint[] newWaypoints = { new AiGroundWayPoint(ref last_known, 50.0, 0.0, 10.0),
new AiGroundWayPoint(ref target, 50.0, 0.0, 10.0) };

g.SetWay(newWaypoints); // Off we go, on new route!

AiWayPoint[] resetWaypoints = g.GetWay(); // Just diagnostics, i.e. can be removed
but basically to check they took
foreach (AiWayPoint waypoint in resetWaypoints)
{
//GamePlay.gpLogServer(all, "New waypoint: x=" + waypoint.P.x.ToString() + " y="
+ waypoint.P.y.ToString(), null);
}

}
setMainMenu(player);
}

Hope that helps put someone on the right spline.

FG28_Kodiak
09-12-2011, 04:36 AM
Nice Idea
'SAAAAANNNNNIIIIII' ;)

With the new command menu feature, we got a very interesting feature.

Vengeanze
09-12-2011, 05:22 AM
This is insanely kewl.
What happens if one crash in enemyland and call for ambulance. :-D

salmo
09-12-2011, 05:45 AM
"... new Beta API for a 'Mission Menu' ..." ???

Where do you access/get a copy of the API?

fearlessfrog
09-12-2011, 05:49 AM
The API is part of the beta patch.

As for if you called it from far away, I've not tried it - but waypoint-wise it would just keep trucking till it got to you.

41Sqn_Banks
09-12-2011, 06:27 AM
You should use the "GamePlay.gpFindPath()" method to let the game engine calculate the waypoints. This way the car will not cross water and use roads and bridges.

As the calculation of the waypoints is done asynchronous (in a different thread) you have to wait until the calculation is finished:

IRecalcPathParams pathParams = GamePlay.gpFindPath(start, 10.0, end, 20.0, PathType.GROUND, groundGroup.Army);
// start is the start coordinate, end it the end coordinate, 10.0 and 20.0 how much the calculated way is allowed to differ from start and end point.

while (pathParams.State == RecalcPathState.WAIT)
{
Game.gpLogServer(new Player[] { Game.gpPlayer() }, "Wait for path.", null);
// This is not a good idea, I think this would stop the game thread. Better check if the path calculation is finished within the OnTick() method.
System.Threading.Thread.Sleep(100);
}
if (pathParams.State == RecalcPathState.SUCCESS)
{
Game.gpLogServer(new Player[] { Game.gpPlayer() }, "Path found (" + pathParams.Path.Length.ToString() + ").", null);
groundGroup.setWay(pathParams.Path)
}
else if(pathParams.State == RecalcPathState.FAILED)
{
Game.gpLogServer(new Player[] { Game.gpPlayer() }, "Path not found.", null);
}


Note that his should only show you the usage. It's not a good idea to sleep the thread while the game is running. I do it for the IL2DCE mission generation when I create the mission - but at this stage the mission is not running.

Look for navyr's tank war example how to check the result of the path calculation within the OnTick() method.

salmo
09-12-2011, 09:11 AM
The API is part of the beta patch.
Thankyou fearlessfrog. Maybe the routine could be reworked so that instead of the pilot calling an ambulance via after-crash menu, an ambulance could arrive "automatically".

Pseudocode:
1. On player crash-landing
2. Is crash-landing within home base radius?
3. If so, cycle through vehicle objects and see if there is an ambulance (&/or fire truck)
4. If an ambulance exists, Is the ambulance within the same home base radius as the crash landing?
5. If so, then send the ambulance to the crash site (calculate waypoints).

Ataros
09-12-2011, 10:03 AM
Various emergency & service cars script by naryv posted an sukhoi.ru today.


using System;
using System.Collections.Generic;
using maddox.game;
using maddox.game.world;
using maddox.GP;

public class Mission : AMission
{
public override void OnBattleStarted()
{
base.OnBattleStarted();
MissionNumberListener = -1;
}

Random rnd = new Random();


[Flags]
internal enum ServiceType // тип обслуживающих машинок
{
NONE = 0,
EMERGENCY = 1,
FIRE = 2,
FUEL = 4,
AMMO = 8,
BOMBS = 16,
PRISONERCAPTURE = 32
}

internal class TechCars
{
internal AiGroundGroup TechCar { get; set; }
internal AiAirport BaseAirport { get; set; }
internal IRecalcPathParams cur_rp { get; set; }
internal int RouteFlag = 0;
internal int cartype = 0;
internal int servPlaneNum = -1;
internal ServiceType CarType { get { return (ServiceType)cartype; } set { cartype = (int)value; } }

public TechCars(AiGroundGroup car, AiAirport airoport, IRecalcPathParams rp)
{
this.TechCar = car;
this.BaseAirport = airoport;
this.cur_rp = rp;
}

}

internal class PlanesQueue {
internal AiAircraft aircraft { get; set; }
internal AiAirport baseAirport { get; set; }
internal int state = 0;
internal ServiceType State { get { return (ServiceType)state; } set { state = (int)value; } }
internal int Lifetime = 0;
internal float health = 1;
public PlanesQueue(AiAircraft aircraft, AiAirport baseAirport, int state)
{
this.aircraft = aircraft;
this.baseAirport = baseAirport;
this.state = state;
}
}

internal List<TechCars> CurTechCars = new List<TechCars>();
internal List<PlanesQueue> CurPlanesQueue = new List<PlanesQueue>();
TechCars TmpCar = null;
bool MissionLoading = false;

internal double PseudoRnd(double MinValue, double MaxValue)
{
return rnd.NextDouble() * (MaxValue - MinValue) + MinValue;
}



public override void OnActorTaskCompleted(int missionNumber, string shortName, AiActor actor)
{
base.OnActorTaskCompleted(missionNumber, shortName, actor);


AiActor ai_actor = actor as AiActor;
if (ai_actor != null)
{
if (ai_actor is AiGroundGroup)
for (int i = 0; i < CurTechCars.Count; i++) // если обслуживающая техника доехала до обслуживаемого самолёта, разрешаем ей освободиться
{
if (CurTechCars[i].TechCar == ai_actor as AiGroundGroup)
if (CurTechCars[i].RouteFlag == 1)
EndPlaneService(i);

else
CheckNotServicedPlanes(i);
};
}
}

internal void CheckNotServicedPlanes(int techCarIndex)
{
for (int j = 0; j < CurPlanesQueue.Count; j++)
{
if (CurTechCars[techCarIndex].TechCar.IsAlive() && (CurPlanesQueue[j].baseAirport == CurTechCars[techCarIndex].BaseAirport) && ((CurTechCars[techCarIndex].CarType & CurPlanesQueue[j].State) != 0) && (CurTechCars[techCarIndex].servPlaneNum == -1))
{
if (SetEmrgCarRoute(j, techCarIndex)) // отправляем машинку обслуживать найденный самолёт
{
return;
}
}
}
}

internal void EndPlaneService(int techCarIndex)
{
if (CurTechCars[techCarIndex].cur_rp == null) return;
CurTechCars[techCarIndex].cur_rp = null; // сбрасываем маршрут
if (CurTechCars[techCarIndex].servPlaneNum >= 0)
{

CurPlanesQueue[CurTechCars[techCarIndex].servPlaneNum].State &= ~CurTechCars[techCarIndex].CarType; // убираем тип обслуживания у обслуживаемого самолёта, если он есть

CurTechCars[techCarIndex].servPlaneNum = -1; // сбрасываем номер обслуживаемого самолёта
Timeout(5f, () =>
{
if (!MoveFromRWay(techCarIndex))// проверяем не стоим ли на взлётке, и уезжаем с неё если так.
{
CurTechCars[techCarIndex].RouteFlag = 0;
CheckNotServicedPlanes(techCarIndex); // и смотрим, нет ли ещё необслуженных самолётов
}
});

}
else Timeout(5f, () =>
{
CurTechCars[techCarIndex].RouteFlag = 0;
CheckNotServicedPlanes(techCarIndex); // и смотрим, нет ли ещё необслуженных самолётов
});


}

internal bool MoveFromRWay(int carNum)
{
bool result = false;
if ((GamePlay.gpLandType(CurTechCars[carNum].TechCar.Pos().x, CurTechCars[carNum].TechCar.Pos().y) & LandTypes.ROAD) == 0)
return result;

Point3d TmpPos = CurTechCars[carNum].TechCar.Pos();
while (((GamePlay.gpLandType(TmpPos.x, TmpPos.y) & LandTypes.ROAD) != 0))
{
TmpPos.x += 10f;
TmpPos.y += 10f;
};
Point2d EmgCarStart, EmgCarFinish;
EmgCarStart.x = CurTechCars[carNum].TechCar.Pos().x; EmgCarStart.y = CurTechCars[carNum].TechCar.Pos().y;
EmgCarFinish.x = TmpPos.x; EmgCarFinish.y = TmpPos.y;
CurTechCars[carNum].servPlaneNum = -1;
CurTechCars[carNum].RouteFlag = 0;
CurTechCars[carNum].cur_rp = null;
CurTechCars[carNum].cur_rp = GamePlay.gpFindPath(EmgCarStart, 10f, EmgCarFinish, 10f, PathType.GROUND, CurTechCars[carNum].TechCar.Army());

result = true;
return result;
}


public bool SetEmrgCarRoute(int aircraftNumber,int carNum)
{
bool result = false;
if (CurTechCars[carNum].TechCar != null)
{
CurTechCars[carNum].servPlaneNum = aircraftNumber; // устанавливаем номер обслуживаемого самолёта
if (CurTechCars[carNum].cur_rp == null)
{
Point2d EmgCarStart, EmgCarFinish, LandedPos;
LandedPos.x = CurPlanesQueue[aircraftNumber].aircraft.Pos().x; LandedPos.y = CurPlanesQueue[aircraftNumber].aircraft.Pos().y;
int Sign = ((carNum % 2) == 0) ? 2 : -2;
EmgCarStart.x = CurTechCars[carNum].TechCar.Pos().x; EmgCarStart.y = CurTechCars[carNum].TechCar.Pos().y;
EmgCarFinish.x = LandedPos.x - PseudoRnd(2f, 5f) * ((LandedPos.x - EmgCarStart.x) / (Math.Abs(LandedPos.x - EmgCarStart.x))) - Sign;
EmgCarFinish.y = LandedPos.y - PseudoRnd(2f, 5f) * ((LandedPos.y - EmgCarStart.y) / (Math.Abs(LandedPos.y - EmgCarStart.y))) - Sign;
CurTechCars[carNum].cur_rp = GamePlay.gpFindPath(EmgCarStart, 10f, EmgCarFinish, 10f, PathType.GROUND, CurTechCars[carNum].TechCar.Army());
result = true;
}
}
return result;
}


public override void OnMissionLoaded(int missionNumber)
{
base.OnMissionLoaded(missionNumber);
if (missionNumber > 0)
{
List<string> CarTypes = new List<string>();
CarTypes.Add(":0_Chief_Emrg_");
CarTypes.Add(":0_Chief_Fire_");
CarTypes.Add(":0_Chief_Fuel_");
CarTypes.Add(":0_Chief_Ammo_");
CarTypes.Add(":0_Chief_Bomb_");
CarTypes.Add(":0_Chief_Prisoner_");

AiGroundGroup MyCar = null;

for (int i = 0; i < 3; i++)
{
for (int j = 0; j < CarTypes.Count; j++)
{
MyCar = GamePlay.gpActorByName(missionNumber.ToString() + CarTypes[j] + i.ToString()) as AiGroundGroup;
if (MyCar != null)
{
TmpCar = new TechCars(MyCar, FindNearestAirport(MyCar), null);
TmpCar.CarType = (ServiceType)(1 << j);
TmpCar.cur_rp = null;
if (!CurTechCars.Contains(TmpCar))
CurTechCars.Add(TmpCar);
MissionLoading = false;
};
}
}
}
}


public override void OnTickGame() {
base.OnTickGame();
try {
if (Time.tickCounter() % 64 == 0)
{

for (int i = 0; i < CurPlanesQueue.Count; i++)
{
CurPlanesQueue[i].Lifetime++;
if ((CurPlanesQueue[i].State == ServiceType.NONE) || (CurPlanesQueue[i].aircraft == null) || (CurPlanesQueue[i].Lifetime > 200))
{
for (int j = 0; j < CurTechCars.Count; j++)
if (CurTechCars[j].servPlaneNum == i)
EndPlaneService(j);
CurPlanesQueue.RemoveAt(i);
}
};

for (int i = 0; i < CurTechCars.Count; i++)
{
TechCars car = CurTechCars[i];
if ((car.TechCar != null && car.cur_rp != null) && (car.cur_rp.State == RecalcPathState.SUCCESS) )
{
if (car.TechCar.IsAlive() && (car.RouteFlag == 0)/* && (car.servPlaneNum != -1)*/)
{
car.RouteFlag = 1;
car.cur_rp.Path[0].P.x = car.TechCar.Pos().x; car.cur_rp.Path[0].P.y = car.TechCar.Pos().y;
car.TechCar.SetWay(car.cur_rp.Path);
//if (car.servPlaneNum != -1) car.RouteFlag = 0;
}

double Dist = Math.Sqrt((car.cur_rp.Path[car.cur_rp.Path.Length - 1].P.x - car.TechCar.Pos().x) * (car.cur_rp.Path[car.cur_rp.Path.Length - 1].P.x - car.TechCar.Pos().x) + (car.cur_rp.Path[car.cur_rp.Path.Length - 1].P.y - car.TechCar.Pos().y) * (car.cur_rp.Path[car.cur_rp.Path.Length - 1].P.y - car.TechCar.Pos().y));
if (car.servPlaneNum != -1)
{
if (Dist < ((CurPlanesQueue[car.servPlaneNum].aircraft.Type() == AircraftType.Bomber) ? 20f : 10f))
EndPlaneService(i);
}
else if (Dist < 15f)
{
EndPlaneService(i);
}
}
if ((car.cur_rp == null) && (car.RouteFlag == 0) && (car.servPlaneNum != -1))
{
EndPlaneService(i);
};
};
}

} catch (Exception e) {}
}




internal AiAirport FindNearestAirport(AiActor actor)
{
AiAirport aMin = null;
double d2Min = 0;
Point3d pd = actor.Pos();
int n = GamePlay.gpAirports().Length;
for (int i = 0; i < n; i++)
{
AiAirport a = (AiAirport)GamePlay.gpAirports()[i];

if (!a.IsAlive())
continue;

Point3d pp;
pp = a.Pos();
pd.z = pp.z;
double d2 = pd.distanceSquared(ref pp);
if ((aMin == null) || (d2 < d2Min))
{
aMin = a;
d2Min = d2;
}
}
if (d2Min > 2250000.0)
aMin = null;

return aMin;
}

internal ISectionFile CreateEmrgCarMission(Point3d startPos, double fRadius, int portArmy, int planeArmy, AircraftType type, float health, Point3d aircraftPos)
{ ISectionFile f = GamePlay.gpCreateSectionFile();
string sect;
string key;
string value;
string ChiefName1 = "0_Chief_" + (health < 1f ? "Fire_" : "Fuel_");
string ChiefName2 = "0_Chief_" + (health < 1f ? "Emrg_" : "Ammo_");
string ChiefName3 = "0_Chief_" + (health < 1f ? "Bomb_" : "Bomb_");


if (portArmy == planeArmy) //свой прилетел
{
switch (portArmy)
{
case 1:
if (health < 1f)
{
sect = "CustomChiefs";
key = "";
value = "Vehicle.custom_chief_emrg_0 $core/icons/tank.mma"; //Пожарка
f.add(sect, key, value);
value = "Vehicle.custom_chief_emrg_1 $core/icons/tank.mma";//Скорая
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_0";
key = "Car.Austin_K2_ATV";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.Fire_pump_UK2_Transport";
value = "1";
f.add(sect, key, value);
sect = "Vehicle.custom_chief_emrg_1";
key = "Car.Austin_K2_Ambulance";
value = "";
f.add(sect, key, value);

sect = "Chiefs";
key = "0_Chief_Fire_0";
value = "Vehicle.custom_chief_emrg_0 gb /skin0 materialsSummer_RAF";
f.add(sect, key, value);
key = "0_Chief_Emrg_1";
value = "Vehicle.custom_chief_emrg_1 gb /skin0 materialsSummer_RAF";
f.add(sect, key, value);
}
else
{
sect = "CustomChiefs";
key = "";
value = "Vehicle.custom_chief_emrg_0 $core/icons/tank.mma";
f.add(sect, key, value);
value = "Vehicle.custom_chief_emrg_1 $core/icons/tank.mma";
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_0"; // заправщики
key = "Car.Albion_AM463";
value = "";
f.add(sect, key, value);
if (type == AircraftType.Bomber) // для бомберов больше горючки и бомбы подвозим
{
key = "Car.Fordson_N";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.Towed_Bowser_UK1_Transport";
value = "1";
f.add(sect, key, value);
}

sect = "Vehicle.custom_chief_emrg_1"; // Оружие
value = "";
key = "Car.Bedford_MW_open";
f.add(sect, key, value);


if (type == AircraftType.Bomber) // для бомберов бомбы подвозим
{
sect = "CustomChiefs";
key = "";
value = "Vehicle.custom_chief_emrg_2 $core/icons/tank.mma";
f.add(sect, key, value);
sect = "Vehicle.custom_chief_emrg_2";
value = "";
key = "Car.Fordson_N";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.BombLoadingCart_UK1_Transport";
value = "1";
f.add(sect, key, value);
key = "TrailerUnit.BombLoadingCart_UK1_Transport";
f.add(sect, key, value);
};

sect = "Chiefs";
key = "0_Chief_Fuel_0";
value = "Vehicle.custom_chief_emrg_0 gb /skin0 materialsSummer_RAF";
f.add(sect, key, value);

key = "0_Chief_Ammo_1";
value = "Vehicle.custom_chief_emrg_1 gb /skin0 materialsSummer_RAF/tow00_00 1_Static";
f.add(sect, key, value);

if (type == AircraftType.Bomber)
{
key = "0_Chief_Bomb_2";
value = "Vehicle.custom_chief_emrg_2 gb /tow01_00 2_Static/tow01_01 3_Static/tow01_02 4_Static/tow01_03 5_Static/tow02_00 6_Static/tow02_01 7_Static";
f.add(sect, key, value);
}
sect = "Stationary";
key = "1_Static";
value = "Stationary.Morris_CS8-Bedford_MW_CargoAmmo3 gb 0.00 0.00 0.00";
f.add(sect, key, value);
if (type == AircraftType.Bomber) // бомбы грузим
{
key = "2_Static";
value = "Stationary.Weapons_.Bomb_B_GP_250lb_MkIV gb 0.00 0.00 0.00";
f.add(sect, key, value);
key = "3_Static";
value = "Stationary.Weapons_.Bomb_B_GP_250lb_MkIV gb 0.00 0.00 0.00";
f.add(sect, key, value);
key = "4_Static";
value = "Stationary.Weapons_.Bomb_B_GP_250lb_MkIV gb 0.00 0.00 0.00";
f.add(sect, key, value);
key = "5_Static";
value = "Stationary.Weapons_.Bomb_B_GP_250lb_MkIV gb 0.00 0.00 0.00";
f.add(sect, key, value);
key = "6_Static";
value = "Stationary.Weapons_.Bomb_B_GP_500lb_MkIV gb 0.00 0.00 0.00";
f.add(sect, key, value);
key = "7_Static";
value = "Stationary.Weapons_.Bomb_B_GP_500lb_MkIV gb 0.00 0.00 0.00";
f.add(sect, key, value);
};
};
break;
case 2:
sect = "CustomChiefs"; //Пожарка
key = "";
value = "Vehicle.custom_chief_emrg_0 $core/icons/tank.mma";
f.add(sect, key, value);
value = "Vehicle.custom_chief_emrg_1 $core/icons/tank.mma";//Скорая
f.add(sect, key, value);
if (health < 1f)
{
sect = "Vehicle.custom_chief_emrg_0";
key = "Car.Renault_UE";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.Foam_Extinguisher_GER1_Transport";
value = "1";
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_1";
if (PseudoRnd(0f, 1f) < 0.5f)
{
key = "Car.Opel_Blitz_med-tent";
}
else { key = "Car.Opel_Blitz_cargo_med"; };
value = "";
f.add(sect, key, value);
sect = "Chiefs";
key = "0_Chief_Fire_0";// "0_Chief_emrg";
value = "Vehicle.custom_chief_emrg_0 de ";
f.add(sect, key, value);
key = "0_Chief_Emrg_1";// "0_Chief_emrg";
value = "Vehicle.custom_chief_emrg_1 de ";
f.add(sect, key, value);
}
else
{
sect = "Vehicle.custom_chief_emrg_0";
key = "Car.Opel_Blitz_fuel";
value = "";
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_1";
key = "Car.Renault_UE";
f.add(sect, key, value);
key = "TrailerUnit.Oil_Cart_GER1_Transport";
value = "1";
f.add(sect, key, value);
key = "Car.Renault_UE";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.Anlasswagen_(starter)_GER1_Transport";
value = "1";
f.add(sect, key, value);

if (type == AircraftType.Bomber) // бомбы грузим
{
sect = "CustomChiefs";
key = "";
value = "Vehicle.custom_chief_emrg_2 $core/icons/tank.mma";
f.add(sect, key, value);
sect = "Vehicle.custom_chief_emrg_2";
key = "Car.Renault_UE";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.HydraulicBombLoader_GER1_Transport";
value = "1";
f.add(sect, key, value);
key = "Car.Renault_UE";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.BombSled_GER1_Transport";
value = "1";
f.add(sect, key, value);
}

sect = "Chiefs";
key = "0_Chief_Fuel_0";
value = "Vehicle.custom_chief_emrg_0 de";
f.add(sect, key, value);
key = "0_Chief_Ammo_1";
value = "Vehicle.custom_chief_emrg_1 de";
f.add(sect, key, value);
if (type == AircraftType.Bomber)
{
key = "0_Chief_Bomb_2";
value = "Vehicle.custom_chief_emrg_2 de /tow01_00 1_Static/tow03_00 2_Static";
f.add(sect, key, value);
sect = "Stationary";
key = "1_Static";
value = "Stationary.Weapons_.Bomb_B_SC-250_Type2_J de 0.00 0.00 0.00";
f.add(sect, key, value);
key = "2_Static";
value = "Stationary.Weapons_.Bomb_B_SC-1000_C de 0.00 0.00 0.00";
f.add(sect, key, value);
};
};
break;
default:
break;

}
}
else
{
switch (portArmy)
{
case 1:
if (health < 1f)
{
sect = "CustomChiefs";
key = "";
value = "Vehicle.custom_chief_emrg_0 $core/icons/tank.mma"; //Пожарка
f.add(sect, key, value);
value = "Vehicle.custom_chief_emrg_1 $core/icons/tank.mma";//Скорая
f.add(sect, key, value);
value = "Vehicle.custom_chief_emrg_2 $core/icons/tank.mma";//Броневик
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_0";
key = "Car.Austin_K2_ATV";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.Fire_pump_UK2_Transport";
value = "1";
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_1";
key = "Car.Austin_K2_Ambulance";
value = "";
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_2";
key = "Car.Beaverette_III";
value = "";
f.add(sect, key, value);

sect = "Chiefs";
key = "0_Chief_Fire_0";
value = "Vehicle.custom_chief_emrg_0 gb /skin0 materialsSummer_RAF";
f.add(sect, key, value);
key = "0_Chief_Emrg_1";
value = "Vehicle.custom_chief_emrg_1 gb /skin0 materialsSummer_RAF";
f.add(sect, key, value);
key = "0_Chief_Prisoner_2";
value = "Vehicle.custom_chief_emrg_2 gb ";
f.add(sect, key, value);
ChiefName3 = "0_Chief_Prisoner_";

}
else
{
sect = "CustomChiefs";
key = "";
value = "Vehicle.custom_chief_emrg_0 $core/icons/tank.mma"; //Пожарка
f.add(sect, key, value);
sect = "Vehicle.custom_chief_emrg_0";
key = "Car.Beaverette_III";
value = "";
f.add(sect, key, value);
sect = "Chiefs";
key = "0_Chief_Prisoner_0";
value = "Vehicle.custom_chief_emrg_0 gb ";
f.add(sect, key, value);
ChiefName1 = "0_Chief_Prisoner_";
};
break;
case 2:
if (health < 1f)
{
sect = "CustomChiefs";
key = "";
value = "Vehicle.custom_chief_emrg_0 $core/icons/tank.mma"; //Пожарка
f.add(sect, key, value);
value = "Vehicle.custom_chief_emrg_1 $core/icons/tank.mma";//Скорая
f.add(sect, key, value);
value = "Vehicle.custom_chief_emrg_2 $core/icons/tank.mma";//Броневик
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_0";
key = "Car.Renault_UE";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.Foam_Extinguisher_GER1_Transport";
value = "1";
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_1";
key = "Car.Opel_Blitz_cargo_med";
value = "";
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_2";
key = "Car.SdKfz_231_6Rad";
value = "";
f.add(sect, key, value);

sect = "Chiefs";
key = "0_Chief_Fire_0";
value = "Vehicle.custom_chief_emrg_0 de";
f.add(sect, key, value);
key = "0_Chief_Emrg_1";
value = "Vehicle.custom_chief_emrg_1 de";
f.add(sect, key, value);
key = "0_Chief_Prisoner_2";
value = "Vehicle.custom_chief_emrg_2 de /marker0 1940-42_var1";
f.add(sect, key, value);
ChiefName3 = "0_Chief_Prisoner_";

}
else
{
sect = "CustomChiefs";
key = "";
value = "Vehicle.custom_chief_emrg_0 $core/icons/tank.mma"; //Пожарка
f.add(sect, key, value);
sect = "Vehicle.custom_chief_emrg_0";
key = "Car.SdKfz_231_6Rad";
value = "";
f.add(sect, key, value);
sect = "Chiefs";
key = "0_Chief_Prisoner_0";
value = "Vehicle.custom_chief_emrg_0 de /marker0 1940-42_var1";
f.add(sect, key, value);
ChiefName1 = "0_Chief_Prisoner_";
};
break;
default:
break;
};
}


Point3d TmpStartPos = startPos;
TmpStartPos.x += PseudoRnd(-30f, 30f) + fRadius; TmpStartPos.y += PseudoRnd(-30f, 30f) + fRadius;
Point3d BirthPos = EmrgVehicleStartPos(TmpStartPos, startPos);

sect = ChiefName1+"0" + "_Road";
key = "";
value = BirthPos.x.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.y.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.z.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " 0 92 5 ";
f.add(sect, key, value);
BirthPos.x -= 50f * ((BirthPos.x - aircraftPos.x) / Math.Abs(BirthPos.x - aircraftPos.x)); BirthPos.y -= 50f * ((BirthPos.y - aircraftPos.y) / Math.Abs(BirthPos.y - aircraftPos.y));
value = BirthPos.x.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.y.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.z.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat);
f.add(sect, key, value);

TmpStartPos = startPos;
TmpStartPos.x += PseudoRnd(-30f, 30f) - fRadius; TmpStartPos.y += PseudoRnd(-30f, 30f) + fRadius;
BirthPos = EmrgVehicleStartPos(TmpStartPos, startPos);
//BirthPos = TmpStartPos;
sect = ChiefName2+"1" + "_Road";
key = "";
value = BirthPos.x.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.y.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.z.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " 0 92 5 ";
f.add(sect, key, value);
BirthPos.x -= 50f * ((BirthPos.x - aircraftPos.x) / Math.Abs(BirthPos.x - aircraftPos.x)); BirthPos.y -= 50f * ((BirthPos.y - aircraftPos.y) / Math.Abs(BirthPos.y - aircraftPos.y));
value = BirthPos.x.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.y.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.z.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat);
f.add(sect, key, value);

TmpStartPos = startPos;
TmpStartPos.x += PseudoRnd(-30f, 30f) + fRadius; TmpStartPos.y += PseudoRnd(-30f, 30f) - fRadius;
BirthPos = EmrgVehicleStartPos(TmpStartPos, startPos);

sect = ChiefName3 + "2" + "_Road";
key = "";
value = BirthPos.x.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.y.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.z.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " 0 92 5 ";
f.add(sect, key, value);
BirthPos.x -= 50f * ((BirthPos.x - aircraftPos.x) / Math.Abs(BirthPos.x - aircraftPos.x)); BirthPos.y -= 50f * ((BirthPos.y - aircraftPos.y) / Math.Abs(BirthPos.y - aircraftPos.y));
value = BirthPos.x.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.y.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.z.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat);
f.add(sect, key, value);

return f;
}


internal Point3d EmrgVehicleStartPos(Point3d startPos, Point3d endPos)
{
Point3d TmpPos = startPos;

while (((GamePlay.gpLandType(TmpPos.x, TmpPos.y) & LandTypes.WATER) != 0) )
{
TmpPos.x -= (TmpPos.x - endPos.x) / 10f;
TmpPos.y -= (TmpPos.y - endPos.y) / 10f;
};

return TmpPos;
}

internal void CheckEmrgCarOnAirport(int aircraftNumber)
{
// Проверяем есть ли машинки в аэропорту
AiGroundGroup MyCar = null;
for (int i = 0; i < CurTechCars.Count; i++)
{
if (CurTechCars[i].TechCar != null )
{
if (CurTechCars[i].TechCar.IsAlive() && CurTechCars[i].BaseAirport == CurPlanesQueue[aircraftNumber].baseAirport && (CurTechCars[i].CarType & CurPlanesQueue[aircraftNumber].State)!=0)
{
MissionLoading = false;
MyCar = CurTechCars[i].TechCar;
if ((CurTechCars[i].cur_rp == null) && (CurTechCars[i].RouteFlag == 0) && (CurTechCars[i].servPlaneNum == -1)) // если стоит без дела - отправим работать
SetEmrgCarRoute(aircraftNumber, i);
}
}
};
if ((MyCar == null) && !MissionLoading)
{
MissionLoading = true;
int ArmyPos = 0;
if (GamePlay.gpFrontExist())
{
ArmyPos = GamePlay.gpFrontArmy(CurPlanesQueue[aircraftNumber].baseAirport.Pos().x, CurPlanesQueue[aircraftNumber].baseAirport.Pos().y);
}
else { ArmyPos = CurPlanesQueue[aircraftNumber].aircraft.Army(); };
// Создаём миссию с машинками
GamePlay.gpPostMissionLoad(CreateEmrgCarMission(Cu rPlanesQueue[aircraftNumber].baseAirport.Pos(), (CurPlanesQueue[aircraftNumber].baseAirport.FieldR() / 4), ArmyPos, CurPlanesQueue[aircraftNumber].aircraft.Army(), CurPlanesQueue[aircraftNumber].aircraft.Type(),CurPlanesQueue[aircraftNumber].health, CurPlanesQueue[aircraftNumber].aircraft.Pos()));
}
return ;
}


public override void OnAircraftCrashLanded(int missionNumber, string shortName, AiAircraft aircraft)
{
base.OnAircraftCrashLanded(missionNumber, shortName, aircraft);
Timeout(5, () =>
{
aircraft.Destroy();
});
}

public override void OnAircraftLanded(int missionNumber, string shortName, AiAircraft aircraft)
{
base.OnAircraftLanded(missionNumber, shortName, aircraft);

AiAirport NearestAirport = FindNearestAirport(aircraft);
if (NearestAirport != null)
{
PlanesQueue CurPlane = new PlanesQueue(aircraft, NearestAirport, 0);
int ArmyPos = 0;
CurPlane.health = (float)aircraft.getParameter(part.ParameterTypes.M _Health, -1);
if (GamePlay.gpFrontExist())
{
ArmyPos = GamePlay.gpFrontArmy(NearestAirport.Pos().x, NearestAirport.Pos().y);
}
else { ArmyPos = aircraft.Army(); };
if (CurPlane.health < 1f)
{
CurPlane.State |= ServiceType.EMERGENCY;
CurPlane.State |= ServiceType.FIRE;
}
else if (aircraft.Army() == ArmyPos)
{
CurPlane.State |= ServiceType.FUEL;
CurPlane.State |= ServiceType.AMMO;
if (aircraft.Type() == AircraftType.Bomber) CurPlane.State |= ServiceType.BOMBS;
};
if (!(aircraft.Army() == ArmyPos)) CurPlane.State |= ServiceType.PRISONERCAPTURE;
if (!CurPlanesQueue.Contains(CurPlane))
{
CurPlanesQueue.Add(CurPlane);
CheckEmrgCarOnAirport(CurPlanesQueue.Count - 1);
}
else
{
for (int i = 0; i < CurPlanesQueue.Count; i++)
if (CurPlanesQueue[i] == CurPlane)
{
CheckEmrgCarOnAirport(i);
break;
}
}
CurPlane = null;
};
}

}

esmiol
09-12-2011, 12:10 PM
awesome :)

maybe we could soon see a script who call supply truck to come...rearm us and go :)

fearlessfrog
09-12-2011, 04:40 PM
Thanks Banks for the FindPath - yep, as I just put down the vehicle on the field I'd just do a quick A->B but that way is far better.

Ataros - Excellent script from narvy, could have done with that yesterday :)

It seems to do quite a lot, such as make sure the vehicles don't bump into the target aircraft (my ambulance was quite destructive) and dynamically spawn the vehicles in the airport dependent on the activity/closeness.

I love that on landing it can decide whether to put out a fire, re-arm you or go capture you as an enemy. Good stuff - thanks.

csThor
09-12-2011, 05:22 PM
Two questions of a curious user (who wouldn't know how to code if his life depended on it):

1.) Wouldn't it be more logical to automate the process of ambulance and firetruck rushing to a damaged aircraft?

2.) Would it be possible to derive a similar procedure for rescue launches or similar "lifeboats"? I mean the Brits had them on the coast and the germans used whatever was locally available for the same purposes. It may be window-dressing for the moment but may be of importance later on (keyword: pilot-centric campaign).

Mington
09-12-2011, 06:07 PM
1.) Wouldn't it be more logical to automate the process of ambulance and firetruck rushing to a damaged aircraft?

Exactly so, inbound to land is a damaged plane - set up to meet it with ambulance/fire engine

Trigger will be perhaps- DamagedPlane crosses some AirfieldEmergency radius - warm up ambulance and fire tender

If definitely landing, equipment approaches runway. Carefully :)

Ming

Mington
09-12-2011, 07:11 PM
It's the coolest CoD FMB moment so far for me, it is a wonder thanks naryv your AirfieldEmergency script works first time perfectly. Awesome :)

http://simhq.com/forum/ubbthreads.php/topics/3388512.html#Post3388512

Ming

fearlessfrog
09-12-2011, 08:02 PM
Two questions of a curious user (who wouldn't know how to code if his life depended on it):

1.) Wouldn't it be more logical to automate the process of ambulance and firetruck rushing to a damaged aircraft?

2.) Would it be possible to derive a similar procedure for rescue launches or similar "lifeboats"? I mean the Brits had them on the coast and the germans used whatever was locally available for the same purposes. It may be window-dressing for the moment but may be of importance later on (keyword: pilot-centric campaign).

(1) Yes, and that's what naryv's script does - it's great. Mine was far simpler and assumed that a vehicle already existed (I really just wanted to see how waypoints worked for ground units).

(2) That's a great idea and very possible.

Raggz
09-12-2011, 09:42 PM
In the radio comms there is a command where you can call the tower for emergency. Could this be used as a trigger and have ambulance and firetruck rush out to the runway?

Lookaloft
09-13-2011, 02:24 PM
Unfortunately I can’t get the Naryv script to work. I copied the script in Notepad and saved it under ANSI as an cs mission. I did this several times but each time the scriptwindow in my test singlemission says: namespace cannot directly contain members such as fields or methods. CS- 0116.
I’m not familiar with C# but the scripts developed on this Forum are very interesting and absolutely impressive. I implement them in my single missions. Application of scripts is a must for the missionbuilder: it gives this beautiful COD-game an extra demension.

Hopefully someone can help me with this Naryv script

Mington
09-13-2011, 05:54 PM
I copied the script in Notepad and saved it under ANSI

You can use Notepad++ which is recommended by the RoF team so maybe Maddox coders are using it, and perhaps save documents in the Unicode Big Endian format, again I've seen the RoF devs recommend this format - because of the Cyrillic characters probably but anyway-

Did you create a mission and then did you paste the copied text into the Scripts window?

I say this because your error was '...cannot directly contain members' - you may be doing something else with the text than pasting it in :)

Have the script-page Internet Explorer (or whatever browser) open, have the FMB running too - use Alt + Tab to switch between them to paste the text into the SCript tab edit window. Copy the script text by using the Ctrl + c trick, then in the FMB open the Scripts tab and do the Ctrl + v trick to paste the text in there. Select the block of text before hitting Ctrl + c to copy selected text to your Clipboard. Sorry if the grandmothers and eggs rule applies!

Make a simple mission with just one plane ready to roll and set it to Player pilot and so on, takeoff and damage the plane, see if the vehicles turn up at your crashed plane

Give me a PM shout if you're stuck and I'll send the mission on to you

Ming

Vengeanze
09-13-2011, 05:55 PM
This gives me wood. I'm stunned that this thread hasn't got like zillions of replies as this is solely such a friggin kewl feature.
Is this what Luthier is talking about?

Mington
09-13-2011, 06:03 PM
the Brits had them on the coast

Frog I have a possible Coastal Command sighting for Thor

The rescue aircraft Walrus in there has the designation-

Class Aircraft.WalrusMkI

Doing the legwork, Igor the whip :)

Ming

Das Attorney
09-13-2011, 07:25 PM
Thanks for the hard work and research on these scripts. Some really awesomesauce going on in this thread.

I was wondering if someone could help me though. I'm using naryv's script to dispatch ambulances etc in a MP dogfight. There's also AI planes that periodically spawn and then land. The problem is that everytime an AI plane lands, fire trucks etc will spawn and eventually lag the game out.

Is there a way of limiting the spawn to just players?

I used to do scripting for Arma 2 (sqf) and the following would be appropriate to filter out AI:

if (_x == player) then
{
// code
};

I'm not used to C# yet and don't know the syntax well at this point. How could I filter out the AI so firetrucks etc only spawn and deploy for human controlled planes?

I've looked in the examples, but I can't 'get it' yet. I know you're busy guys so even a basic example/link would get me started.

Thanks

Sokol1
09-14-2011, 02:37 AM
Unfortunately I can’t get the Naryv script to work. I copied the script in Notepad and saved it under ANSI as an cs mission.

I copy the script direct in FMB window, so when save the mission the script is save too.

http://img716.imageshack.us/img716/9747/clipboard03ra.jpg (http://imageshack.us/photo/my-images/716/clipboard03ra.jpg/)

After a ''perfect'' wheels up landing they send me winch, fuel, ammo and bombs (spend previous load before landing)! :)
Ambulance is not need. :)

Sokol1

fearlessfrog
09-14-2011, 05:02 AM
Thanks for the hard work and research on these scripts. Some really awesomesauce going on in this thread.

I was wondering if someone could help me though. I'm using naryv's script to dispatch ambulances etc in a MP dogfight. There's also AI planes that periodically spawn and then land. The problem is that everytime an AI plane lands, fire trucks etc will spawn and eventually lag the game out.

Is there a way of limiting the spawn to just players?

I used to do scripting for Arma 2 (sqf) and the following would be appropriate to filter out AI:

if (_x == player) then
{
// code
};

I'm not used to C# yet and don't know the syntax well at this point. How could I filter out the AI so firetrucks etc only spawn and deploy for human controlled planes?

I've looked in the examples, but I can't 'get it' yet. I know you're busy guys so even a basic example/link would get me started.

Thanks

On a quick peek, I think there may be a bug around line 212, i.e. this bit:


if (!CurTechCars.Contains(TmpCar))
CurTechCars.Add(TmpCar);
MissionLoading = false;


..in that it's adding new cars even if they exist as it doesn't find a match for the existing object.

Another way to do this would be an 'if (CurTechCars.count < MAX_CARS) in before the CurTechCars.Add(TmpCar)

Hopefully that's a few clues, as I'm away from a computer (typing this on a pad)

Lookaloft
09-14-2011, 12:46 PM
Mington, thanks for your detailed advice, and you too Sokol1 for your response. No grandmothers and eggs rule applied here, Ming. On the contrary I’m very happy with your suggestions, pleased as Punch as people say. Put them already into practice and all went very well now! What a terrific script this Naryv script, and what a fantastic game this Cliffs of Dover!

Ataros
09-14-2011, 05:39 PM
maybe we could soon see a script who call supply truck to come...rearm us and go :)

It is not possible in the game yet and not planned yet. Ask the devs to include it if it is really needed (in appropriate threads). Their argument is that R&R takes at least 30 minutes and no one would really want to waste that much time neither offline nor online.

fearlessfrog
09-14-2011, 08:28 PM
It is not possible in the game yet and not planned yet. Ask the devs to include it if it is really needed (in appropriate threads). Their argument is that R&R takes at least 30 minutes and no one would really want to waste that much time neither offline nor online.

One work-around for this I had a play with was to fake it by taking the existing player/position and then destroying/recreating the same type of aircraft, i.e. you get refueled and re-armed, plus could even put a delay on that too. I know it's not realistic, but a 5 minute re-arm/refuel custom script does appeal.

Ataros
09-14-2011, 08:54 PM
One work-around for this I had a play with was to fake it by taking the existing player/position and then destroying/recreating the same type of aircraft, i.e. you get refueled and re-armed, plus could even put a delay on that too. I know it's not realistic, but a 5 minute re-arm/refuel custom script does appeal.

This is the only way to do it now I am afraid.

BTW can you make the new aircraft to spawn at exactly the same position heading the same direction as the old one? If yes, the change would be hardly noticeable for a player.

fearlessfrog
09-14-2011, 10:12 PM
This is the only way to do it now I am afraid.

BTW can you make the new aircraft to spawn at exactly the same position heading the same direction as the old one? If yes, the change would be hardly noticeable for a player.

I haven't tried to fix the rotation. Looking at the API I was assuming that the 'epsilon' point and angle might set that, i.e. 'Place().Pos().epsilonEquals' but would either have to try it a bit more (or we could ask the devs?)

Anyone else dealt with a rotation angle?

Mington
09-15-2011, 11:35 AM
I demand fully-automated working traffic lights in London town and fleets of Routemasters. Civilization: Cliffs of Dover

Next Week's Demand Or The Kitten Gets It: a flocking algorithm for Heinkels

Ming :)

Das Attorney
09-15-2011, 11:01 PM
On a quick peek, I think there may be a bug around line 212, i.e. this bit:


if (!CurTechCars.Contains(TmpCar))
CurTechCars.Add(TmpCar);
MissionLoading = false;


..in that it's adding new cars even if they exist as it doesn't find a match for the existing object.

Another way to do this would be an 'if (CurTechCars.count < MAX_CARS) in before the CurTechCars.Add(TmpCar)

Hopefully that's a few clues, as I'm away from a computer (typing this on a pad)

Thanks :)

I managed to modify the script to only work on player planes by adding a condition to ignore AI planes - using !isAiControlledPlane(aircraft)

It's working for player planes only now quite nicely. :)

I'd like to have a go at adding in some code to rearm/refuel/repair. Off the top of my head, it could probably work by automatically setting fuel etc to 1 when the relevant truck completes it's last waypoint. Or when it completes it's last waypoint, then it activates the Mission menu for the player to select the appropriate emergency service. I'm not sure if these solutions would be possible as I don't know many of the scripting commands.

How can you see the API? Do you have to run kegetys -dump command or is there a different way to look at the API (in the COD folder somewhere maybe)?

EDIT: I suspect it's in the maddox.dll....

fearlessfrog
09-16-2011, 03:48 AM
The API is written using Microsoft .Net libraries, so you can examine them in things like Visual Studio (there is a free edition called Express). The 'object browser' shows the signatures and symbols of the files like game.world.dll that you reference in your project.

SNAFU
09-16-2011, 08:25 AM
Thanks :)

I managed to modify the script to only work on player planes by adding a condition to ignore AI planes - using !isAiControlledPlane(aircraft)

It's working for player planes only now quite nicely. :)



Would you be so kind and check the code below, in which I tried to modify the script according to your hint (at the end of the original script) and tell me if this is the right direction?



...
..
.
public override void OnAircraftCrashLanded(int missionNumber, string shortName, AiAircraft aircraft)
{
base.OnAircraftCrashLanded(missionNumber, shortName, aircraft);

if (!isAiControlledPlane(aircraft)) // SNAFU
{ //SNAFU

Timeout(5, () =>
{
aircraft.Destroy();
});
}

return; // SNAFU
} // SNAFU

public override void OnAircraftLanded(int missionNumber, string shortName, AiAircraft aircraft)
{
base.OnAircraftLanded(missionNumber, shortName, aircraft);

if (!isAiControlledPlane(aircraft)) // SNAFU
{ //SNAFU

AiAirport NearestAirport = FindNearestAirport(aircraft);
if (NearestAirport != null)
{
PlanesQueue CurPlane = new PlanesQueue(aircraft, NearestAirport, 0);
int ArmyPos = 0;
CurPlane.health = (float)aircraft.getParameter(part.ParameterTypes.M _Health, -1);
if (GamePlay.gpFrontExist())
{
ArmyPos = GamePlay.gpFrontArmy(NearestAirport.Pos().x, NearestAirport.Pos().y);
}
else { ArmyPos = aircraft.Army(); };
if (CurPlane.health < 1f)
{
CurPlane.State |= ServiceType.EMERGENCY;
CurPlane.State |= ServiceType.FIRE;
}
else if (aircraft.Army() == ArmyPos)
{
CurPlane.State |= ServiceType.FUEL;
CurPlane.State |= ServiceType.AMMO;
if (aircraft.Type() == AircraftType.Bomber) CurPlane.State |= ServiceType.BOMBS;
};
if (!(aircraft.Army() == ArmyPos)) CurPlane.State |= ServiceType.PRISONERCAPTURE;
if (!CurPlanesQueue.Contains(CurPlane))
{
CurPlanesQueue.Add(CurPlane);
CheckEmrgCarOnAirport(CurPlanesQueue.Count - 1);
}
else
{
for (int i = 0; i < CurPlanesQueue.Count; i++)
if (CurPlanesQueue[i] == CurPlane)
{
CheckEmrgCarOnAirport(i);
break;
}
}
CurPlane = null;
};
}
return; // SNAFU
} // SNAFU
}

Das Attorney
09-16-2011, 06:46 PM
Yes that's looking good :)

I haven't tested your one but it should work. I left in the condition to check for an airfield and added the condition to check for player planes:

public override void OnAircraftLanded(int missionNumber, string shortName, AiAircraft aircraft)
{
base.OnAircraftLanded(missionNumber, shortName, aircraft);

AiAirport NearestAirport = FindNearestAirport(aircraft);
//if (NearestAirport != null)

if ((NearestAirport != null) && (!isAiControlledPlane(aircraft)))
{
PlanesQueue CurPlane = new PlanesQueue(aircraft, NearestAirport, 0);
int ArmyPos = 0;
CurPlane.health = (float)aircraft.getParameter(part.ParameterTypes.M _Health, -1);
if (GamePlay.gpFrontExist())
{
ArmyPos = GamePlay.gpFrontArmy(NearestAirport.Pos().x, NearestAirport.Pos().y);
}
else { ArmyPos = aircraft.Army(); };
if (CurPlane.health < 1f)
{
CurPlane.State |= ServiceType.EMERGENCY;
CurPlane.State |= ServiceType.FIRE;
}
else if (aircraft.Army() == ArmyPos)
{
CurPlane.State |= ServiceType.FUEL;
CurPlane.State |= ServiceType.AMMO;
if (aircraft.Type() == AircraftType.Bomber) CurPlane.State |= ServiceType.BOMBS;
};
if (!(aircraft.Army() == ArmyPos)) CurPlane.State |= ServiceType.PRISONERCAPTURE;
if (!CurPlanesQueue.Contains(CurPlane))
{
CurPlanesQueue.Add(CurPlane);
CheckEmrgCarOnAirport(CurPlanesQueue.Count - 1);
}
else
{
for (int i = 0; i < CurPlanesQueue.Count; i++)
if (CurPlanesQueue[i] == CurPlane)
{
CheckEmrgCarOnAirport(i);
break;
}
}
CurPlane = null;
};

}

There's probably a set of unnecessary brackets in there, but it works ok. I haven't tested on a dedicated server, just a hosted one.

If you've got any hints for setting up a repair script, please share :) I can't find the scripting commands in the beta code, so any pointers would be welcome!

I'm hoping that we can get a reference page for scripting commands on a wiki, much like this one:

http://community.bistudio.com/wiki/Category:Scripting_Commands_ArmA2

That would be really useful.

Ataros
09-17-2011, 10:06 AM
Oh and Ataros this is latest update with install instructions back in the mix.
I know you don't like the vehicles spawning straight away. But as the game goes one side does begin to win over the other and the randomness is good.

feel free to offer a tick time code to put in for armour spawn after destroyed or on new load mission for armour.

Thank you very much!
Regarding spawn delay I have a concern: what happens when a marker is captured and then a delayed spawn of a defending tank group occurs? Probably the marker color changes again and it is recaptured by the defending side?

Gerbil Maximus
09-22-2011, 11:21 AM
Hey all I'm new to Cliffs FMB but long time lurker here and decided to register as this script has me stumped.

So I have made my MP Dogfight Mission, its ready and working. So can someone give me a quite instruction on how to put this ambulance script into my game (which ever script is best). Sorry if its real simple by the way but i cant understand how to.

Thank you

Gerbil M

Ataros
09-23-2011, 07:50 PM
1. Copy and paste the script to notepad and save as a .cs file (not .cs.txt !!!) The file must have the same name as the mission .mis file.

2. Put the .cs file in the same directory as .mis file.

3. Run the mission.

Gerbil Maximus
09-24-2011, 12:38 PM
Ah rgr i see thank you Ataros :) will test soon so this should work for every airfield in my MP map?

Mington
09-25-2011, 11:26 AM
Gerbil you can also add the script to your mission by- after the copy operation Ataros speaks of above, if your FMB is running and you are looking at your mission, you can go to the Script tab and paste the text in there to create the script. This can be easier than finding the mission on your computer :)

(I tell you this to point out that CoD is Windows-friendly, standard Windows operations like Ctrl + v work)

Ming

Gerbil Maximus
09-27-2011, 09:09 AM
Rgr Thank you

AKA_Scorp
01-17-2012, 06:22 PM
One work-around for this I had a play with was to fake it by taking the existing player/position and then destroying/recreating the same type of aircraft, i.e. you get refueled and re-armed, plus could even put a delay on that too. I know it's not realistic, but a 5 minute re-arm/refuel custom script does appeal.


Probably seems like a dumb question but what is the command to spawn an aircraft as described above.

Thanks

Ataros
01-18-2012, 07:40 AM
Probably seems like a dumb question but what is the command to spawn an aircraft as described above.

Thanks

Load a ready submission which contains an aircraft or dynamical create a submission file with a script and then load it. Check out how naryv creates tanks in his examples attached in a sticky thread above.

AKA_Scorp
01-18-2012, 06:07 PM
Load a ready submission which contains an aircraft or dynamical create a submission file with a script and then load it. Check out how naryv creates tanks in his examples attached in a sticky thread above.

OK, I can see the principle of the section file and how to create a sub mission from script. I'm sure I could put in static objects, chiefs and AI flights but how do I force a human pilot into an aircraft at a specific coordinate?

Thanks

FG28_Kodiak
01-18-2012, 06:12 PM
Player Class: PlaceEnter(Actor, Placeindex)
example:
player.PlaceEnter(Aircraft, 0);

AKA_Scorp
01-18-2012, 06:41 PM
Player Class: PlaceEnter(Actor, Placeindex)
example:
player.PlaceEnter(Aircraft, 0);

Thanks,

flug32
05-22-2016, 07:01 AM
Thanks to everyone who posted about the ambulance script on this thread. I've worked on an updated version of it that is designed to work in multiuser servers (though it should work offline just as well).

- Vehicles spawn in along with you & then drive off (ie, your service vehicles just finished with getting your a/c ready, then they drive off)

- When you land (full stop landing--putting chocks in will make sure) the fuel & repair trucks will come trundling up pretty soon.

- Whenever you crash, even just borking up a takeoff & damaging your propeller & undercarriage, you'll soon be visited by an ambulance and a fire truck. Even if you crash out in the middle of nowhere. Only exception would be in water--they if close to the shore the emergency vehicles will do their best & drive right up to the shore. (Rescue boats will hae to come in a future version I guess. Someone to pick you up when you parachute in could be a future improvement as well.)

- Vehicles mostly avoid you but if you hit them they WILL damage you. So you have to look sharp & avoid.

- Vehicles travel to & from Spawn Points (ie, "Birthplace" in the .mis files) preferentially. They always travel more or less directly between the target aircraft & an area immediately around the spawn point .This gives you general control over where the vehicles appear & where they travel to/park just by moving the spawn point at your airfield in FMB.

- I couldn't get the "reuse old vehicles at the same airport" code to work at all, so this simply spawns in new vehicles every time & then spawns them out after they are no longer needed. It seems to work fine so far.

-You're welcome to use as is or adapt for use in your own scripts, missions, servers, etc.

- It's designed to be a painless plug-in to any existing mission. It's a self-standing mission, so you just put it in your mission folder & then load it as a sub-mission during OnBattleStarted().

ZIP file with script & mission files, sample code, instructions all in one (http://brenthugh.com/twc/auto-generate-vehicles-mission-script-add-in-2016-05-22.zip)

FILE: auto-generate-vehicles.cs

<see separate message>

The mission file is just a stub and you don't need anything in it except the headers (and maybe not even those--I haven't really experimented). It just allows your main .cs file to load the auto-vehicle-generate.cs neatly as a sub-mission. However I like to keep a minimal .mis file in place with a map, weather, etc. because then you can edit & run it easily in FMB. That is helpful for testing. Here is my sample .mis file:

FILE: auto-generate-vehicles.mis


[PARTS]
core.100
bob.100
[MAIN]
MAP Land$Online_Volcanic_Island
BattleArea 6000 1700 21000 38000 1000
TIME 12
Army 1 gb
Army 2 de
WeatherIndex 0
CloudsHeight 1000
BreezeActivity 0
ThermalActivity 0
[GlobalWind_0]
Power 0.000 0.000 0.000
BottomBound 0.00
TopBound 1500.00
GustPower 5
GustAngle 0
[splines]
[CustomChiefs]
[Stationary]
[Buildings]
[BuildingsLinks]
[Airdromes]


In the MAIN mission script file you have something like this:
FILE: main.cs
public override void OnBattleStarted()
{
base.OnBattleStarted();
ReadInitialSubmissions(MISSION_ID + "-auto-generate-vehicles", 0, 0);
//etc . . . your code

}

}

Here is the ReadInitialSubmission script I use:


public void ReadInitialSubmissions(string filenameID, int timespread=60, int wait=0)
{

string MISSION_ID = "M001";

string USER_DOC_PATH = Environment.GetFolderPath(Environment.SpecialFolde r.MyDocuments); // DO NOT CHANGE
string CLOD_PATH = USER_DOC_PATH + @"/1C SoftClub/il-2 sturmovik cliffs of dover - MOD/"; // DO NOT CHANGE
string FILE_PATH = @"missions/Multi/MyMissionFolder"; // mission install directory (CHANGE AS NEEDED - your sub-mission .mis and .cs files should be in this directory)

List<string> InitSubMissions = GetFilenamesFromDirectory(CLOD_PATH + FILE_PATH, filenameID); // gets .mis files with with word filenameID in them

DebugAndLog ("Debug: Loading " + InitSubMissions.Count + " missions to load. " + filenameID + " " + CLOD_PATH + FILE_PATH);
foreach (string s in InitSubMissions)
{
//Distribute loading of initial sub-missions over the first timespread seconds
//If you make each sub-mission small enough it will load without a noticeable stutter
//If they are large & make a noticeable stutter, probably best to load them all very near the beginning of the mission
//so that all the stutters happen at that point
if ((timespread==0) && (wait==0)) {
GamePlay.gpPostMissionLoad(s);
DebugAndLog( s + " file Loaded");
} else {
Timeout(wait + random.Next(timespread), () => {

GamePlay.gpPostMissionLoad(s);

});
}
}

}



You need to be sure that event triggers are enabled--and enabled in the right place. Similar code is already in auto-vehicle-generate.cs but I think perhaps you also need it in your main mission cs file. (In any event, if you've been trying to use triggers and having trouble, not having this flag set correctly, or setting it to late in the startup process, might be your problem.)


//////////////////////////////////////////////////////////////////////////////////////////////////

//Listen to events of every mission
public override void Init(maddox.game.ABattle battle, int missionNumber)
{
base.Init(battle, missionNumber);
MissionNumberListener = -1; //Listen to events of every mission
//This is what allows you to catch all the OnTookOff, OnAircraftDamaged, and other similar events. Vitally important to make this work!
//If we load missions as sub-missions, as we often do, it is vital to have this in Init, not in "onbattlestarted" or some other place where it may never be detected or triggered if this sub-mission isn't loaded at the very start.
}

//////////////////////////////////////////////////////////////////////////////////////////////////


Last, you must have lines like the following in the [rts] section of both your conf.ini and confs.ini file.

I believe that conf.ini pertains to your regular Launcher.exe process and confs.ini applies if you are running "Launcher.exe -server" as a dedicated server. Note that you will need to exit Launcher.exe, then edit and save conf.ini & confs.ini, then re-start Launcher.exe. If you don't follow this process, your conf.ini and/or confs.ini will be overwritten when Launcher.exe exits.

[rts]
scriptAppDomain=0
;avoids the dreaded serialization runtime error when running server or testing missions in FMB
;per http://forum.1cpublishing.eu/showthread.php?t=34797

Lastly, thanks to everyone who has been posting here and in other forums. The code above includes tons of techniques and snippets borrowed from here and there. All horrible hacks and terrible formatting are my own :-)

Again: ZIP file with script & mission files, sample code, instructions all in one (http://brenthugh.com/twc/auto-generate-vehicles-mission-script-add-in-2016-05-22.zip)

flug32
05-22-2016, 07:11 AM
FILE: auto-generate-vehicles.cs

<see separate message>
Here is part 1/2 of the actual .cs code for FILE: auto-generate-vehicles.cs

//
//Various emergency & service cars script created by posters at http://forum.1cpublishing.eu/showthread.php?t=26112 and by naryv
//Hacked extensively by flug

//$reference parts/core/Strategy.dll
//$reference parts/core/gamePlay.dll
//$reference System.Core.dll
using System;
//using System.Core;
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;

public class Mission : AMission
{

public bool DEBUG=false;
public int VEHICLE_LIFE_SEC = 120;
public double CAR_POS_RADIUS = 80; //distance vehicles will be positioned from the center point of the Birthplace, Airfield, etc where cars are positioned
public int TICKS_PER_MINUTE=1986; //empirically, based on a timeout test. This is approximate & varies slightly.

//for landing or crash, they start SPAWN_START_DISTANCE_M away from the plane in the direction of the nearest BirthPlace or Airport point. They end SPAWN_END_DISTANCE_M away from the a/c. (Distances are approx., various randomness & functions added on top of these values.)
public int SPAWN_START_DISTANCE_M = 100; //how far away they start from the a/c when approaching after landing
public int SPAWN_END_DISTANCE_M = 20; //how close they approach you upon (eg) landing

//For spawn-in, the vehicles start this close to the a/c and proceed to
// the birthplace or airport point.
public int SPAWN_START_DISTANCE_REVERSE_M = 12; //how close they start when spawning in onPlaceEnter


private List<AiActor> actorPlaceEnterList; //HashSet doesn't work for some reason; it would be a better solution
private List<AiAircraft> aircraftDamagedList;
private List<AiAircraft> aircraftActiveList;

maddox.game.ABattle Battle;

public Mission () {
//HashSet<int> evenNumbers = new HashSet<int>();
actorPlaceEnterList = new List<AiActor>();
aircraftDamagedList = new List<AiAircraft>();
aircraftActiveList = new List<AiAircraft>();

}
//Listen to events of every mission
public override void Init(maddox.game.ABattle battle, int missionNumber)
{
base.Init(battle, missionNumber);
Battle=battle;
MissionNumberListener = -1; //Listen to events of every mission
//This is what allows you to catch all the OnTookOff, OnAircraftDamaged, and other similar events. Vitally important to make this mission/cs file work!
//If we load missions as sub-missions, as we often do, it is vital to have this in Init, not in "onbattlestarted" or some other place where it may never be detected or triggered if this sub-mission isn't loaded at the very start.

if (DEBUG) GamePlay.gpLogServer(null, ".Net framework: " + Environment.Version, new object[] { });

}


public override void OnBattleStarted()
{
base.OnBattleStarted();
//MissionNumberListener = -1;
}

Random rnd = new Random();


[Flags]
internal enum ServiceType // ??? ????????????? ???????
//note that all these types don't work; the actual type is determined by
//createemrgcarmission depending on a/c type, army, and a few other things.
//So it's not really determined by the settings in the curTechCar.CarType field
{
NONE = 0,
EMERGENCY = 1,
FIRE = 2,
FUEL = 4,
AMMO = 8,
BOMBS = 16,
PRISONERCAPTURE = 32,
SPAWNIN = 64
}


//Like AiActor or AiBirthplace but only has .Name() & .Loc()
internal class BasePos
{
internal string _Name;
internal Point3d _Pos;

public BasePos (string name, Point3d pos)
{
if (name!=null)
this._Name=name;
else this._Name="";
//if (pos!=null)
this._Pos=pos;
//else this.Pos=new Point3d(0,0,0);
}

public BasePos (BasePos bp)
{
this._Name=bp.Name();
this._Pos=bp.Pos();
Console.WriteLine ("BasePos inited: " + this.ToString ("F2"));
}

//a default constructor . . .
public BasePos (object o=null)
{
this._Name="";
this._Pos=new Point3d(0,0,0);
}

public string Name(string name=null) {

if (name==null) return this._Name;
else {
this._Name=name;
return null;
}

}

public Point3d Pos(Point3d pos) {
this._Pos=pos;
return this._Pos;
}

public Point3d Pos() {
return this._Pos;
}
public string ToString (string format){
return _Name + " "
+ _Pos.x.ToString( format ) + " "
+ _Pos.y.ToString( format ) + " "
+ _Pos.z.ToString( format );

}

}

internal class TechCars
{
internal AiGroundGroup TechCar { get; set; }
internal BasePos basePos { get; set; }
internal IRecalcPathParams cur_rp { get; set; }
internal int RouteFlag = 0;
internal int cartype = 0;
internal int servPlaneNum = -1;
internal int MAX_CARS = 100;
internal ServiceType CarType { get { return (ServiceType)cartype; } set { cartype = (int)value; } }

public TechCars(AiGroundGroup car, BasePos airoport, IRecalcPathParams rp)
{
this.TechCar = car;
this.basePos = airoport;
this.cur_rp = rp;

Console.WriteLine("TechCars created. basePos: " + this.basePos.ToString("F2"));
}

}

internal class PlanesQueue {
internal AiAircraft aircraft { get; set; }
internal BasePos basePos { get; set; }
internal int state = 0;
internal ServiceType State { get { return (ServiceType)state; } set { state = (int)value; } }
internal int Lifetime = 0;
internal float health = 1;
public PlanesQueue(AiAircraft aircraft, BasePos basePos, int state)
{
this.aircraft = aircraft;
this.basePos = basePos as BasePos;
this.state = state;
}
}

internal List<TechCars> CurTechCars = new List<TechCars>();
internal List<PlanesQueue> CurPlanesQueue = new List<PlanesQueue>();
TechCars TmpCar = null;
bool MissionLoading = false;
int MissionLoadingAircraftNumber = -1;

internal double PseudoRnd(double MinValue, double MaxValue)
{
return rnd.NextDouble() * (MaxValue - MinValue) + MinValue;
}



public override void OnActorTaskCompleted(int missionNumber, string shortName, AiActor actor)
{
base.OnActorTaskCompleted(missionNumber, shortName, actor);
if (DEBUG) GamePlay.gpLogServer(null, "OnActorTaskComplete", new object[] { });

AiActor ai_actor = actor as AiActor;
if (ai_actor != null)
{
if (ai_actor is AiGroundGroup)
for (int i = 0; i < CurTechCars.Count; i++) // ???? ????????????? ??????? ??????? ?? ?????????????? ????????, ????????? ?? ????????????
{
if (CurTechCars[i].TechCar == ai_actor as AiGroundGroup) {
//if (CurTechCars[i].RouteFlag == 1)
TechCars car = CurTechCars[i] as TechCars;
if (DEBUG) GamePlay.gpLogServer(null, "OnActorTaskComplete - ending plane service for " + i.ToString() + " in 120 sec.", new object[] { });
//this is basically to ensure that AI objects don't just hang around indefinitely when their tasks are done.
//In normal behavior, they may complete several tasks in the course of moving abou the airport, so we don't just want
//to destroy them immediately when task is done
Timeout(VEHICLE_LIFE_SEC, () =>
{
if (DEBUG) GamePlay.gpLogServer(null, "OnActorTaskComplete - ending plane service for " + i.ToString() + " now", new object[] { });
EndPlaneService(car, ai_actor as AiGroundGroup);

});
}


//we're just destroying them @ this point
// else
// CheckNotServicedPlanes(i);
};
}
}


internal void CheckNotServicedPlanes(int techCarIndex)
{
for (int j = 0; j < CurPlanesQueue.Count; j++)
{
if (CurTechCars[techCarIndex].TechCar.IsAlive() && (CurPlanesQueue[j].basePos == CurTechCars[techCarIndex].basePos) && ((CurTechCars[techCarIndex].CarType & CurPlanesQueue[j].State) != 0) && (CurTechCars[techCarIndex].servPlaneNum == -1))
{
if (SetEmrgCarRoute(j, techCarIndex)) // ?????????? ??????? ??????????? ????????? ???????
{
return;
}
}
}
}

//Removes the ground vehicle from the CurTechCars list & also destroys the AI object
//We call it with the List item (not the index) because the index can change between call & execution, esp. if call via a timeout, which is common
//We also include the TechCar field, (an AiGroundGroup) because sometimes the List item can be destroyed but the actual Ai Airgroup is still floating around undead
//
internal void EndPlaneService(TechCars tC, AiGroundGroup ground=null)
{
try
{
if (DEBUG) GamePlay.gpLogServer(null, "EndPlaneService/despawning now", new object[] { });
if (DEBUG) GamePlay.gpLogServer(null, "EndPlaneService/despawning " + tC.servPlaneNum.ToString(), new object[] { });
if (tC != null) {
if (DEBUG) GamePlay.gpLogServer(null, " Number of objects: " + tC.TechCar.GetItems().Length, new object[] { });
//if (CurTechCars[techCarIndex].cur_rp == null) return;
tC.cur_rp = null; // ?????????? ???????

//Just destroy the ground items at this point.
if (tC.TechCar.GetItems() != null && tC.TechCar.GetItems().Length > 0)
{
if (DEBUG) GamePlay.gpLogServer(null, "EndPlaneService/despawning 1 ", new object[] { });
foreach (AiActor actor in tC.TechCar.GetItems())
{
if (DEBUG) GamePlay.gpLogServer(null, "EndPlaneService/despawning 2 " , new object[] { });
(actor as AiGroundActor).Destroy();
}
}

CurTechCars.Remove(tC);
}

if (ground != null) {
if (DEBUG) GamePlay.gpLogServer(null, "EndPlaneService/despawning 3 ", new object[] { });
foreach (AiActor actor in ground.GetItems())
{
if (DEBUG) GamePlay.gpLogServer(null, "EndPlaneService/despawning 4 " , new object[] { });
(actor as AiGroundActor).Destroy();
}

}

}
catch (Exception e) {System.Console.WriteLine (e.ToString());}
}


internal bool MoveFromRWay(int carNum)
{
bool result = false;
if (DEBUG) GamePlay.gpLogServer(null, "Removing aircraft from runway at " + CurTechCars[carNum].basePos.Name(), new object[] { });
if ((GamePlay.gpLandType(CurTechCars[carNum].TechCar.Pos().x, CurTechCars[carNum].TechCar.Pos().y) & LandTypes.ROAD) == 0)
return result;

Point3d TmpPos = CurTechCars[carNum].TechCar.Pos();
while (((GamePlay.gpLandType(TmpPos.x, TmpPos.y) & LandTypes.ROAD) != 0))
{
TmpPos.x += 10f;
TmpPos.y += 10f;
};
Point2d EmgCarStart, EmgCarFinish;
EmgCarStart.x = CurTechCars[carNum].TechCar.Pos().x; EmgCarStart.y = CurTechCars[carNum].TechCar.Pos().y;
EmgCarFinish.x = TmpPos.x; EmgCarFinish.y = TmpPos.y;
CurTechCars[carNum].servPlaneNum = -1;
CurTechCars[carNum].RouteFlag = 0;
CurTechCars[carNum].cur_rp = null;
CurTechCars[carNum].cur_rp = GamePlay.gpFindPath(EmgCarStart, 10f, EmgCarFinish, 10f, PathType.GROUND, CurTechCars[carNum].TechCar.Army());

result = true;
return result;
}


public bool SetEmrgCarRoute(int aircraftNumber,int carNum)
{
bool result = false;
if (DEBUG) GamePlay.gpLogServer(null, "Setting a Car Route "+aircraftNumber.ToString() + " " + carNum.ToString() + " at " + CurTechCars[carNum].basePos.Name() , new object[] { });
if (CurTechCars[carNum].TechCar != null)
{
CurTechCars[carNum].servPlaneNum = aircraftNumber; // ????????????? ????? ?????????????? ????????
if (CurTechCars[carNum].cur_rp == null)
{
Point2d EmgCarStart, EmgCarFinish, LandedPos;
LandedPos.x = CurPlanesQueue[aircraftNumber].aircraft.Pos().x; LandedPos.y = CurPlanesQueue[aircraftNumber].aircraft.Pos().y;
int Sign = ((carNum % 2) == 0) ? 2 : -2;
EmgCarStart.x = CurTechCars[carNum].TechCar.Pos().x; EmgCarStart.y = CurTechCars[carNum].TechCar.Pos().y;

//Drive the car from where it is to the point located in the direction of the aircraft position but 20 meters short of it.
double disx, disy;
disx=Math.Abs(EmgCarStart.x - LandedPos.x) - 20;
if (disx<10) disx=20;
disy=Math.Abs(EmgCarStart.y - EmgCarStart.y) - 20;
if (disy<10) disy=20;

EmgCarFinish.x = EmgCarStart.x - disx * ((EmgCarStart.x - LandedPos.x) / Math.Abs(EmgCarStart.x - LandedPos.x)); EmgCarFinish.y = EmgCarStart.x - disy * ((EmgCarStart.y - LandedPos.y) / Math.Abs(EmgCarStart.y - LandedPos.y));

//EmgCarFinish.x = LandedPos.x - PseudoRnd(0f, 1f) * ((LandedPos.x - EmgCarStart.x) / (Math.Abs(LandedPos.x - EmgCarStart.x))) - Sign;
//EmgCarFinish.y = LandedPos.y - PseudoRnd(0f, 1f) * ((LandedPos.y - EmgCarStart.y) / (Math.Abs(LandedPos.y - EmgCarStart.y))) - Sign;

//For spawn-in, we want the cars to start in close to the a/c & drive away

CurTechCars[carNum].cur_rp = GamePlay.gpFindPath(EmgCarStart, 15f, EmgCarFinish, 15f, PathType.GROUND, CurTechCars[carNum].TechCar.Army());
if (DEBUG) GamePlay.gpLogServer(null, "Setting a Car Route "+aircraftNumber.ToString() + " " + carNum.ToString() + " " + EmgCarStart.ToString() + " to " + EmgCarFinish.ToString() + " at " + CurTechCars[carNum].basePos.Name() , new object[] { });
result = true;
}
}
return result;
}


public override void OnMissionLoaded(int missionNumber)
{
base.OnMissionLoaded(missionNumber);
if (missionNumber > 0) //whenever a new mission loads, this slurps up any matching groundcars into the curTechCars list so they can be manipulated etc
//if (missionNumber==MissionNumber ) // important check!
{
if (DEBUG) GamePlay.gpLogServer(null, "Starting vehicle sub-mission loaded", new object[] { });
List<string> CarTypes = new List<string>();
CarTypes.Add(":0_Chief_Emrg_");
CarTypes.Add(":0_Chief_Fire_");
CarTypes.Add(":0_Chief_Fuel_");
CarTypes.Add(":0_Chief_Ammo_");
CarTypes.Add(":0_Chief_Bomb_");
CarTypes.Add(":0_Chief_Prisoner_");

AiGroundGroup MyCar = null;

for (int i = 0; i < 3; i++)
{
for (int j = 0; j < CarTypes.Count; j++)
{
MyCar = GamePlay.gpActorByName(missionNumber.ToString() + CarTypes[j] + i.ToString()) as AiGroundGroup;

//if (DEBUG) GamePlay.gpLogServer(null, "Creating groundcar group for " +missionNumber.ToString() + CarTypes[j] + i.ToString(), new object[] { });
if (MyCar != null)
{
TmpCar = new TechCars(MyCar, FindNearestAirport(MyCar), null);
if (DEBUG) GamePlay.gpLogServer(null, "Creating groundcar group at " + FindNearestAirport(MyCar).Name() + " " + MissionLoadingAircraftNumber.ToString(), new object[] { });
TmpCar.CarType = (ServiceType)(1 << j);
TmpCar.cur_rp = null;
TmpCar.servPlaneNum=MissionLoadingAircraftNumber;
if (!CurTechCars.Contains(TmpCar))
CurTechCars.Add(TmpCar);

//if (CurTechCars.count < MAX_CARS) CurTechCars.Add(TmpCar);
//These things are unruly, so we're setting a max life on them.
Timeout(2*VEHICLE_LIFE_SEC, () =>
{
(MyCar as AiGroundActor).Destroy();
});



MissionLoading = false;
};
}
}
}
}


public override void OnTickGame() {
base.OnTickGame();
//try {
if ( (Time.tickCounter()) == 0) {
// if (DEBUG) GamePlay.gpLogServer(null, "Ground vehicles started ", new object[] { });

}

if ((Time.tickCounter() % (TICKS_PER_MINUTE/6)) == 12 ) {

checkForStoppedAircraft(aircraftActiveList);
}

if (Time.tickCounter() % 64 == 0)
{
//if (DEBUG) GamePlay.gpLogServer(null, "Ground vehicles continues . . . ", new object[] { });
for (int i = 0; i < CurPlanesQueue.Count; i++)
{
CurPlanesQueue[i].Lifetime++;
if (DEBUG) GamePlay.gpLogServer(null, "Lifetime: " + CurPlanesQueue[i].Lifetime, new object[] { });
if ((CurPlanesQueue[i].State == ServiceType.NONE) || (CurPlanesQueue[i].aircraft == null) || (CurPlanesQueue[i].Lifetime > (int)((double)VEHICLE_LIFE_SEC*TICKS_PER_MINUTE/64/60)))
{
foreach ( TechCars car in CurTechCars ) //don't use a for count/index loop here as we are destroying some of the objects mid-loop . . . arghh
{

if (car.servPlaneNum == i) {
if (DEBUG) GamePlay.gpLogServer(null, "Removing ground car for plane " + car.servPlaneNum + " " + car.CarType + " in 5 seconds", new object[] { });
//EndPlaneService(j);
Timeout ( 5f, () => { EndPlaneService(car, car.TechCar); });

}
}
CurPlanesQueue.RemoveAt(i);
}
};

foreach ( TechCars car in CurTechCars ) // (int i = 0; i < CurTechCars.Count; i++) //again ix-nay on the loop-for-ay . . . .
{
if (DEBUG) GamePlay.gpLogServer(null, "Ground car at " + car.basePos.Name(), new object[] { });
//TechCars car = CurTechCars[i];
if ((car.TechCar != null && car.cur_rp != null) && (car.cur_rp.State == RecalcPathState.SUCCESS) )
{
if (car.TechCar.IsAlive()) // && (car.RouteFlag == 0)) // && (car.servPlaneNum != -1))
{
car.RouteFlag = 1;
car.cur_rp.Path[0].P.x = car.TechCar.Pos().x; car.cur_rp.Path[0].P.y = car.TechCar.Pos().y;
car.TechCar.SetWay(car.cur_rp.Path);
//if (car.servPlaneNum != -1) car.RouteFlag = 0;
}

//The code below avoids the current plane, right? But, I'm worried about these ground vehicles hitting **all the other planes** that might be about this particular airport . . . . maybe some fixup needed
double Dist = Math.Sqrt((car.cur_rp.Path[car.cur_rp.Path.Length - 1].P.x - car.TechCar.Pos().x) * (car.cur_rp.Path[car.cur_rp.Path.Length - 1].P.x - car.TechCar.Pos().x) + (car.cur_rp.Path[car.cur_rp.Path.Length - 1].P.y - car.TechCar.Pos().y) * (car.cur_rp.Path[car.cur_rp.Path.Length - 1].P.y - car.TechCar.Pos().y));
if (car.servPlaneNum != -1)
{
if (Dist < ((CurPlanesQueue[car.servPlaneNum].aircraft.Type() == AircraftType.Bomber) ? 20f : 10f))
//EndPlaneService(i);
EndPlaneService(car, car.TechCar);
}
else if (Dist < 15f)
{
//EndPlaneService(i);
EndPlaneService(car, car.TechCar);
}
}
if ((car.cur_rp == null) && (car.RouteFlag == 0) && (car.servPlaneNum != -1))
{
//EndPlaneService(car, car.TechCar);
};
if (car.servPlaneNum == -1 || car.TechCar == null)
//EndPlaneService(i); //Once it is no longer serving a plane, we just zap it.
EndPlaneService(car, car.TechCar);
};
}

//} catch (Exception e) {System.Console.WriteLine (e.ToString());}
}





internal BasePos FindNearestAirport(AiActor actor)
{
try
{
if (actor==null) return null;
Point3d pd = actor.Pos();
if (DEBUG) GamePlay.gpLogServer(null, "Checking airport " + actor.Name(), new object[] { });
return FindNearestAirport(pd) as BasePos;
}
catch (Exception e) {System.Console.WriteLine (e.ToString()); BasePos ret3=null; return ret3; }

}

internal BasePos FindNearestAirport(Point3d pd)
{
try{
AiActor aMin = null;
AiBirthPlace aMinB = null;
double d2Min = 0;
BasePos ret= new BasePos ();
BasePos ret2=new BasePos ();
Point3d retpd;

//If we find a birthplace (ie, spawnpoint) closer than 2km we return that
//otherwise we'll search all airports for something closer
//And . . AiBirthPlace & AiAirport & AiActor are ALMOST the same thing but then again not quite so we have to dance a bit.

aMinB=FindNearestBirthplace(pd);
//if (DEBUG) GamePlay.gpLogServer(null, "Checking airport (Birthplace) found " + aMinB.Name() + " " + aMinB.Pos().distance(ref pd).ToString("F0"), new object[] { });
//if (1==0 && aMinB!= null) {
if (aMinB!= null) {

d2Min=aMinB.Pos().distance(ref pd);
//if (DEBUG) GamePlay.gpLogServer(null, "Checking airport (Birthplace) found " + aMinB.Name() + " " + aMinB.Pos().distance(ref pd).ToString("F0")
//+ " " + aMinB.Pos().ToString(), new object[] { });

if (d2Min<2000) {
retpd=aMinB.Pos();
if (retpd.z==0) retpd.z = pd.z; //BirthPlaces usu. have elevation 0 which makes the ai route finder die horribly
ret= new BasePos (aMinB.Name(), retpd);
return ret;
}
}

if (DEBUG) GamePlay.gpLogServer(null, "Checking airport (Birthplace) NOfound " + d2Min.ToString("F0"), new object[] { });

int n = GamePlay.gpAirports().Length;
for (int i = 0; i < n; i++)
{
AiActor a = (AiActor)GamePlay.gpAirports()[i];


if (a==null) continue;
if (!a.IsAlive()) continue;

//if (DEBUG) GamePlay.gpLogServer(null, "Checking airport " + a.Name(), new object[] { });
Point3d pp;
pp = a.Pos();
pd.z = pp.z;
double d2 = pd.distanceSquared(ref pp);
if ((aMin == null) || (d2 < d2Min) )
{
aMin = a;
d2Min = d2;
}
}
if (DEBUG) GamePlay.gpLogServer(null, "CAirport Found: " + aMin.Name() + " " + aMin.Pos().ToString() + " dist " + d2Min.ToString("F2"), new object[] { });

//Hmm, with our new scheme it doesn't really matter if aMin is very
//distant or what. The cars always start relatively close to the a/c
//and **in the direction of** the airport, but not *at* the airport
//if (d2Min > 2250000.0)
// aMin = null;

//return aMin as BasePos

if (aMin != null) ret2= new BasePos (aMin.Name(), aMin.Pos());
if (DEBUG) GamePlay.gpLogServer(null, "CAirport Returning: " + ret2.Name() + " " + ret2.Pos().ToString() + " dist " + d2Min.ToString("F2") + " " + ret2.ToString("F0"), new object[] { });
return ret2;
}
catch (Exception e) {System.Console.WriteLine (e.ToString()); BasePos ret3=null; return ret3; }
}

public AiBirthPlace GetBirthPlaceByName(string birthPlaceName)
{
foreach (AiBirthPlace bp in GamePlay.gpBirthPlaces())
{
if (DEBUG) GamePlay.gpLogServer(null, "Checking airport " + bp.Name(), new object[] { });
if (bp.Name() == birthPlaceName)
return bp;
}

return null;
}


public AiBirthPlace FindNearestBirthplace(AiActor actor)
{
//AiBirthPlace nearestBirthplace = null;
//AiBirthPlace[] birthPlaces = GamePlay.gpBirthPlaces();

Point3d pos = actor.Pos();

return FindNearestBirthplace(pos);
}

public AiBirthPlace FindNearestBirthplace (Point3d pos)
{
AiBirthPlace nearestBirthplace = null;
AiBirthPlace[] birthPlaces = GamePlay.gpBirthPlaces();

if (birthPlaces != null)
{
foreach (AiBirthPlace airport in birthPlaces)
{
if (nearestBirthplace != null)
{
//if (DEBUG) GamePlay.gpLogServer(null, "Checking airport " + airport.Name() + " "
// + airport.Pos().distance(ref pos).ToString("F0"), new object[] { });
if (nearestBirthplace.Pos().distance(ref pos) > airport.Pos().distance(ref pos))
nearestBirthplace = airport;
}
else nearestBirthplace = airport;
}
}
//AiActor ret=new AiActor();
//ret.Pos( nearestBirthplace.Pos());
//ret.Name(nearestBirthplace.Name());
if (DEBUG) GamePlay.gpLogServer(null, "Checking airport FOUND" + nearestBirthplace.Name() + " "
+ nearestBirthplace.Pos().distance(ref pos).ToString("F0"), new object[] { });
return nearestBirthplace;
}
//CONTINUED IN PART 2!


CODE CONTINUED IN PART 2!

Note that I trimmed down some comments, debugging code, etc to make it fit on the forum better. Full code is in the
ZIP file with script & mission files, sample code, instructions all in one (http://brenthugh.com/twc/auto-generate-vehicles-mission-script-add-in-2016-05-22.zip)

flug32
05-22-2016, 07:14 AM
FILE: auto-generate-vehicles.cs
CODE CONTINUED IN PART 2!

And, here is part 2/2 of FILE: auto-generate-vehicles.cs

///TODO: This is highly repetitious & could be abstracted to a single simpler
//method called 3X. Then it would be easy to add several more types of
//cars etc.

internal ISectionFile CreateEmrgCarMission(Point3d startPos, double fRadius, int portArmy, int planeArmy, AircraftType type, float health, Point3d aircraftPos, bool reverse=false, ServiceType planeState=ServiceType.NONE )
//Note that planeState isn't actually used below . . . health is, plus
//a/c type, army, etc.
{ ISectionFile f = GamePlay.gpCreateSectionFile();
string sect;
string key;
string value, value1, value2;
string ChiefName1 = "0_Chief_" + (health < 1f ? "Fire_" : "Fuel_");
string ChiefName2 = "0_Chief_" + (health < 1f ? "Emrg_" : "Ammo_");
string ChiefName3 = "0_Chief_" + (health < 1f ? "Bomb_" : "Bomb_");

//startPos=aircraftPos; //bhugh special, we're just going to spawn them in very near the actual a/c
if (DEBUG) GamePlay.gpLogServer(null, "Ground car group created at " + startPos.ToString () + " for " + aircraftPos.ToString () + " " + FindNearestAirport(aircraftPos).Name(), new object[] { });
if (portArmy == planeArmy) //???? ????????
{
switch (portArmy)
{
case 1:
if (health < 1f)
{
sect = "CustomChiefs";
key = "";
value = "Vehicle.custom_chief_emrg_0 $core/icons/tank.mma"; //???????
f.add(sect, key, value);
value = "Vehicle.custom_chief_emrg_1 $core/icons/tank.mma";//??????
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_0";
key = "Car.Austin_K2_ATV";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.Fire_pump_UK2_Transport";
value = "1";
f.add(sect, key, value);
sect = "Vehicle.custom_chief_emrg_1";
key = "Car.Austin_K2_Ambulance";
value = "";
f.add(sect, key, value);

sect = "Chiefs";
key = "0_Chief_Fire_0";
value = "Vehicle.custom_chief_emrg_0 gb /skin0 materialsSummer_RAF";
f.add(sect, key, value);
key = "0_Chief_Emrg_1";
value = "Vehicle.custom_chief_emrg_1 gb /skin0 materialsSummer_RAF";
f.add(sect, key, value);
}
else
{
sect = "CustomChiefs";
key = "";
value = "Vehicle.custom_chief_emrg_0 $core/icons/tank.mma";
f.add(sect, key, value);
value = "Vehicle.custom_chief_emrg_1 $core/icons/tank.mma";
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_0"; // ??????????
key = "Car.Albion_AM463";
value = "";
f.add(sect, key, value);
if (type == AircraftType.Bomber) // ??? ???????? ?????? ??????? ? ????? ????????
{
key = "Car.Fordson_N";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.Towed_Bowser_UK1_Transport";
value = "1";
f.add(sect, key, value);
}

sect = "Vehicle.custom_chief_emrg_1"; // ??????
value = "";
key = "Car.Bedford_MW_open";
f.add(sect, key, value);


if (type == AircraftType.Bomber) // ??? ???????? ????? ????????
{
sect = "CustomChiefs";
key = "";
value = "Vehicle.custom_chief_emrg_2 $core/icons/tank.mma";
f.add(sect, key, value);
sect = "Vehicle.custom_chief_emrg_2";
value = "";
key = "Car.Fordson_N";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.BombLoadingCart_UK1_Transport";
value = "1";
f.add(sect, key, value);
key = "TrailerUnit.BombLoadingCart_UK1_Transport";
f.add(sect, key, value);
};

sect = "Chiefs";
key = "0_Chief_Fuel_0";
value = "Vehicle.custom_chief_emrg_0 gb /skin0 materialsSummer_RAF";
f.add(sect, key, value);

key = "0_Chief_Ammo_1";
value = "Vehicle.custom_chief_emrg_1 gb /skin0 materialsSummer_RAF/tow00_00 1_Static";
f.add(sect, key, value);

if (type == AircraftType.Bomber)
{
key = "0_Chief_Bomb_2";
value = "Vehicle.custom_chief_emrg_2 gb /tow01_00 2_Static/tow01_01 3_Static/tow01_02 4_Static/tow01_03 5_Static/tow02_00 6_Static/tow02_01 7_Static";
f.add(sect, key, value);
}
sect = "Stationary";
key = "1_Static";
value = "Stationary.Morris_CS8-Bedford_MW_CargoAmmo3 gb 0.00 0.00 0.00";
f.add(sect, key, value);
if (type == AircraftType.Bomber) // ????? ??????
{
key = "2_Static";
value = "Stationary.Weapons_.Bomb_B_GP_250lb_MkIV gb 0.00 0.00 0.00";
f.add(sect, key, value);
key = "3_Static";
value = "Stationary.Weapons_.Bomb_B_GP_250lb_MkIV gb 0.00 0.00 0.00";
f.add(sect, key, value);
key = "4_Static";
value = "Stationary.Weapons_.Bomb_B_GP_250lb_MkIV gb 0.00 0.00 0.00";
f.add(sect, key, value);
key = "5_Static";
value = "Stationary.Weapons_.Bomb_B_GP_250lb_MkIV gb 0.00 0.00 0.00";
f.add(sect, key, value);
key = "6_Static";
value = "Stationary.Weapons_.Bomb_B_GP_500lb_MkIV gb 0.00 0.00 0.00";
f.add(sect, key, value);
key = "7_Static";
value = "Stationary.Weapons_.Bomb_B_GP_500lb_MkIV gb 0.00 0.00 0.00";
f.add(sect, key, value);
};
};
break;
case 2:
sect = "CustomChiefs"; //???????
key = "";
value = "Vehicle.custom_chief_emrg_0 $core/icons/tank.mma";
f.add(sect, key, value);
value = "Vehicle.custom_chief_emrg_1 $core/icons/tank.mma";//??????
f.add(sect, key, value);
if (health < 1f)
{
sect = "Vehicle.custom_chief_emrg_0";
key = "Car.Renault_UE";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.Foam_Extinguisher_GER1_Transport";
value = "1";
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_1";
if (PseudoRnd(0f, 1f) < 0.5f)
{
key = "Car.Opel_Blitz_med-tent";
}
else { key = "Car.Opel_Blitz_cargo_med"; };
value = "";
f.add(sect, key, value);
sect = "Chiefs";
key = "0_Chief_Fire_0";// "0_Chief_emrg";
value = "Vehicle.custom_chief_emrg_0 de ";
f.add(sect, key, value);
key = "0_Chief_Emrg_1";// "0_Chief_emrg";
value = "Vehicle.custom_chief_emrg_1 de ";
f.add(sect, key, value);
}
else
{
sect = "Vehicle.custom_chief_emrg_0";
key = "Car.Opel_Blitz_fuel";
value = "";
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_1";
key = "Car.Renault_UE";
f.add(sect, key, value);
key = "TrailerUnit.Oil_Cart_GER1_Transport";
value = "1";
f.add(sect, key, value);
key = "Car.Renault_UE";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.Anlasswagen_(starter)_GER1_Transport";
value = "1";
f.add(sect, key, value);

if (type == AircraftType.Bomber) // ????? ??????
{
sect = "CustomChiefs";
key = "";
value = "Vehicle.custom_chief_emrg_2 $core/icons/tank.mma";
f.add(sect, key, value);
sect = "Vehicle.custom_chief_emrg_2";
key = "Car.Renault_UE";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.HydraulicBombLoader_GER1_Transport";
value = "1";
f.add(sect, key, value);
key = "Car.Renault_UE";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.BombSled_GER1_Transport";
value = "1";
f.add(sect, key, value);
}

sect = "Chiefs";
key = "0_Chief_Fuel_0";
value = "Vehicle.custom_chief_emrg_0 de";
f.add(sect, key, value);
key = "0_Chief_Ammo_1";
value = "Vehicle.custom_chief_emrg_1 de";
f.add(sect, key, value);
if (type == AircraftType.Bomber)
{
key = "0_Chief_Bomb_2";
value = "Vehicle.custom_chief_emrg_2 de /tow01_00 1_Static/tow03_00 2_Static";
f.add(sect, key, value);
sect = "Stationary";
key = "1_Static";
value = "Stationary.Weapons_.Bomb_B_SC-250_Type2_J de 0.00 0.00 0.00";
f.add(sect, key, value);
key = "2_Static";
value = "Stationary.Weapons_.Bomb_B_SC-1000_C de 0.00 0.00 0.00";
f.add(sect, key, value);
};
};
break;
default:
break;

}
}
else
{
switch (portArmy)
{
case 1:
if (health < 1f)
{
sect = "CustomChiefs";
key = "";
value = "Vehicle.custom_chief_emrg_0 $core/icons/tank.mma"; //???????
f.add(sect, key, value);
value = "Vehicle.custom_chief_emrg_1 $core/icons/tank.mma";//??????
f.add(sect, key, value);
value = "Vehicle.custom_chief_emrg_2 $core/icons/tank.mma";//????????
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_0";
key = "Car.Austin_K2_ATV";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.Fire_pump_UK2_Transport";
value = "1";
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_1";
key = "Car.Austin_K2_Ambulance";
value = "";
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_2";
key = "Car.Beaverette_III";
value = "";
f.add(sect, key, value);

sect = "Chiefs";
key = "0_Chief_Fire_0";
value = "Vehicle.custom_chief_emrg_0 gb /skin0 materialsSummer_RAF";
f.add(sect, key, value);
key = "0_Chief_Emrg_1";
value = "Vehicle.custom_chief_emrg_1 gb /skin0 materialsSummer_RAF";
f.add(sect, key, value);
key = "0_Chief_Prisoner_2";
value = "Vehicle.custom_chief_emrg_2 gb ";
f.add(sect, key, value);
ChiefName3 = "0_Chief_Prisoner_";

}
else
{
sect = "CustomChiefs";
key = "";
value = "Vehicle.custom_chief_emrg_0 $core/icons/tank.mma"; //???????
f.add(sect, key, value);
sect = "Vehicle.custom_chief_emrg_0";
key = "Car.Beaverette_III";
value = "";
f.add(sect, key, value);
sect = "Chiefs";
key = "0_Chief_Prisoner_0";
value = "Vehicle.custom_chief_emrg_0 gb ";
f.add(sect, key, value);
ChiefName1 = "0_Chief_Prisoner_";
};
break;
case 2:
if (health < 1f)
{
sect = "CustomChiefs";
key = "";
value = "Vehicle.custom_chief_emrg_0 $core/icons/tank.mma"; //???????
f.add(sect, key, value);
value = "Vehicle.custom_chief_emrg_1 $core/icons/tank.mma";//??????
f.add(sect, key, value);
value = "Vehicle.custom_chief_emrg_2 $core/icons/tank.mma";//????????
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_0";
key = "Car.Renault_UE";
value = "";
f.add(sect, key, value);
key = "TrailerUnit.Foam_Extinguisher_GER1_Transport";
value = "1";
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_1";
key = "Car.Opel_Blitz_cargo_med";
value = "";
f.add(sect, key, value);

sect = "Vehicle.custom_chief_emrg_2";
key = "Car.SdKfz_231_6Rad";
value = "";
f.add(sect, key, value);

sect = "Chiefs";
key = "0_Chief_Fire_0";
value = "Vehicle.custom_chief_emrg_0 de";
f.add(sect, key, value);
key = "0_Chief_Emrg_1";
value = "Vehicle.custom_chief_emrg_1 de";
f.add(sect, key, value);
key = "0_Chief_Prisoner_2";
value = "Vehicle.custom_chief_emrg_2 de /marker0 1940-42_var1";
f.add(sect, key, value);
ChiefName3 = "0_Chief_Prisoner_";

}
else
{
sect = "CustomChiefs";
key = "";
value = "Vehicle.custom_chief_emrg_0 $core/icons/tank.mma"; //???????
f.add(sect, key, value);
sect = "Vehicle.custom_chief_emrg_0";
key = "Car.SdKfz_231_6Rad";
value = "";
f.add(sect, key, value);
sect = "Chiefs";
key = "0_Chief_Prisoner_0";
value = "Vehicle.custom_chief_emrg_0 de /marker0 1940-42_var1";
f.add(sect, key, value);
ChiefName1 = "0_Chief_Prisoner_";
};
break;
default:
break;
};
}



//Little cleanup on variables; avoid div by zero later on; route finder doesn't like it if the points are underground
if (startPos.z == 0 ) startPos.z=1000;
if (aircraftPos.z == 0 ) aircraftPos.z=1000;
if ((startPos.x - aircraftPos.x )< 5 ) startPos.x = aircraftPos.x + 75;
if ((startPos.y - aircraftPos.y )< 5 ) startPos.y = aircraftPos.y + 75;

//Instead of starting way out of sight, we're going to start
//in the direction of the startPos, but at distance dis, which
//is closer in to the a/c
Point3d closerStartPos=startPos;
if (!reverse) {
double dis=SPAWN_START_DISTANCE_M;
closerStartPos.x = aircraftPos.x+ dis * ((startPos.x - aircraftPos.x) / Math.Abs(startPos.x - aircraftPos.x)); closerStartPos.y = aircraftPos.y + dis * ((startPos.y - aircraftPos.y) / Math.Abs(startPos.y - aircraftPos.y));
}
Point3d TmpStartPos = closerStartPos;
TmpStartPos.x += PseudoRnd(-30f, 30f) + fRadius; TmpStartPos.y += PseudoRnd(-30f, 30f) + fRadius;
Point3d BirthPos = EmrgVehicleStartPos(TmpStartPos, startPos);
double disx, disy;

sect = ChiefName1+"0" + "_Road";
key = "";
value1 = BirthPos.x.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.y.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.z.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " 0 92 5 ";
Console.WriteLine(value1);
//BirthPos.x -= 50f * ((BirthPos.x - aircraftPos.x) / Math.Abs(BirthPos.x - aircraftPos.x)); BirthPos.y -= 50f * ((BirthPos.y - aircraftPos.y) / Math.Abs(BirthPos.y - aircraftPos.y));

double edis=SPAWN_END_DISTANCE_M; //how far away from the plane we stop, when driving in towards it.
if (reverse) edis= SPAWN_START_DISTANCE_REVERSE_M; //IN CASE OF reverse we call this the 'start' distance because we are starting rather then ending here. (Thus the name, 'reverse'). This tells where the vehicles start, when starting close & then driving out away from the a/c.
disx=Math.Abs(BirthPos.x - aircraftPos.x) - (edis-2);
if (disx<(edis-2)) disx=(edis-2);
disy=Math.Abs(BirthPos.y - aircraftPos.y) - edis;
if (disy<edis) disy=edis;

BirthPos.x -= disx * ((BirthPos.x - aircraftPos.x) / Math.Abs(BirthPos.x - aircraftPos.x)); BirthPos.y -= disy * ((BirthPos.y - aircraftPos.y) / Math.Abs(BirthPos.y - aircraftPos.y));
value2 = BirthPos.x.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.y.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.z.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " 0 92 5 ";;
Console.WriteLine(value2);

if (reverse ) {
f.add(sect, key, value2);
f.add(sect, key, value1);
}else {
f.add(sect, key, value1);
f.add(sect, key, value2);
}

TmpStartPos = closerStartPos;
TmpStartPos.x += PseudoRnd(-30f, 30f) - fRadius; TmpStartPos.y += PseudoRnd(-30f, 30f) + fRadius;
BirthPos = EmrgVehicleStartPos(TmpStartPos, startPos);
//BirthPos = TmpStartPos;
sect = ChiefName2+"1" + "_Road";
key = "";
value1 = BirthPos.x.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.y.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.z.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " 0 92 5 ";
Console.WriteLine(value1);

disx=Math.Abs(BirthPos.x - aircraftPos.x) - (edis+1);
if (disx<10) disx=(edis+1);
disy=Math.Abs(BirthPos.y - aircraftPos.y) - (edis-2);
if (disy<(edis-2)) disy=(edis-2);

BirthPos.x -= disx * ((BirthPos.x - aircraftPos.x) / Math.Abs(BirthPos.x - aircraftPos.x)); BirthPos.y -= disy * ((BirthPos.y - aircraftPos.y) / Math.Abs(BirthPos.y - aircraftPos.y));

value2 = BirthPos.x.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.y.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.z.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " 0 92 5 ";;
Console.WriteLine(value2);

if (reverse ) {
f.add(sect, key, value2);
f.add(sect, key, value1);
}else {
f.add(sect, key, value1);
f.add(sect, key, value2);
}


TmpStartPos = closerStartPos;
TmpStartPos.x += PseudoRnd(-30f, 30f) + fRadius; TmpStartPos.y += PseudoRnd(-30f, 30f) - fRadius;
BirthPos = EmrgVehicleStartPos(TmpStartPos, startPos);

sect = ChiefName3 + "2" + "_Road";
key = "";
value1 = BirthPos.x.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.y.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.z.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " 0 92 5 ";
Console.WriteLine(value1);

disx=Math.Abs(BirthPos.x - aircraftPos.x) - (edis-1);
if (disx<(edis-1)) disx=(edis-1);
disy=Math.Abs(BirthPos.y - aircraftPos.y) - (edis+2);
if (disy<(edis+2)) disy=(edis+2);

BirthPos.x -= disx * ((BirthPos.x - aircraftPos.x) / Math.Abs(BirthPos.x - aircraftPos.x)); BirthPos.y -= disy * ((BirthPos.y - aircraftPos.y) / Math.Abs(BirthPos.y - aircraftPos.y));
value2 = BirthPos.x.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.y.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " " + BirthPos.z.ToString(System.Globalization.CultureIn fo.InvariantCulture.NumberFormat) + " 0 92 5 ";;
Console.WriteLine(value2);
if (reverse ) {
f.add(sect, key, value2);
f.add(sect, key, value1);
}else {
f.add(sect, key, value1);
f.add(sect, key, value2);
}

return f;
}


internal Point3d EmrgVehicleStartPos(Point3d startPos, Point3d endPos)
{
Point3d TmpPos = startPos;

while (((GamePlay.gpLandType(TmpPos.x, TmpPos.y) & LandTypes.WATER) != 0) )
{
TmpPos.x -= (TmpPos.x - endPos.x) / 10f;
TmpPos.y -= (TmpPos.y - endPos.y) / 10f;
};

return TmpPos;
}

internal void CheckEmrgCarOnAirport(int aircraftNumber)
{
// ????????? ???? ?? ??????? ? ?????????
//MissionLoading=false; //kludgy fix, this seems to be in place to avoid mission load loops or something, but it doesn't work
AiGroundGroup MyCar = null;

{
if (DEBUG) GamePlay.gpLogServer(null, "Creating a new car group at " + CurPlanesQueue[aircraftNumber].basePos.Name() + " for " + aircraftNumber, new object[] { });
MissionLoading = true;
MissionLoadingAircraftNumber=aircraftNumber;
int ArmyPos = 0;
if (GamePlay.gpFrontExist())
{
ArmyPos = GamePlay.gpFrontArmy(CurPlanesQueue[aircraftNumber].basePos.Pos().x, CurPlanesQueue[aircraftNumber].basePos.Pos().y);
}
else { ArmyPos = CurPlanesQueue[aircraftNumber].aircraft.Army(); };
// ??????? ?????? ? ?????????
//In case of spawn-in, we reverse the direction making it away from the plane.

if (DEBUG) GamePlay.gpLogServer(null, "Creating a new car group at " + ((int)CurPlanesQueue[aircraftNumber].State).ToString()+" " + ((int)ServiceType.SPAWNIN).ToString()+ " " + ( ((int)CurPlanesQueue[aircraftNumber].State & (int)ServiceType.SPAWNIN) ==0 ).ToString(), new object[] { });

bool reverse=true;
if ( ((int)CurPlanesQueue[aircraftNumber].State & (int)ServiceType.SPAWNIN) ==0 ) reverse=false;

GamePlay.gpPostMissionLoad(CreateEmrgCarMission(Cu rPlanesQueue[aircraftNumber].basePos.Pos(), CAR_POS_RADIUS, ArmyPos, CurPlanesQueue[aircraftNumber].aircraft.Army(), CurPlanesQueue[aircraftNumber].aircraft.Type(),CurPlanesQueue[aircraftNumber].health, CurPlanesQueue[aircraftNumber].aircraft.Pos(), reverse, CurPlanesQueue[aircraftNumber].State));


}
return ;
}

public override void OnAircraftLanded (int missionNumber, string shortName, AiAircraft aircraft)
{
base.OnAircraftLanded(missionNumber, shortName, aircraft);


aircraftActiveList.Remove(aircraft);
StartVehiclesForAircraft (aircraft );
}


public override void OnAircraftCrashLanded(int missionNumber, string shortName, AiAircraft aircraft)
{
base.OnAircraftCrashLanded(missionNumber, shortName, aircraft);
StartVehiclesForAircraft (aircraft);
aircraftActiveList.Remove(aircraft);





}

//TODO: For bombers, this will re-spawn vehicles whenever they change places

public override void OnPlaceEnter (Player player, AiActor actor, int placeIndex)
{
base.OnPlaceEnter(player, actor, placeIndex);

AiAircraft aircraft= actor as AiAircraft;

if (DEBUG) GamePlay.gpLogServer(null, "Place Enter: " +
player.Name() + " " +
actor.Name() + " " +
placeIndex.ToString() + " " , new object[] { });

//do this only once per actor (avoids many multiple vehicle spawn-ins whwenever bomber players move between positions, triggering this OnplaceEnter repeatedly)
if (!actorPlaceEnterList.Contains(actor)) {
StartVehiclesForAircraft (aircraft, true ); //this is spawn-in, so we reverse vehicle direction
actorPlaceEnterList.Add(actor);
}

}


public override void OnCarter(AiActor actor, int placeIndex)
{
base.OnCarter(actor, placeIndex);

AiAircraft aircraft= actor as AiAircraft;

if (DEBUG) GamePlay.gpLogServer(null, "OnCarter: " +
actor.Name() + " " +
placeIndex.ToString() + " " , new object[] { });

}

public override void OnPersonMoved(AiPerson person, AiActor fromCart, int fromPlaceIndex)
{
base.OnPersonMoved(person, fromCart, fromPlaceIndex);

AiAircraft aircraft= fromCart as AiAircraft;

if (DEBUG) GamePlay.gpLogServer(null, "OnPersonMoved: " +
fromCart.Name() + " " +
person.Name() + " " +
fromPlaceIndex.ToString() + " " , new object[] { });

}


public override void OnAircraftTookOff(int missionNumber, string shortName, AiAircraft aircraft)
{
base.OnAircraftTookOff(missionNumber, shortName, aircraft);
try
{
//AiAircraft aircraft= actor as AiAircraft;

if (DEBUG) GamePlay.gpLogServer(null, "Starting vehicle/took off", new object[] { });

if ( !isAiControlledPlane2(aircraft) && !aircraftActiveList.Contains(aircraft)) aircraftActiveList.Add(aircraft);
}
catch (Exception e) {System.Console.WriteLine (e.ToString());}

}


public override void OnAircraftDamaged(int missionNumber, string shortName, AiAircraft aircraft, AiDamageInitiator initiator, NamedDamageTypes damageType)
{

base.OnAircraftDamaged(missionNumber, shortName, aircraft, initiator, damageType);
try
{
//just keep a list of all damaged aircraft
if (!aircraftDamagedList.Contains(aircraft)) aircraftDamagedList.Add(aircraft);
if ( !isAiControlledPlane2(aircraft) && !aircraftActiveList.Contains(aircraft)) aircraftActiveList.Add(aircraft);

}
catch (Exception e) {System.Console.WriteLine (e.ToString());}

//add your code here
}

public override void OnAircraftCutLimb(int missionNumber, string shortName, AiAircraft aircraft, AiDamageInitiator initiator, LimbNames limbName)
{

base.OnAircraftCutLimb(missionNumber, shortName, aircraft, initiator, limbName);
try
{
//just keep a list of all damaged aircraft
if (!aircraftDamagedList.Contains(aircraft)) aircraftDamagedList.Add(aircraft);
if ( !isAiControlledPlane2(aircraft) && !aircraftActiveList.Contains(aircraft)) aircraftActiveList.Add(aircraft);
}
catch (Exception e) {System.Console.WriteLine (e.ToString());}

}

public void OnAircraftStopped (AiAircraft aircraft)
{
try
{
if (aircraft != null ) {

if (DEBUG) GamePlay.gpLogServer(null, "Aircraft detected as stopped/landed: " +
aircraft.Player(0).Name() + " " +
aircraft.Name() + " ", new object[] { });

StartVehiclesForAircraft (aircraft );
aircraftActiveList.Remove(aircraft); //The only way they can get back on the list is by taking off again. Given the way CloD works, this might not be possible if they just landed in a field or whatever. But them's the breaks . . .
}

}
catch (Exception e) {System.Console.WriteLine (e.ToString());}

}

public override void OnActorDead(int missionNumber, string shortName, AiActor actor, List<DamagerScore> damages)
{
#region stb
base.OnActorDead(missionNumber, shortName, actor, damages);
try
{
if (actor as AiAircraft != null ) {
StartVehiclesForAircraft (actor as AiAircraft );
aircraftActiveList.Remove(actor as AiAircraft);
}

}
catch (Exception e) {System.Console.WriteLine (e.ToString());}
#endregion
//add your code here
}


public void StartVehiclesForAircraft (AiAircraft aircraft, bool spawnIn=false ) {
try
{
if (aircraft==null || isAiControlledPlane2(aircraft) ) return;
if (DEBUG) GamePlay.gpLogServer(null, "Getting Airports for startvehicles", new object[] { });
BasePos NearestAirport = new BasePos();
NearestAirport = FindNearestAirport(aircraft);

if (DEBUG) GamePlay.gpLogServer(null, "Starting vehicles at " + NearestAirport.Name(), new object[] { });

if (NearestAirport != null)
{

PlanesQueue CurPlane = new PlanesQueue(aircraft, NearestAirport, 0);
int ArmyPos = 0;
CurPlane.health = (float)aircraft.getParameter(part.ParameterTypes.M _Health, -1);

if (aircraftDamagedList.Contains(aircraft)) CurPlane.health /=2;
//CurPlane.health=0; //testing

float cdam = (float)aircraft.getParameter(part.ParameterTypes.M _CabinDamage, 1);
float ndam = (float)aircraft.getParameter(part.ParameterTypes.M _NamedDamage, 1);

//note that MANY of the CurPlane.States set below do not do anything
//because the actual type of ai vehicle spawned is determined
//in the createemrgcarmission method via various criteria including
//army, health, type of plane etc but not really using the states
//set here AT ALL. FYI.
if (DEBUG) GamePlay.gpLogServer(null, "Health = " + CurPlane.health.ToString() + " " + cdam.ToString() + " " + ndam.ToString(), new object[] { });

if (spawnIn) CurPlane.State |= ServiceType.SPAWNIN;
if (GamePlay.gpFrontExist())
{
ArmyPos = GamePlay.gpFrontArmy(NearestAirport.Pos().x, NearestAirport.Pos().y);
}
else { ArmyPos = aircraft.Army(); };
//if (true || CurPlane.health < 1f) //testing
if (CurPlane.health < 1f)
{
CurPlane.State |= ServiceType.EMERGENCY;
CurPlane.State |= ServiceType.FIRE;
}
else if (aircraft.Army() == ArmyPos)
{
CurPlane.State |= ServiceType.FUEL;
CurPlane.State |= ServiceType.AMMO;
if (aircraft.Type() == AircraftType.Bomber) CurPlane.State |= ServiceType.BOMBS;
CurPlane.State |= ServiceType.BOMBS;
};
//if (true || !(aircraft.Army() == ArmyPos)) CurPlane.State |= ServiceType.PRISONERCAPTURE; //testing
if (!(aircraft.Army() == ArmyPos)) CurPlane.State |= ServiceType.PRISONERCAPTURE;
if (!CurPlanesQueue.Contains(CurPlane))
{
if (DEBUG) GamePlay.gpLogServer(null, "Starting vehicles at " + NearestAirport.Name()+ " Type: " + CurPlane.State, new object[] { });
CurPlanesQueue.Add(CurPlane);
CheckEmrgCarOnAirport(CurPlanesQueue.Count - 1);
}
else
{
for (int i = 0; i < CurPlanesQueue.Count; i++)
if (CurPlanesQueue[i] == CurPlane)
{
CheckEmrgCarOnAirport(i);
break;
}
}
CurPlane = null;
}
}
catch (Exception e) {System.Console.WriteLine (e.ToString());}
}

private void checkForStoppedAircraft (List <AiAircraft> aircraftList){
int a_count=0;
double Z_VelocityTAS;

foreach (AiAircraft a in aircraftList ) {

Z_VelocityTAS = a.getParameter(part.ParameterTypes.Z_VelocityTAS, -1);

//If true airspeed is 0 now, check again in 5 seconds. If still 0 we're assuming crashed and/or landed somehow.
if (Z_VelocityTAS == 0) {

Timeout (5, () => {
Z_VelocityTAS = a.getParameter(part.ParameterTypes.Z_VelocityTAS, -1);
if (Z_VelocityTAS == 0) OnAircraftStopped(a);

});


}

}


}

//Returns whether aircraft is an Ai plane (no humans in any seats)
private bool isAiControlledPlane2(AiAircraft aircraft)
{ // returns true if specified aircraft is AI controlled with no humans aboard, otherwise false
if (aircraft == null) return false;
//check if a player is in any of the "places"
for (int i = 0; i < aircraft.Places(); i++)
{
if (aircraft.Player(i) != null) return false;
}
return true;
}




private string Left(string Original, int Count)
{
if (Original == null || Original == string.Empty || Original.Length <
Count) {
return Original;
} else {
// Return a sub-string of the original string, starting at index 0.
return Original.Substring(0, Count);
}
}
}



ZIP file with script & mission files, sample code, instructions all in one (http://brenthugh.com/twc/auto-generate-vehicles-mission-script-add-in-2016-05-22.zip)