PDA

View Full Version : Organising flights in a multisquad campaign.


5./JG27.Farber
04-07-2012, 10:51 PM
Well our campaign is nearly ready and everyday the "grail patch" draws closer. The big question still remains. How to coordinate (especially red players) into action realistically during our missions. We could simply direct the squadrons in the air and then use one of the following:


Obviously the most realistic method would be have a fighter command to plot and direct red squadrons where they are needed. However I dont see anyone comming forward to do this. This thread wexplains how it might happen: http://forum.1cpublishing.eu/showthread.php?t=30918

Triggers are the next obvious solution. However Triggers are a dead cert! There is no room for error which is unrealistic. Also the Orange writing is a massive immertion killer.

The third option is a brief. This is ok for blue on the attack. However red may feel cheated by a brief if they dont get into action and or lose out in the mission.

The forth option is let radar and the game call out the action and squadrons conduct themselves.



What do you (especially those willing to take pat) think. We are open to all ideas however keep them short and simple - no essays please. I look forward to reading your responses.

FG28_Kodiak
04-08-2012, 09:06 AM
Triggers are the next obvious solution. However Triggers are a dead cert! There is no room for error which is unrealistic. Also the Orange writing is a massive immertion killer.


In my Coops i use triggers, but without Orange writing, i simulate a radio message.

if ("EnterAreaM8".Equals(shortName))
{
int i = 0;

AiActor actor = GamePlay.gpActorByName("0:BoB_LW_JG51_III.000");


(actor as AiAircraft).SayToGroup(actor.Group() as AiAirGroup, "Enemy_planes");

Timeout(i += 2, () =>
{
(actor as AiAircraft).SayToGroup(actor.Group() as AiAirGroup, "In_square");
});

Timeout(i += 2, () =>
{
(actor as AiAircraft).SayToGroup(actor.Group() as AiAirGroup, "M");
});
Timeout(i += 2, () =>
{
(actor as AiAircraft).SayToGroup(actor.Group() as AiAirGroup, "n8");
});

GamePlay.gpGetTrigger(shortName).Enable = false;
}

5./JG27.Farber
04-08-2012, 11:10 AM
Could a trigger have a chance of success? Or is it like a switch - on/off. Can a message in chat be sent to a specific Squadron or just one team?

bolox
04-08-2012, 11:31 AM
triggers can be made to do 'random' things
// Script that triggered an accidental damage to the player plane
// Autor: FG28_Kodiak

using System;
using maddox.game;
using maddox.game.world;

public class Mission : maddox.game.AMission
{

AiAircraft PlayerPlane;



public override void OnTrigger(int missionNumber, string shortName, bool active)
{
if (("trigger01".Equals(shortName) || "trigger02".Equals(shortName)) && active)
{
DoDamage();
}
GamePlay.gpGetTrigger(shortName).Enable = false;
}


private void DoDamage()
{
PlayerPlane = (AiAircraft)GamePlay.gpPlayer().Place();

Random RandomIncident = new Random();

switch (RandomIncident.Next(1,9))
{
case 1:
PlayerPlane.hitNamed(part.NamedDamageTypes.Control sElevatorDisabled);
GamePlay.gpHUDLogCenter("Elevator Disabled");
break;
case 2:
PlayerPlane.hitNamed(part.NamedDamageTypes.Control sAileronsDisabled);
GamePlay.gpHUDLogCenter("Ailerons Disabled");
break;
case 3:
PlayerPlane.hitNamed(part.NamedDamageTypes.Control sRudderDisabled);
GamePlay.gpHUDLogCenter("Rudder Disabled");
break;
case 4:
PlayerPlane.hitNamed(part.NamedDamageTypes.Eng0Pro pBlade0Broken);
GamePlay.gpHUDLogCenter("PropBlade Broken");
break;
case 5:
PlayerPlane.hitNamed(part.NamedDamageTypes.Eng0Tot alFailure);
GamePlay.gpHUDLogCenter("Engine Failure");
break;
case 6:
PlayerPlane.hitNamed(part.NamedDamageTypes.Eng0Oil SecondariesFire);
GamePlay.gpHUDLogCenter("Oil is on fire");
break;
case 7:
PlayerPlane.hitNamed(part.NamedDamageTypes.Hydraul icsPumpFailure);
GamePlay.gpHUDLogCenter("Hydraulics Pump failure");
break;
case 8:
PlayerPlane.hitNamed(part.NamedDamageTypes.Underca rriageDownLockFailureL);
GamePlay.gpHUDLogCenter("UndercarriageDownLock Left failure");
break;
case 9:
PlayerPlane.hitNamed(part.NamedDamageTypes.Eng0Plu g00Failure);
PlayerPlane.hitNamed(part.NamedDamageTypes.Eng0Plu g01Failure);
PlayerPlane.hitNamed(part.NamedDamageTypes.Eng0Plu g05Failure);
GamePlay.gpHUDLogCenter("Engine Plug 0,1,5 failure");
break;
}

}

public override void OnAircraftLanded(int missionNumber, string shortName, AiAircraft aircraft)
{
GamePlay.gpHUDLogCenter("Excellent!");
}

public override void OnAircraftCrashLanded(int missionNumber, string shortName, AiAircraft aircraft)
{
GamePlay.gpHUDLogCenter("Congratulation You are alive ;-)");
}


}

message can be sent to pretty much anyone you like as i understand it- but Kodiak will be able to answer that much better than me (and probably a better way of doing the random:o )

41Sqn_Banks
04-08-2012, 11:33 AM
The best would be IMHO to react on Radar messages. However I didn't find a way so far to react on them in the script.

Osprey
04-08-2012, 12:02 PM
I think the main problem with radar is that it tells the RAF the type. In reality FC RDF plotters would only be able to give location, height, heading and approximate numbers eg.

"30 plus, 10 miles North of Calais at Angels 12, Heading 300"

Ground control (who the flight lead was in contact with) would give a vector and height for interception. This often took the form of a normal conversation when single pilots were sent to intercept single enemies. eg. "climb to 10,000ft, heading south-south west, you should see him on your 2 o'clock by now"

So what would be immense is to have the script loop the list to find all RAF flight leads (which have at least a single wingman) and then load a custom menu for that pilot. The custom menu would have a ground control option to locate the enemy and firing it would give him a height, position, heading and number in the chat, vocally or on the orange HUD. Since this would be by request to that flight leader only then I don't see a problem with that. It is then up to him to direct his squadron to the enemy.

It is possible to create a menu, to get the enemy, to get the flight position of the pilot.
I would code this but I'm shite at it, I have the idea but not the technical ability :(

I'm not a fan of randomness, RDF was excellent and pretty much directed the RAF into position - there are hundreds of accounts from both sides about this. I only just read about how Brian Kingcombe would scramble away inland from the enemy who were 15kft, he'd climb to 20kft, turn around and shallow dive south to meet them at speed. Anyway, replication would be ace.

FG28_Kodiak
04-08-2012, 02:07 PM
An other way to get the Enemy location via script.
In this script i check the distance between red Airgroups and blue Airgroups. And if it lower as a given value, there will a Voice Message being generated to the ally Airgroups which are in range to the enemy. The script is not fully tested yet, i am to busy at the moment (home renovation :rolleyes:). Feel free to expand it, the voice files (use the filename without .ogg) are located in ..\Steam\SteamApps\common\il-2 sturmovik cliffs of dover\parts\bob\speech, you can add for example the type of the Enemy Aircrafts, their Numbers etc. The timedelay between the checks should be larger as in my example to avoid overlaping of the messages.



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


public class Mission : AMission
{

public void sendMessagesToAirgroup(AiAirGroup from, double maxDistance)
{

AiAirGroup[] EnemyAirgroups;
Point3d StartPos = new Point3d(from.Pos().x, from.Pos().y, 1.0);

EnemyAirgroups = GamePlay.gpAirGroups((from.Army() == 1) ? 2 : 1);

int i = 0;

foreach (AiAirGroup aag in EnemyAirgroups)
{
Point3d enemyPosition = new Point3d(aag.Pos().x, aag.Pos().y, 1.0);

if (from.Pos().distance(ref enemyPosition) < maxDistance)
{

string sectorName = GamePlay.gpSectorName(aag.Pos().x, aag.Pos().y);

string[] splittet = sectorName.Split(',');
string alpha = splittet[0];
string number = "n" + splittet[1];

AiAircraft LeaderPlane = (from.GetItems()[0] as AiAircraft);

Timeout(i, () =>
{
(LeaderPlane as AiAircraft).SayToGroup(from as AiAirGroup, "Enemy_planes");
});


Timeout(i += 2, () =>
{
LeaderPlane.SayToGroup(from, "In_square");
});

Timeout(i += 2, () =>
{
LeaderPlane.SayToGroup(from, alpha);
});
Timeout(i += 2, () =>
{
LeaderPlane.SayToGroup(from, number);
});

i += 2;
}
}
}


public override void OnTickGame()
{
base.OnTickGame();

if (Time.tickCounter() % 600 == 299)
{
foreach(AiAirGroup aag in GamePlay.gpAirGroups(1))
sendMessagesToAirgroup(aag, 100000.0);

}
}
}

_79_dev
04-08-2012, 02:53 PM
That looks interesting, Kodiak. Do I just copy that script over existing one? Do I need any extra functions to invoke messages? Where do I edit distaff value between Airgroups? .... If You answer I will test it as soon as possible...

FG28_Kodiak
04-08-2012, 02:56 PM
Only copy the sendMessagesToAirgroup to your code, in OnTickGame you will see the example how to use it
sendMessagesToAirgroup(aag, 100000.0);
bold is the distance.

salmo
04-08-2012, 02:59 PM
Some more radar speech code that might be useful. I use sayMessageTo to voice to everyone in the army, not just the airgroup, and have converted altitude to use 'angels' & 'metres' (depending on the army) with custom functions.

Interestingly, both (actor as AiAircraft).getParameter(part.ParameterTypes.Z_Alt itudeMSL, -1); and (actor as AiAircraft).getParameter(part.ParameterTypes.Z_Alt itudeAGL, -1); both fail to get the airgroup altitude in a multi-player server environment, so you need to use the Pos().z & convert the z offset to metres.

Code removed by author

Osprey
04-08-2012, 03:19 PM
Now this stuff looks very promising.

I'm going to crack open VS express and have a bigger look. I can't write the stuff but adapting it may be easier.

5./JG27.Farber
04-08-2012, 04:36 PM
Kodiak thats great. Were gonna give it a whirl on our server today. I have some ideas for expansion. Could there be some kind of delay between requesting the information and recieving it, say 4 mins? This would simulate the request going to and from the lines of communication. Also. What if some radars are knocked out or damaged? Can this be simulated also?

P.S. I know how you feel with the renovation, Ive been doing it for 4 months now. It will be finished this week. What a grind.

FG28_Kodiak
04-08-2012, 06:00 PM
you wanna ask the operators with the mission menu? and than after 4min you will get the response? This is possible without problem.
Okay for clearance, when we use "radars" will you give the messages to the Airgroups in range of a spezific radarstation or to all. So if you hit the 'radarbutton' then you get Information of all Enemies Airgoups in range of any available radarstation or only from this you are in Range?

_79_dev
04-08-2012, 06:40 PM
i gave it a go : this is my script


//$reference IL2ClodCommanderStats.dll
// v.1_0. script by FG28_Kodiak, ZaltysZ, Oreva, Small_Bee, RAF238thWildWillie
using System;
using System.Diagnostics;
using System.Collections;
using maddox.GP;
using maddox.game;
using maddox.game.world;
using part;
using System.Collections.Generic;
using IL2ClodCommanderStats;


public class Mission : AMission
{
#region Stats Initialization
// For Connection to the IL2 Clod Commander Application
// This allows you to store stats on players within Cliffs of Dover
// Change the following to meet your needs
//
private static string serverName = "5./JG27 SERVER";
private static string serverIP = "127.0.0.1";

// Password is not used currently
private static string serverPassword = "password";
private static Int32 serverPort = 27015;
private StatsRecording stats = new StatsRecording(serverName, serverIP, serverPassword, serverPort);
private Dictionary<String, AiActor> allActors = new Dictionary<String, AiActor>();
private List<ServerCommand> newCmds = new List<ServerCommand>();
private Stopwatch MissionTimer = new Stopwatch();
#endregion

int LastMissionLoaded = 0;

double initTime;


////////////////setting chat messages
private void sendChatMessage(string msg, params object[] args)
{
GamePlay.gpLogServer(null, msg, args);
}


private void sendChatMessage(Player player, string msg, params object[] args)
{
if (player != null)
GamePlay.gpLogServer(new Player[] { player }, msg, args);
}


private void sendChatMessage(int army, string msg, params object[] args)
{
List<Player> Consignees = new List<Player>();

if (GamePlay.gpPlayer() != null)
Consignees.Add(GamePlay.gpPlayer());
if (GamePlay.gpRemotePlayers() != null)
Consignees.AddRange(GamePlay.gpRemotePlayers());

if (army == -1)
GamePlay.gpLogServer(null, msg, args);
else if (Consignees.Exists(item => item.Army() == army))
GamePlay.gpLogServer(Consignees.FindAll(item => item.Army() == army).ToArray(), msg, args);
}
////////////////testing voice messages

public void sendMessagesToAirgroup(AiAirGroup from, double maxDistance)
{

AiAirGroup[] EnemyAirgroups;
Point3d StartPos = new Point3d(from.Pos().x, from.Pos().y, 1.0);

EnemyAirgroups = GamePlay.gpAirGroups((from.Army() == 1) ? 2 : 1);

int i = 0;

foreach (AiAirGroup aag in EnemyAirgroups)
{
Point3d enemyPosition = new Point3d(aag.Pos().x, aag.Pos().y, 1.0);

if (from.Pos().distance(ref enemyPosition) < maxDistance)
{

string sectorName = GamePlay.gpSectorName(aag.Pos().x, aag.Pos().y);

string[] splittet = sectorName.Split(',');
string alpha = splittet[0];
string number = "n" + splittet[1];

AiAircraft LeaderPlane = (from.GetItems()[0] as AiAircraft);

Timeout(i, () =>
{
(LeaderPlane as AiAircraft).SayToGroup(from as AiAirGroup, "Enemy_planes");
});


Timeout(i += 2, () =>
{
LeaderPlane.SayToGroup(from, "In_square");
});

Timeout(i += 2, () =>
{
LeaderPlane.SayToGroup(from, alpha);
});
Timeout(i += 2, () =>
{
LeaderPlane.SayToGroup(from, number);
});

i += 2;
}
}
}


public override void OnTickGame()
{
base.OnTickGame();

if (Time.tickCounter() % 600 == 299)
{
foreach(AiAirGroup aag in GamePlay.gpAirGroups(1))
sendMessagesToAirgroup(aag, 100000.0);

}



// loading sub-missions


#region Stats Timer
if (MissionTimer.Elapsed.TotalSeconds >= 5) // 5 seconds
{
MissionTimer.Restart(); // stopwatch reset to 0 and restart
if (stats != null )
{
newCmds = stats.getCommands();
if (newCmds != null && newCmds.Count > 0)
ProcessCommands(newCmds);
}
}
#endregion
///////////////////////
// server info every 5 min

if (Time.tickCounter() % 9000 == 1800)

{

sendChatMessage((-1), "PLEAS VISIT www.5jg27.net TO CHECK STATS");

}


// load ground objects
if (Time.tickCounter() % 2592000 == 300)
{
GamePlay.gpPostMissionLoad("missions/Multi/Dogfight/kanalkampf/kanalkampf_ground.mis");

}

// loads Mission1
if (Time.tickCounter() % 2592000 == 12600)
{
GamePlay.gpPostMissionLoad("missions/Multi/Dogfight/kanalkampf/kanalkampf_Air1Red1Blue1.mis");

}

// loads Rescue
if (Time.tickCounter() % 2592000 == 95000)
{
GamePlay.gpPostMissionLoad("missions/Multi/Dogfight/kanalkampf/kanalkampf_Airsearescue.mis");

}


// loads Mission2
if (Time.tickCounter() % 2592000 == 66600)
{
GamePlay.gpPostMissionLoad("missions/Multi/Dogfight/kanalkampf/kanalkampf_Air2Red2.mis");


}

// loads Mission3
if (Time.tickCounter() % 2592000 == 144000)
{
GamePlay.gpPostMissionLoad("missions/Multi/Dogfight/kanalkampf/kanalkampf_Air3Blue2.mis");


}



}
////////////////////////////////////////////////////////////////////////////////////////////////////

//

// Base Scripts for Missions
private bool isAiControlledPlane (AiAircraft aircraft)
{
if (aircraft == null)
{
return false;
}

Player [] players = GamePlay.gpRemotePlayers ();
foreach (Player p in players)
{
if (p != null && (p.Place () is AiAircraft) && (p.Place () as AiAircraft) == aircraft)
{
return false;
}
}

return true;
}

private void destroyPlane (AiAircraft aircraft) {
if (aircraft != null) {
aircraft.Destroy ();
}
}

private void explodeFuelTank (AiAircraft aircraft)
{
if (aircraft != null)
{
aircraft.hitNamed (part.NamedDamageTypes.FuelTank0Exploded);
}
}

private void destroyAiControlledPlane (AiAircraft aircraft) {
if (isAiControlledPlane (aircraft)) {
destroyPlane (aircraft);
}
}

private void damageAiControlledPlane (AiActor actor) {
if (actor == null || !(actor is AiAircraft)) {
return;
}

AiAircraft aircraft = (actor as AiAircraft);

if (!isAiControlledPlane (aircraft)) {
return;
}

if (aircraft == null) {
return;
}

aircraft.hitNamed (part.NamedDamageTypes.ControlsElevatorDisabled);
aircraft.hitNamed (part.NamedDamageTypes.ControlsAileronsDisabled);
aircraft.hitNamed (part.NamedDamageTypes.ControlsRudderDisabled);
aircraft.hitNamed (part.NamedDamageTypes.FuelPumpFailure);

int iNumOfEngines = (aircraft.Group() as AiAirGroup).aircraftEnginesNum();
for (int i = 0; i < iNumOfEngines; i++)
{
aircraft.hitNamed((part.NamedDamageTypes)Enum.Pars e(typeof(part.NamedDamageTypes), "Eng" + i.ToString() + "TotalFailure"));
}

/***Timeout (240, () =>
{explodeFuelTank (aircraft);}
);
* ***/

Timeout (300, () =>
{destroyPlane (aircraft);}
);
}

public override void Init(maddox.game.ABattle battle, int missionNumber)
{
base.Init(battle, missionNumber);
MissionNumberListener = -1; //Listen to events of every mission
}

//////////////////////////////////////////
// Methods for Stats (You can add any code you may need after the Stats Region
//////////////////////////////////////////

public override void OnBattleStarted()
{
base.OnBattleStarted();
#region Stats
MissionTimer.Start(); // start the stopwatch
#endregion
}

private void sendScreenMessageTo(int army, string msg, object[] parms)
{
List<Player> Players = new List<Player>();

// on Dedi the server or for singleplayertesting
if (GamePlay.gpPlayer() != null)
{
if (GamePlay.gpPlayer().Army() == army || army == -1)
Players.Add(GamePlay.gpPlayer());
}
if (GamePlay.gpRemotePlayers() != null || GamePlay.gpRemotePlayers().Length > 0)
{
foreach (Player p in GamePlay.gpRemotePlayers())
{
if (p.Army() == army || army == -1)
Players.Add(p);
}
}
if (Players != null && Players.Count > 0)
GamePlay.gpHUDLogCenter(Players.ToArray(), msg, parms);
}

private void ProcessCommands(List<ServerCommand> newCmds)
{
try
{
foreach (ServerCommand sc in newCmds)
{
if (sc.CommandType.Equals("HUDmsg"))
{
if (sc.ToWho.Equals("All"))
GamePlay.gpHUDLogCenter(sc.Command);
else if (sc.ToWho.Equals("Red"))
{
sendScreenMessageTo(1, sc.Command, null);
}
else if (sc.ToWho.Equals("Blue"))
{
sendScreenMessageTo(2, sc.Command, null);
}
else
{
if (GamePlay.gpRemotePlayers() != null || GamePlay.gpRemotePlayers().Length > 0)
{

foreach (Player p in GamePlay.gpRemotePlayers())
{
if (p.Name() == sc.ToWho)
GamePlay.gpLogServer(new Player[] { p }, sc.Command, null);
}
}
// Message is for a specific player based on player name in string sc.ToWho
}
}
}
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.ProcessCommands - Exception: " + ex);
}
}

public override void OnActorCreated(int missionNumber, string shortName, AiActor actor)
{
#region stats
base.OnActorCreated(missionNumber, shortName, actor);
// Add actor to list of all Actors
if (!allActors.ContainsKey(shortName))
allActors.Add(shortName, actor);
try
{
stats.newActor(shortName, actor);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnActorCreated - Exception: " + ex);
}
#endregion
}


public override void OnPersonHealth(maddox.game.world.AiPerson person, maddox.game.world.AiDamageInitiator initiator, float deltaHealth)
{
#region stats
base.OnPersonHealth(person, initiator, deltaHealth);
try
{
stats.playerHealth(person, initiator, deltaHealth);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnPersonHealth - Exception: " + ex);
}
#endregion

}

public override void OnPersonParachuteFailed(maddox.game.world.AiPerson person)
{
#region stats
base.OnPersonParachuteFailed(person);
try
{
stats.personParachute("Failed", person);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnPersonParachuteFailed - Exception: " + ex);
}
#endregion
}

public override void OnPersonParachuteLanded(maddox.game.world.AiPerson person)
{
#region stats
base.OnPersonParachuteLanded(person);
try
{
stats.personParachute("Landed", person);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnPersonParachuteLanded - Exception: " + ex);
}
#endregion
}

public override void OnPlayerArmy(maddox.game.Player player, int army)
{
#region stats
base.OnPlayerArmy(player, army);
try
{
stats.playerArmy(player, army);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnPlayerArmy - Exception: " + ex);
}
#endregion

}

public override void OnPlayerConnected(maddox.game.Player player)
{
#region stats
base.OnPlayerConnected(player);
try
{
stats.pilotInfo(player);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnPlayerConnected - Exception: " + ex);
}

#endregion
// Your code here
}
public override void OnPlayerDisconnected(maddox.game.Player player, string diagnostic)
{
#region stats
base.OnPlayerDisconnected(player, diagnostic);
try
{
stats.playerDisconnect(player, diagnostic);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnPlayerDisconnected - Exception: " + ex);
}

#endregion
// Your code here
}

public override void OnPlaceEnter(Player player, AiActor actor, int placeIndex)
{
#region stats
base.OnPlaceEnter(player, actor, placeIndex);
try
{
Point2d actorPos = new Point2d(actor.Pos().x, actor.Pos().y);
String startingGrid = GamePlay.gpSectorName(actorPos.x, actorPos.y).ToString();
stats.sortieBegin(player, actor, placeIndex, actorPos, startingGrid);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnPlaceEnter - Exception: " + ex);
}
#endregion
//add your code here
}


public override void OnPlaceLeave(Player player, AiActor actor, int placeIndex)
{
#region stats
base.OnPlaceLeave(player, actor, placeIndex);
try
{
stats.sortieEnd(player, actor, placeIndex);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnPlaceLeave - Exception: " + ex);
}

#endregion
//add your code here
Timeout(1, () =>
{ damageAiControlledPlane(actor); }
);
}

public override void OnAircraftCrashLanded (int missionNumber, string shortName, AiAircraft aircraft)
{
#region stats
base.OnAircraftCrashLanded (missionNumber, shortName, aircraft);
try
{
Point2d actorPos = new Point2d(aircraft.Pos().x, aircraft.Pos().y);
String gridRef = GamePlay.gpSectorName(actorPos.x, actorPos.y).ToString();
stats.aircraftLanded("CrashLanded", shortName, aircraft, actorPos, gridRef);
System.Console.WriteLine("Stats.OnAircraftCrashLanded - ("+shortName+")");
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnAircraftCrashLanded - Exception: " + ex);
}
#endregion
//add your code here
Timeout (300, () =>
{ destroyPlane(aircraft); }
);
}

public override void OnAircraftTookOff(int missionNumber, string shortName, AiAircraft aircraft)
{
#region stats
base.OnAircraftTookOff(missionNumber, shortName, aircraft);
try
{
stats.aircraftTakeoff(shortName, aircraft);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnAircraftTookOff - Exception: " + ex);
}
#endregion
//add your code here
}

public override void OnAircraftLanded (int missionNumber, string shortName, AiAircraft aircraft)
{
#region stats
base.OnAircraftLanded(missionNumber, shortName, aircraft);
try
{
Point2d actorPos = new Point2d(aircraft.Pos().x, aircraft.Pos().y);
String gridRef = GamePlay.gpSectorName(actorPos.x, actorPos.y).ToString();
stats.aircraftLanded("Landed", shortName, aircraft, actorPos, gridRef);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnAircraftTookOff - Exception: " + ex);
}
#endregion
//add your code here

Timeout(300, () =>
{ destroyPlane(aircraft); }
);
}

public override void OnActorDamaged(int missionNumber, string shortName, AiActor actor, AiDamageInitiator initiator, NamedDamageTypes damageType)
{
#region stats
base.OnActorDamaged(missionNumber, shortName, actor, initiator, damageType);
try
{
stats.missionActorDamaged(shortName, actor, initiator, damageType);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnActorDamaged - Exception: " + ex);
}
#endregion
//add your code here

}

public override void OnAircraftDamaged(int missionNumber, string shortName, AiAircraft aircraft, AiDamageInitiator initiator, NamedDamageTypes damageType)
{
#region stats
base.OnAircraftDamaged(missionNumber, shortName, aircraft, initiator, damageType);
try
{
stats.missionAircraftDamaged(shortName, aircraft, initiator, damageType);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnAircraftDamaged - Exception: " + ex);
}
#endregion
//add your code here

}
public override void OnAircraftLimbDamaged(int missionNumber, string shortName, AiAircraft aircraft, AiLimbDamage limbDamage)
{
#region stats
base.OnAircraftLimbDamaged(missionNumber, shortName, aircraft, limbDamage);
try
{
stats.aircraftLimbDamaged(shortName, aircraft, limbDamage);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnAircraftLimbDamaged - Exception: " + ex);
}
#endregion
//add your code here
}
public override void OnAircraftCutLimb(int missionNumber, string shortName, AiAircraft aircraft, AiDamageInitiator initiator, LimbNames limbName)
{
#region stats
base.OnAircraftCutLimb(missionNumber, shortName, aircraft, initiator, limbName);
try
{
stats.missionAircraftCutLimb(shortName, aircraft, initiator, limbName);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnAircraftCutLimb - Exception: " + ex);
}
#endregion
//add your code here
}

public override void OnActorDead(int missionNumber, string shortName, AiActor actor, List<DamagerScore> damages)
{
#region stats
base.OnActorDead(missionNumber, shortName, actor, damages);
try
{
stats.missionActorDead(shortName, actor, damages);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnActorDead - Exception: " + ex);
}
#endregion
//add your code here

}

public override void OnActorDestroyed(int missionNumber, string shortName, AiActor actor)
{
#region stats
base.OnActorDestroyed(missionNumber, shortName, actor);
try
{
stats.actorDestroyed(shortName, actor);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnActorDestroyed - Exception: " + ex);
}
#endregion
//add your code here

}
public override void OnAircraftKilled(int missionNumber, string shortName, AiAircraft aircraft)
{
#region stats
base.OnAircraftKilled(missionNumber, shortName, aircraft);
try
{
stats.aircraftKilled(shortName, aircraft);
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnAircraftKilled - Exception: " + ex);
}
#endregion
//add your code here

}

public override void OnBattleStoped()
{
#region stats
base.OnBattleStoped();
try
{
stats.battleStopped();
// Loop through list of AiActors and destroy them all
List<string> keys = new List<string>(allActors.Keys);
for (int i = 0; i < keys.Count; i++)
{
AiActor a = allActors[keys[i]];
AiAircraft aircraft = a as AiAircraft;
if (aircraft != null)
{
aircraft.Destroy();
}
else
{
AiGroundActor aiGroundActor = a as AiGroundActor;
if (aiGroundActor != null)
{
aiGroundActor.Destroy();
}
else
{
System.Console.WriteLine("Stats.OnBattleStoped - Unknown Actor (" + a.Name()+") ShortName ("+keys[i]+")");
}
}
}
stats.disconnectStats();
}
catch (Exception ex)
{
System.Console.WriteLine("Stats.OnBattleStoped - Exception: " + ex);
}
#endregion
//add your code here
}
//////////////////////////////////////////////////////////////////////////////////////////////////



}



console gives that every 20-30sec


at System.Runtime.Remoting.Proxies.RealProxy.HandleRe turnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateI nvoke(MessageData& msgData, Int32 type)
at maddox.game.IBattle.OnTickGame()
at maddox.game.GameDef.tickGame()
at uRQMLX6vlhrpBgfbWjC.eBu9gE6TS67VJSjj97t.Hwe9ADXhn0 AxpFMw7lv0(Object )
at uRQMLX6vlhrpBgfbWjC.eBu9gE6TS67VJSjj97t.8An9qLaqwN d()
at 93bAC3gAbOoH4F4mw53.Pljjc2gU48bdLjaViwW.Af3oASrUh2 7g1Z9hCmya(Object )
at 93bAC3gAbOoH4F4mw53.Pljjc2gU48bdLjaViwW.9B8JM8x755 B(Boolean , Boolean )
=================================================
=================================================
System.NullReferenceException: Object reference not set to an instance of an object.
Server stack trace:
at Mission.sendMessagesToAirgroup(AiAirGroup from, Double maxDistance)
at Mission.OnTickGame()
at maddox.game.ABattle.OnTickGame()
at maddox.game.world.Strategy.OnTickGame()
at System.Runtime.Remoting.Messaging.StackBuilderSink ._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]& outArgs)
at System.Runtime.Remoting.Messaging.StackBuilderSink .SyncProcessMessage(IMessage msg, Int32 methodPtr, Boolean fExecuteInContext)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleRe turnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateI nvoke(MessageData& msgData, Int32 type)
at maddox.game.IBattle.OnTickGame()
at maddox.game.GameDef.tickGame()
at uRQMLX6vlhrpBgfbWjC.eBu9gE6TS67VJSjj97t.Hwe9ADXhn0 AxpFMw7lv0(Object )
at uRQMLX6vlhrpBgfbWjC.eBu9gE6TS67VJSjj97t.8An9qLaqwN d()
at 93bAC3gAbOoH4F4mw53.Pljjc2gU48bdLjaViwW.Af3oASrUh2 7g1Z9hCmya(Object )
at 93bAC3gAbOoH4F4mw53.Pljjc2gU48bdLjaViwW.9B8JM8x755 B(Boolean , Boolean )
=================================================




is that because there is no airgroups loaded yet?

FG28_Kodiak
04-08-2012, 07:26 PM
is that because there is no airgroups loaded yet?

Yepp:
Added
if (EnemyAirgroups == null) return;
to method sendMessagesToAirgroup
and
if (GamePlay.gpAirGroups(1) != null)
in OnTickGame
to avoid exception


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


public class Mission : AMission
{

public void sendMessagesToAirgroup(AiAirGroup from, double maxDistance)
{

AiAirGroup[] EnemyAirgroups;
Point3d StartPos = new Point3d(from.Pos().x, from.Pos().y, 1.0);

EnemyAirgroups = GamePlay.gpAirGroups((from.Army() == 1) ? 2 : 1);
if (EnemyAirgroups == null) return;

int i = 0;

foreach (AiAirGroup aag in EnemyAirgroups)
{
Point3d enemyPosition = new Point3d(aag.Pos().x, aag.Pos().y, 1.0);

if (from.Pos().distance(ref enemyPosition) < maxDistance)
{

string sectorName = GamePlay.gpSectorName(aag.Pos().x, aag.Pos().y);

string[] splittet = sectorName.Split(',');
string alpha = splittet[0];
string number = "n" + splittet[1];

AiAircraft LeaderPlane = (from.GetItems()[0] as AiAircraft);

Timeout(i, () =>
{
(LeaderPlane as AiAircraft).SayToGroup(from as AiAirGroup, "Enemy_planes");
});


Timeout(i += 2, () =>
{
LeaderPlane.SayToGroup(from, "In_square");
});

Timeout(i += 2, () =>
{
LeaderPlane.SayToGroup(from, alpha);
});
Timeout(i += 2, () =>
{
LeaderPlane.SayToGroup(from, number);
});

i += 2;
}
}
}


public override void OnTickGame()
{
base.OnTickGame();

if (Time.tickCounter() % 600 == 299)
{
if (GamePlay.gpAirGroups(1) != null)
foreach(AiAirGroup aag in GamePlay.gpAirGroups(1))
sendMessagesToAirgroup(aag, 100000.0);

}
}
}

5./JG27.Farber
04-08-2012, 11:26 PM
you wanna ask the operators with the mission menu? and than after 4min you will get the response? This is possible without problem.
Okay for clearance, when we use "radars" will you give the messages to the Airgroups in range of a spezific radarstation or to all. So if you hit the 'radarbutton' then you get Information of all Enemies Airgoups in range of any available radarstation or only from this you are in Range?

Hmm if the radar closest gave the information that would be good otherwise it is as if the aircraft has the radar, or maybe not? :confused:

How would you ask the radar? :confused:

FG28_Kodiak
04-09-2012, 03:54 AM
via MissionMenu.

csThor
04-09-2012, 07:55 AM
If my input is allowed. I agree with Kodiak about the menu thing. Think of it as a Squadron Leader "registering" with a certain Sector Station Ops Room to receive RADAR information. That could be easily done either by making this selection available in the menu (i.e. Select Sector -> Biggin Hill, Kenley, Tangmere etc - if you want more structure and accept a slightly higher workload for the squadron leader) or simply via a distance check for the airgroup (if it's supposed to be easier).

I do, however, suggest to take a look at how the Squad Select Series at Warbirds and the Friday Squad Ops at Aces High II operate. They appoint a CO prior to a run and he has to devise a general plan. Since we lack the numbers they can (theoretically) operate with there would, of course, be some adaptations necessary. My suggestion would be:

1.) Select one squad for CO duty prior to the run. Rotate this position within the squad pool after each run.
2.) Give the CO a number of targets he can choose from. For example a mission set in August 1940 would give the Luftwaffe CO a list of primary airfields of which he has to hit a certain number (depending on number of players, I guess no more than two for CloD) plus a list of secondary targets which may give the LW additional points. This way you create a more realistic Fog of War situation in which the RAF doesn't know which targets will be hit by the LW in advance.

For the RAF this CO duty is, of course, not that important. Later it will certainly become more important once we move to theaters which include offensive ops by both sides.

_79_dev
04-09-2012, 08:22 AM
Kodiak, Your script for generating voice messages due to distance from blue Airgroup seems to be working fine on server console, however I can't test it physically my self cause I am on holidays and using IPod at the moment (can't run Cliffs of Dover on iPod...yet). If someone can give us a hand pleas go to 5jg27.net there is direct connect button to server.

Just one more question, by the look at the script It supose to give radio messages every x amount of seconds or do I have to go to TAB menu to ask for message? (just getting confused here)

FG28_Kodiak
04-09-2012, 09:19 AM
At the moment with time amount, but it's no problem doing this via menu. But at the moment there is the problem with the missionmenu for the red side on dediserver, so it's better to wait for the patch, and hope the patch solving this problem.
How many of this Sector Station Ops Room where available at BoB? How many radars and other observers was attached to one station?

Osprey
04-09-2012, 09:43 AM
Hi Kodiak, whilst Farber has done the vast majority of the work and it is a JG27 campaign, I am providing a large number of his enemy fighters for it (looking forward to that :) ) and I have been working with him on how this runs (though the buck stops with him).

you wanna ask the operators with the mission menu? and than after 4min you will get the response? This is possible without problem.
Okay for clearance, when we use "radars" will you give the messages to the Airgroups in range of a spezific radarstation or to all. So if you hit the 'radarbutton' then you get Information of all Enemies Airgoups in range of any available radarstation or only from this you are in Range?

Hmm if the radar closest gave the information that would be good otherwise it is as if the aircraft has the radar, or maybe not? :confused:

How would you ask the radar? :confused:

Yes, mission menu! This is what I suggested in the first place. The flight leaders select #1

http://i7.photobucket.com/albums/y294/JerryFodder/sampleselection.png

Everyone else selects something else, therefore this means not everyone gets the menu option and have to be organised by the squadron leader. Only 3 or 4 people on RAF would even have this ability so their workload goes up and the pressure for their team to stay together in flight becomes very important.

The information provided should be:
1. Location
2. Approximate numbers (30+??)
3. Height
4. Heading

The flight lead has to then direct his crew to intercept. If this can be provided vocally then that is even better.

Fighter Command had a WHOLE picture of the area via the plotting room bunker. This would receive all radar information from the RDF stations and observer corps, then scramble accordingly. @FARBER I imagine that for the purposes of campaign, that this event, ie "501 squadron scramble" could be handle by trigger. That way we start at the right time from the right place. I wouldn't want to be on some random patrol for ages because I don't know what's going on and then find ourselves 180 miles away with 1/2 tank of fuel left. Sorry to talk mission but I would suggest participating RAF squadrons being "At Readiness" with kites ready to scramble waiting for the off would be most realistic.

4 minutes delay
This 4 minutes delay for response is interesting since information cannot be absolutely up to date. I'm trying to find out the delay between RDF station and reporting to the squadrons. 4 mins seems a long time, you could cross the Channel in that time. An alternative suggestion is that the flight leaders have a time delay between plot requests of a few minutes, so once they request and get feedback they cannot select again until that time has expired - this would add in the random factor for LW course changes and human error until visual confirmation, it would stop a flight leader from just hammering the button!


This is why I love this stuff, because you learn so much about it all as you go :) I read that Ground Controlled Interception didn't occur until later because the RDF stations needed to scan the whole sky, therefore a squadron was scrambled and vectored to an interception position and that was it, though it was accurate. There was no 'within range of a station' since the entire coast was covered as one. It's really this we need to replicate. The difficulty would be that since we don't have the whole of 11 group in the mission then the Luftwaffe could 'slip the net' so we probably could do with a bit more reliability.
Here's a nice overview
http://www.radarpages.co.uk/mob/gci/gci.htm


If my input is allowed. I agree with Kodiak about the menu thing. Think of it as a Squadron Leader "registering" with a certain Sector Station Ops Room to receive RADAR information. That could be easily done either by making this selection available in the menu (i.e. Select Sector -> Biggin Hill, Kenley, Tangmere etc - if you want more structure and accept a slightly higher workload for the squadron leader) or simply via a distance check for the airgroup (if it's supposed to be easier).

I do, however, suggest to take a look at how the Squad Select Series at Warbirds and the Friday Squad Ops at Aces High II operate. They appoint a CO prior to a run and he has to devise a general plan. Since we lack the numbers they can (theoretically) operate with there would, of course, be some adaptations necessary. My suggestion would be:

1.) Select one squad for CO duty prior to the run. Rotate this position within the squad pool after each run.
2.) Give the CO a number of targets he can choose from. For example a mission set in August 1940 would give the Luftwaffe CO a list of primary airfields of which he has to hit a certain number (depending on number of players, I guess no more than two for CloD) plus a list of secondary targets which may give the LW additional points. This way you create a more realistic Fog of War situation in which the RAF doesn't know which targets will be hit by the LW in advance.

For the RAF this CO duty is, of course, not that important. Later it will certainly become more important once we move to theaters which include offensive ops by both sides.

I see where you are coming from but I wouldn't want to sit out and direct anyone, and if you have a list of targets which the LW can hit then chances of even meeting in the sky are slim - which is the whole point. I don't care for tactics, winning or losing, I'm interested in experiencing the same as squadrons did in the BoB (minus the death).

Osprey
04-09-2012, 09:48 AM
At the moment with time amount, but it's no problem doing this via menu. But at the moment there is the problem with the missionmenu for the red side on dediserver, so it's better to wait for the patch, and hope the patch solving this problem.
How many of this Sector Station Ops Room where available at BoB? How many radars and other observers was attached to one station?


DANG! So the custom menu code is not available on dedicated servers? I must admit, I couldn't get it to work but I put it down to me being rubbish. Is there a way you can fire an event to the server to obtain that radar information?

Please view my above post for further context, they are just ideas.

Osprey
04-09-2012, 09:57 AM
About the 4 minutes delay. It appears to be bang on because the RDF data went through a filter station (where people examined the plotted information) before being passed to the FC bunker - this website is definitely worth a read.

http://www.ventnorradar.co.uk/CH.htm

"The edited data was assembled as markers on a large plotting table and this showed the situation as it had been something like four minutes previously : since then the bombers would have flown about another fifteen miles"

This is interesting too. Vector was only confirmed with the second reading.
"Repeated plots became the direction of travel (vector) with the height and estimated number of aircraft repeatedly confirmed"

CH was blind past the coast and then the OC was used. But was the delay resolved for the BoB???
"With the separate raids thus identified, the information was passed to an Operations room staff who could then make the tactical decisions regarding the deployment and vectoring of the defending aircraft, either those already in the air or presently on the ground, towards their ever moving targets. It was found that those best equipped to calculate the required courses were recuperating experienced pilots as they were able to better visulise the everchanging relationships between defending and attacking aircraft. However, once the enemy aircraft had crossed the coast the CH RDF could no longer detect them and then the Royal Observer Corps reports to the Filter Room became the sole means of tracking the enemy."

"The system of having to use correction charts before reporting plots to the Filter Room contributed to the four minute delay and and sometimes of course the human factor introduced errors. This problem was solved by 'The Calculator'. Designed and installed by the Post Office ( which later constructed the Colossi computers for Bletchley Park) and using relays and uniselectors, this little known and uncelebrated early form of computer automatically added the correction factors to the input plots and displayed the results visually as the grid reference. The machine could also correct heights in the same way and a mechanically linked teleprinter could send the data by telephone line to the remote Filter Room. Ventnor was equipped with its first calculator in June 1940 and received its second in April 1941."

csThor
04-09-2012, 10:07 AM
I see where you are coming from but I wouldn't want to sit out and direct anyone, and if you have a list of targets which the LW can hit then chances of even meeting in the sky are slim - which is the whole point.

Err ... no. The CO position is essentially a pre-flight position and is not meant to be RAF Fighter Command CO or commanding General of a german Fliegerkorps during the game. Once the planning is done the CO reverts to flying as usual. I don't know what number of players and squads you're planning with but given the obvious limitations on player numbers and ability to display a lot of aircraft I guess we're talking about maybe forty to fifty people at best so the target area and the target selection will always be within the confines of this limit. Going back to my example of an August 1940 mission I'd envision the german task to hit at least one of the primary targets (i.e. Biggin Hill, Kenley, Croydon) and also offers secondary targets such as radar stations, harbors and secondary airfields.

And just to make that absolutely clear I am not talking about using the whole width of the map if it doesn't reflect the number of players engaged. Right now that is simply not possible from the technical side as we don't see 128 player servers working and being filled to that point (plus a load of AI for the bombers which few people fly, anyway). The target zone would be limited to one of the areas assigned to the german Luftflotten (Luftflotte 3 = west of Seine, Luftflotte 2 = east of Seine). This way both RAF and LW would meet in one area and not play chicken with each other.

I don't care for tactics, winning or losing, I'm interested in experiencing the same as squadrons did in the BoB (minus the death).

The problem is you can't exclude tactics if you want that immersion. The Luftwaffe's bombers would be jubilant if they could fly unmolested. ;)

Osprey
04-09-2012, 10:13 AM
Sort of reminds me of the USL in that there was a known mission and both sides planned for it plus if someone knew what was going to happen though then it sort of spoils it for them.

Although it would be nice, I wouldn't expect Farber to have to tail a bomber with flaps down lol I can live without that total repetition!

csThor
04-09-2012, 11:31 AM
Never heard anything about the USL so I can't compare but essentially I envision a system in which the RAF will not know what the LW will attack until the action is underway. It's not that I want the LW to announce its targets before flying ... that would make the whole system pointless (all that will be known in advance is the area of operations - ergo whether the west or the east of the Channel map will be used).
Tactically the squadrons should be given not more than a general task but not told "you must do close escort for the bombers". All these considerations should be up to the squad.

5./JG27.Farber
04-09-2012, 04:11 PM
I don't know what number of players and squads you're planning with but given the obvious limitations on player numbers and ability to display a lot of aircraft I guess we're talking about maybe forty to fifty people at best....

We had around 70 last time at the peak if I recall. Were hoping to fill the server. :-P

Good Post Osprey. Heh 4 minutes hey? Good guess then. You guys can sit on the ground in readyness, no problem.

FG28_Kodiak
04-09-2012, 06:37 PM
Ok example for the mission menu:
Select red side then TAB->4 at moment the "radar" is not functional, menu at the moment is an example, so feel free to comment :rolleyes:

for rest i must first read the posts above :rolleyes:


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


public class Mission : AMission
{
#region class Menu

internal class Menu
{
internal class MenuEntry
{
internal string MenuName { get; set; }
internal bool active { get; set; }
}

internal List<MenuEntry> menuEntries = new List<MenuEntry>();

public void AddMenuEntry(string description, bool active)
{
MenuEntry NewMenuEntry = new MenuEntry();

NewMenuEntry.MenuName = description;
NewMenuEntry.active = active;

menuEntries.Add(NewMenuEntry);
}

public string[] GetMenuDescriptions()
{
List<string> Descriptions = new List<string>();

menuEntries.ForEach(item =>
{
Descriptions.Add(item.MenuName);
});

return Descriptions.ToArray();
}

public bool[] GetActives()
{
List<bool> Actives = new List<bool>();

menuEntries.ForEach(item =>
{
Actives.Add(item.active);
});

return Actives.ToArray();
}

public bool IsValid()
{
if (menuEntries == null || menuEntries.Count < 1)
return false;
else
return true;

}

}

#endregion


internal class Pilot
{
public Player player { get; set; }
public LocalHeadquarters connectedHeadquarter;

public Pilot(Player player)
{
this.player = player;
}
}


internal List<Pilot> PilotsInGame = new List<Pilot>();


internal class LocalHeadquarters
{
public string Name { get; set; }
public Point2d LocationCoords { get; set; }
public List<string> AttachedRadarStations = new List<string>();

public LocalHeadquarters(string name, double x, double y)
{
this.Name = name;
this.LocationCoords = new Point2d(x, y);
}
}


List<LocalHeadquarters> Headquarters = new List<LocalHeadquarters>{
{new LocalHeadquarters("Alpha", 100.0, 100.0)},
{new LocalHeadquarters("Beta", 200.0, 200.0)},
{new LocalHeadquarters("Gamma", 300.0, 300.0)},
{new LocalHeadquarters("Delta", 400.0, 400.0)}};



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


if (!PilotsInGame.Exists(item => item.player == player))
{
PilotsInGame.Add(new Pilot(player));
}


PilotsInGame.ForEach(item =>
{
GamePlay.gpLogServer(null, "Spieler: {0}", new[] {item.player.Name() });
});

SetMainMenu(player);
}


public void SetMainMenu(Player player)
{
if (player.Army() == 1) // red Side
GamePlay.gpSetOrderMissionMenu(player, false, 0, new string[] { "Radaroperations" }, new bool[] { true });
else
GamePlay.gpSetOrderMissionMenu(player, false, 0, new string[] { "Not Available" }, new bool[] { true });
}


public void SetRadarMenu(Player player)
{
string headQuarter = "Select Headquarter";
string connectedTo = "";

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);

if (PilotsInGame[i].connectedHeadquarter != null)
connectedTo = string.Format(" (connected: {0})", PilotsInGame[i].connectedHeadquarter.Name);
}
headQuarter += connectedTo;

GamePlay.gpSetOrderMissionMenu(player, true, 100, new string[] { headQuarter, "Get Radar Information" }, new bool[] { true, true });

}


public void SetConnectToHeadquarterMenu(Player player)
{
Menu NewMenu = new Menu();
LocalHeadquarters headQuarter = null;

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);
headQuarter = PilotsInGame[i].connectedHeadquarter;
}

Headquarters.ForEach(item =>
{
if (headQuarter != null && item == headQuarter)
{
NewMenu.AddMenuEntry(item.Name + " *", true);
}
else
NewMenu.AddMenuEntry(item.Name, true);
});

if (NewMenu.IsValid())
GamePlay.gpSetOrderMissionMenu(player, true, 101, NewMenu.GetMenuDescriptions() , NewMenu.GetActives());
else
GamePlay.gpSetOrderMissionMenu(player, true, 101, new string[]{ "Not available" }, new bool[]{true});
}


public void SetHeadQuarter(Player player, int menuItemIndex)
{
if (PilotsInGame.Exists(item => item.player == player))
PilotsInGame[PilotsInGame.FindIndex(item => item.player == player)].connectedHeadquarter = Headquarters[menuItemIndex-1];

}


public override void OnOrderMissionMenuSelected(Player player, int ID, int menuItemIndex)
{


if (ID == 0)
{ // main menu
if (menuItemIndex == 1)
{
SetRadarMenu(player);
}
//if (menuItemIndex == 2)
//{


//}
//if (menuItemIndex == 3)
//{


//}
}


if (ID == 100)
{
//Radar Menu
if (menuItemIndex == 1)
{
SetConnectToHeadquarterMenu(player);
}

if (menuItemIndex == 2)
{
SetMainMenu(player);
}


if (menuItemIndex == 0)
SetMainMenu(player);
}

if (ID == 101)
{
//Radar Menu
if (menuItemIndex == 0)
SetRadarMenu(player);
else
{
SetHeadQuarter(player, menuItemIndex);
SetRadarMenu(player);
}
}
}
}

5./JG27.Farber
04-09-2012, 08:50 PM
The menu works. I can attach myself to one of the radaroperators, Alpha, Beta, Gamma and Delta.

I can also see the request option.

FG28_Kodiak
04-09-2012, 09:09 PM
Any wishes for the captions of the menus?

_79_dev
04-09-2012, 09:51 PM
I am trying my best but I can't get to work voice messages script invoked by time for some reason.
I persume that it will be adopted for this menu script... Is it correct?

5./JG27.Farber
04-09-2012, 10:18 PM
Any wishes for the captions of the menus?

Select Ground Control Frequency? Or Select Frequency?

Channel 1,2,3,4?

Make Request?


Would be nice if when you asked for information the voice actor acknoledged and said, "Roger, Standby "players callsign letter""


What do the channels actually mean/do/will do?

FG28_Kodiak
04-10-2012, 04:40 AM
@_79_dev
Yes would be the better way, the first example was only for showing the possibilies.

@5./JG27.Farber

..voice actor acknoledged and said, "Roger, Standby "players callsign letter"

If there are ogg files present it's possible, but i must first look for it. May be there is a way to create own ogg files for this, but not tested it at the moment.

What do the channels actually mean/do/will do?

Plan is to attach sectors (channel 1: C1..D4 etc.) to be observed to a "channel or headquarter", if the player make a request, he get the information for each enemy Airgroup in these sectors. The number of available "channels" depens of the distance to them, if two in range the player can select one of the two, if none of them in range, no radar information is present etc. May be the player is transfered to an other "headquarter" when leaving the Area from another. There are many possibilities.

bolox
04-10-2012, 05:59 AM
May be there is a way to create own ogg files for this, but not tested it at the moment.


I have tried this with no success (probably me being stupid :rolleyes:)
http://forum.1cpublishing.eu/showthread.php?t=30245
Would open up some nice possibilities if can be done- Desastersoft seems to have added 8 sound files for their demo (hidden away in //...steamapps/.../desastersoft/wick vs dundas/sounds)

FG28_Kodiak
04-10-2012, 09:07 AM
Have tried it also but not very intensive :grin:, no result.

Another possibility should be the Soundplayer integrated in System.Media. But i don't think it work on Multiplayer, scripts are serverbased. For singleplayer missions it should work.
But we only interested in Multiplayer :cool:

5./JG27.Farber
04-11-2012, 07:17 AM
By the callsign letter, this is the one you choose in game. I choose "Falke" for farber for example. I have heard the computer call it to me, or is this only in offline?

FG28_Kodiak
04-11-2012, 08:01 AM
I can get the airgroup callsign via script, but must test if this is able for a single player.

Osprey
04-11-2012, 10:49 AM
I've been absent, but Kodiak this is very promising stuff!! :)

I'd like to help with this, I have ideas but I'm at work so I will post later on.

~S~ and many thanks!

5./JG27.Farber
04-11-2012, 12:25 PM
I can get the airgroup callsign via script, but must test if this is able for a single player.

You mean multiplayer? :-P

FG28_Kodiak
04-11-2012, 01:28 PM
Yep a single player in multiplayer :-P

FG28_Kodiak
04-11-2012, 02:32 PM
Ok Problem, there are callsighn?? ogg present, but if i try to play them via SayToGroup nothing happens. :(
So it seems it's better to wait for the next patch, hope the problem is solved.

Osprey
04-11-2012, 03:45 PM
I think that customising sounds to individuals may be a step too far for now, the existing sounds seem to have most of the comments needed to piece together a decent instruction. I'll write up some design/requirements and example scenarios in which existing logic could be used to create those instructions. I think we could start with some basic vocal instruction and extend the logic in pieces to improve the description as that is proven out.

I need a chat with Farber about his requirements from a German pilot perspective. We need to be careful here, and I'm not trying to gain advantage for the RAF BUT it should be noted that in 1940 it was not possible for German fighter pilots to talk on radios to German bomber crews because they had completely different sets and 'crystals'. This is explained in Ulrich Stienhilpers book "Spitfire on my Tail" who was responsible for radio comms for his squadron. Of course I am open to correction on this, and I'm keen to learn about any use of German radar in directing fighter squadrons - again I don't think there was any, they used radar for attack eg finding ships to target in the Channel.

5./JG27.Farber
04-11-2012, 03:51 PM
No, fighters could not communicate with bombers and niether will we, as most if not all will be AI.

FG28_Kodiak
04-11-2012, 04:23 PM
Argh sometimes i could the devs ... :grin:

Callsigns work, the oggs are called callsign1 .. etc. but you must write CallSign1 in code :rolleyes:

Osprey
04-11-2012, 04:36 PM
You can't use a variable or parameter?

FG28_Kodiak
04-11-2012, 04:49 PM
Yes i can, but i must it 'translate' it somewhere. :rolleyes:

Ok try of a 'connect' - communication (use the mission-menu ;)):


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


public class Mission : AMission
{
#region class Menu

internal class Menu
{
internal class MenuEntry
{
internal string MenuName { get; set; }
internal bool active { get; set; }
}

internal List<MenuEntry> menuEntries = new List<MenuEntry>();

public void AddMenuEntry(string description, bool active)
{
MenuEntry NewMenuEntry = new MenuEntry();

NewMenuEntry.MenuName = description;
NewMenuEntry.active = active;

menuEntries.Add(NewMenuEntry);
}

public string[] GetMenuDescriptions()
{
List<string> Descriptions = new List<string>();

menuEntries.ForEach(item =>
{
Descriptions.Add(item.MenuName);
});

return Descriptions.ToArray();
}

public bool[] GetActives()
{
List<bool> Actives = new List<bool>();

menuEntries.ForEach(item =>
{
Actives.Add(item.active);
});

return Actives.ToArray();
}

public bool IsValid()
{
if (menuEntries == null || menuEntries.Count < 1)
return false;
else
return true;

}

}

#endregion


internal class Pilot
{
public Player player { get; set; }
public LocalHeadquarters connectedHeadquarter;

public Pilot(Player player)
{
this.player = player;
}
}


internal List<Pilot> PilotsInGame = new List<Pilot>();


internal class LocalHeadquarters
{
public string Name { get; set; }
public Point2d LocationCoords { get; set; }
public string Callsign { get; set; }
public List<string> AttachedSectors = new List<string>();

public LocalHeadquarters(string name, string callsign, double x, double y)
{
this.Name = name;
this.Callsign = callsign;
this.LocationCoords = new Point2d(x, y);
}
}


List<LocalHeadquarters> Headquarters = new List<LocalHeadquarters>{
{new LocalHeadquarters("Luton", "CallSign24", 100.0, 100.0)},
{new LocalHeadquarters("RadPoe", "CallSign32", 200.0, 200.0)},
{new LocalHeadquarters("Forest", "CallSign15", 300.0, 300.0)},
{new LocalHeadquarters("Bearskin", "CallSign5", 400.0, 400.0)}};



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


if (!PilotsInGame.Exists(item => item.player == player))
{
PilotsInGame.Add(new Pilot(player));
}

GamePlay.gpLogServer(null, "Callsign: {0}", new[] { (actor as AiAircraft).CallSign() });

SetMainMenu(player);
}


private void connectToHeadquarterSpeech(AiAircraft aircraft, LocalHeadquarters localHQ)
{
double initTime = 0.0;

aircraft.SayToGroup(aircraft.AirGroup(), "Hello_guys");

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "This_is");
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), localHQ.Callsign);
});


// to is missing as ogg


Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), aircraft.CallSign());
});



Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "leader__");
});


}



public void SetMainMenu(Player player)
{
if (player.Army() == 1) // red Side
GamePlay.gpSetOrderMissionMenu(player, false, 0, new string[] { "Radaroperations" }, new bool[] { true });
else
GamePlay.gpSetOrderMissionMenu(player, false, 0, new string[] { "Not Available" }, new bool[] { true });
}


public void SetRadarMenu(Player player)
{
string headQuarter = "Select Headquarter";
string connectedTo = "";

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);

if (PilotsInGame[i].connectedHeadquarter != null)
connectedTo = string.Format(" (connected: {0})", PilotsInGame[i].connectedHeadquarter.Name);
}
headQuarter += connectedTo;

GamePlay.gpSetOrderMissionMenu(player, true, 100, new string[] { headQuarter, "Get Radar Information" }, new bool[] { true, true });

}


public void SetConnectToHeadquarterMenu(Player player)
{
Menu NewMenu = new Menu();
LocalHeadquarters headQuarter = null;

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);
headQuarter = PilotsInGame[i].connectedHeadquarter;
}

Headquarters.ForEach(item =>
{
if (headQuarter != null && item == headQuarter)
{
NewMenu.AddMenuEntry(item.Name + " *", true);
}
else
NewMenu.AddMenuEntry(item.Name, true);
});

if (NewMenu.IsValid())
GamePlay.gpSetOrderMissionMenu(player, true, 101, NewMenu.GetMenuDescriptions() , NewMenu.GetActives());
else
GamePlay.gpSetOrderMissionMenu(player, true, 101, new string[]{ "Not available" }, new bool[]{true});
}


public void SetHeadQuarter(Player player, int menuItemIndex)
{
if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);
PilotsInGame[i].connectedHeadquarter = Headquarters[menuItemIndex-1];
if (player.Place() != null)
connectToHeadquarterSpeech((player.Place() as AiAircraft), PilotsInGame[i].connectedHeadquarter);
}

}


public override void OnOrderMissionMenuSelected(Player player, int ID, int menuItemIndex)
{


if (ID == 0)
{ // main menu
if (menuItemIndex == 1)
{
SetRadarMenu(player);
}
//if (menuItemIndex == 2)
//{


//}
//if (menuItemIndex == 3)
//{


//}
}


if (ID == 100)
{
//Radar Menu
if (menuItemIndex == 1)
{
SetConnectToHeadquarterMenu(player);
}

if (menuItemIndex == 2)
{
SetMainMenu(player);
}


if (menuItemIndex == 0)
SetMainMenu(player);
}

if (ID == 101)
{
//Radar Menu
if (menuItemIndex == 0)
SetRadarMenu(player);
else
{
SetHeadQuarter(player, menuItemIndex);
SetRadarMenu(player);
}
}
}
}

Osprey
04-11-2012, 04:54 PM
Oooo you're working ahead of me :) This looks interesting, when I get back from football I'll run it up in VS and try it in a dedicated server. Do I need any special map set up so it knows about the sectors?

FG28_Kodiak
04-11-2012, 04:57 PM
Do you will script this by your own? - No problem i 've some other scriptwork to do. :rolleyes:
At the moment it's only a test for communication, nor other features like sectors implemented.

Osprey
04-11-2012, 07:05 PM
No no, I am rubbish at writing the code myself because I am not trained properly. I can only read it and understand it usually, and write basic logic.

I would just like to offer help with getting this working, I think a lot of people would find 'realistic radar' quite useful :)

_79_dev
04-11-2012, 08:15 PM
Latest script for menu is up on server, ready to be tested... thanks Kodiak... Conect here http://www.5jg27.net/

FG28_Kodiak
04-12-2012, 04:14 AM
I would just like to offer help with getting this working, I think a lot of people would find 'realistic radar' quite useful :)

A great help would be looking what oggs (and sequence) are usefull for communication. So i can 'translate' it direct in code.

salmo
04-12-2012, 06:07 AM
Kodiak,
Here's the speech file (text) with all the ogg phrases in various languages. http://www.freefilehosting.net/speech I'm trying to concatonate various phrases into sentences, without success at the moment. There must be a way, since the inj-game radar speech uses almost complete sentences from the ogg phrases.

FG28_Kodiak
04-12-2012, 06:38 AM
Kodiak,
Here's the speech file (text) with all the ogg phrases in various languages. http://www.freefilehosting.net/speech I'm trying to concatonate various phrases into sentences, without success at the moment. There must be a way, since the inj-game radar speech uses almost complete sentences from the ogg phrases.
I think it depens of the length of a speech file, at the moment i use 3 sec. but the speech files have different lenghts, so it would better to use the exact lenghts + few miliseconds. Your speech.txt is the file for the subtitles, could be usefull, thanks. ;)

Osprey
04-12-2012, 07:11 AM
A great help would be looking what oggs (and sequence) are usefull for communication. So i can 'translate' it direct in code.

I can give you a solution to a minor problem, in one of your scripts you said in comments that there was no "to.ogg" - just use the "2.ogg" instead since it is phonetically the same.


Really sorry I did nothing yesterday. I am changing ISP and since I told them I have been getting cut off every night :(

I have been looking at these and have some ideas on what needs to be said by the radio operator. For instance, I'm interested in the Point3D position of both aircraft, therefore for example

if(pilot.height < aiairgroup.height)
{
//use "climb.ogg" & "2.ogg" angels i;
}
else
{
//use "descend.ogg & "2.ogg" angels i;
}

There are also direction oggs such as "turn right.ogg" but I don't know how sophisticated this can be. Unless it is easy to work out interception vectors then that is probably too much work or a longer term project. It is not needed for now

I would like to know more about the sectors and how you intend to have this working. I have some input for this. There are a few important behaviour requirements for this if possible, briefly:

1. No aircraft reported under 600m (Chain Home Low min operating height)
2. RDF range as far as the French coast (and into France some 50 miles in the Pas De Calais area) A range map is available on Wikipedia

I have an idea for a 4 minute delay but it's probably complicated and not the first thing to implement. It would involve the radar constantly working behind in the script and storing 4 minute old data in variables, then releasing that to the user on request instead of current data. Perhaps if the RDF event fired every minute transparently and the resulting information string stored and passed on every minute into a new variable until the 4th minute, then this was accessed and played to the player. (hope that makes sense)

Speak later ~S~

41Sqn_Banks
04-12-2012, 07:22 AM
I think it depens of the length of a speech file, at the moment i use 3 sec. but the speech files have different lenghts, so it would better to use the exact lenghts + few miliseconds. Your speech.txt is the file for the subtitles, could be usefull, thanks. ;)

Wouldn't it be possible to open the ogg files with a .Net media class not to play it but only to read the length?

By this one could write a function that takes a array/list of strings that contains the names of the ogg files that should be played in a row.

FG28_Kodiak
04-12-2012, 07:42 AM
Really sorry I did nothing yesterday. I am changing ISP and since I told them I have been getting cut off every night.

No problem with that, at the moment i am busy, also.


Ok to sectors how i will use them.

If a Battle Area is available you get the sector of a Actor with:
GamePlay.gpSectorName(x, y)
for exampe i get "D,1" as a string.
Then i look if there is a HQ which is ' responsible' for this sector, each HQ gets a List with Sectors (its the job of the missionbuilder which sectors are stored in the Lists). If the player is connected to this HQ he became a response like
"Mason Leader Enemy Bombers are in sector D 1", the rest depends on what is possible like Angels 12, Heading 320 etc.
if he is not connected to that HQ he get no message, or
"Mason Leader no enemy in sectors".

To which 'HQ' the player can connect depens of the range to the next HQs.


On German side may be i introduce a "Fliegerleitoffizier" which give targets to german stukas or bombers. Like "ship convoi in sector D,1 destroy.."

5./JG27.Farber
04-12-2012, 09:33 AM
Nice. :-P

FG28_Kodiak
04-12-2012, 09:34 AM
Wouldn't it be possible to open the ogg files with a .Net media class not to play it but only to read the length?

By this one could write a function that takes a array/list of strings that contains the names of the ogg files that should be played in a row.

Good idea will try it.

FG28_Kodiak
04-12-2012, 09:39 AM
So added sectors to headquarters, for testing i use a simple chat message, at the moment.


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


public class Mission : AMission
{
#region class Menu

internal class Menu
{
internal class MenuEntry
{
internal string MenuName { get; set; }
internal bool active { get; set; }
}

internal List<MenuEntry> menuEntries = new List<MenuEntry>();

public void AddMenuEntry(string description, bool active)
{
MenuEntry NewMenuEntry = new MenuEntry();

NewMenuEntry.MenuName = description;
NewMenuEntry.active = active;

menuEntries.Add(NewMenuEntry);
}

public string[] GetMenuDescriptions()
{
List<string> Descriptions = new List<string>();

menuEntries.ForEach(item =>
{
Descriptions.Add(item.MenuName);
});

return Descriptions.ToArray();
}

public bool[] GetActives()
{
List<bool> Actives = new List<bool>();

menuEntries.ForEach(item =>
{
Actives.Add(item.active);
});

return Actives.ToArray();
}

public bool IsValid()
{
if (menuEntries == null || menuEntries.Count < 1)
return false;
else
return true;

}

}

#endregion


internal class Pilot
{
public Player player { get; set; }
public LocalHeadquarters connectedHeadquarter;

public Pilot(Player player)
{
this.player = player;
}
}


internal List<Pilot> PilotsInGame = new List<Pilot>();


internal class LocalHeadquarters
{
public string Name { get; set; }
public Point2d LocationCoords { get; set; }
public string Callsign { get; set; }
private List<string> AttachedSectors = new List<string>();

public LocalHeadquarters(string name, string callsign, double x, double y, params string[] sectors)
{
this.Name = name;
this.Callsign = callsign;
this.LocationCoords = new Point2d(x, y);

if (sectors != null)
AttachedSectors.AddRange(sectors);
}

public bool observeSector(string sectorName)
{
if (AttachedSectors.Exists(item => item.Equals(sectorName)))
return true;
else
return false;
}
}


List<LocalHeadquarters> Headquarters = new List<LocalHeadquarters>{
{new LocalHeadquarters("Luton", "CallSign24", 100.0, 100.0, "A,1","A,2","A,3","B,1","B,2","B,3")},
{new LocalHeadquarters("RadPoe", "CallSign32", 200.0, 200.0, "C,1","C,2","C,3","D,1","D,2","D,3")},
{new LocalHeadquarters("Forest", "CallSign15", 300.0, 300.0, "E,1","E,2","E,3","F,1","F,2","F,3")},
{new LocalHeadquarters("Bearskin", "CallSign5", 400.0, 400.0, "G,1","G,2","G,3")}};



public void checkSectors(Player player)
{

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);

if (PilotsInGame[i].connectedHeadquarter != null)
{
if (Headquarters.Exists(hq => hq == PilotsInGame[i].connectedHeadquarter))
{
LocalHeadquarters localHQ = PilotsInGame[i].connectedHeadquarter;
AiAirGroup[] EnemyAirgroups = GamePlay.gpAirGroups((player.Army() == 1) ? 2 : 1);

if (EnemyAirgroups != null)
{
bool foundEnemy = false;

foreach (AiAirGroup aag in EnemyAirgroups)
{
string sectorName = GamePlay.gpSectorName(aag.Pos().x, aag.Pos().y);

if (localHQ.observeSector(sectorName) && aag.Pos().z > 600.00)
{
foundEnemy = true;
string type = "";

if (aag.isAircraftType(AircraftType.Bomber) || aag.isAircraftType(AircraftType.DiveBomber))
type = "Bombers";
else if (aag.isAircraftType(AircraftType.Fighter))
type = "Fighters";


GamePlay.gpLogServer(new[] { player }, "Enemy {0} in Sector: {1}", new[] { type, sectorName });
}
}
if(!foundEnemy)
GamePlay.gpLogServer(new[] { player }, "No Enemy in Range", null);

}
}
}
}
}


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


if (!PilotsInGame.Exists(item => item.player == player))
{
PilotsInGame.Add(new Pilot(player));
}

SetMainMenu(player);
}


private void connectToHeadquarterSpeech(AiAircraft aircraft, LocalHeadquarters localHQ)
{
double initTime = 0.0;

aircraft.SayToGroup(aircraft.AirGroup(), "Hello_guys");

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "This_is");
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), localHQ.Callsign);
});


// to is missing as ogg


Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), aircraft.CallSign());
});



Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "leader__");
});


}



public void SetMainMenu(Player player)
{
if (player.Army() == 1) // red Side
GamePlay.gpSetOrderMissionMenu(player, false, 0, new string[] { "Radaroperations" }, new bool[] { true });
else
GamePlay.gpSetOrderMissionMenu(player, false, 0, new string[] { "Not Available" }, new bool[] { true });
}


public void SetRadarMenu(Player player)
{
string headQuarter = "Select Headquarter";
string connectedTo = "";

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);

if (PilotsInGame[i].connectedHeadquarter != null)
connectedTo = string.Format(" (connected: {0})", PilotsInGame[i].connectedHeadquarter.Name);
}
headQuarter += connectedTo;

GamePlay.gpSetOrderMissionMenu(player, true, 100, new string[] { headQuarter, "Get Radar Information" }, new bool[] { true, true });

}


public void SetConnectToHeadquarterMenu(Player player)
{
Menu NewMenu = new Menu();
LocalHeadquarters headQuarter = null;

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);
headQuarter = PilotsInGame[i].connectedHeadquarter;
}

Headquarters.ForEach(item =>
{
if (headQuarter != null && item == headQuarter)
{
NewMenu.AddMenuEntry(item.Name + " *", true);
}
else
NewMenu.AddMenuEntry(item.Name, true);
});

if (NewMenu.IsValid())
GamePlay.gpSetOrderMissionMenu(player, true, 101, NewMenu.GetMenuDescriptions() , NewMenu.GetActives());
else
GamePlay.gpSetOrderMissionMenu(player, true, 101, new string[]{ "Not available" }, new bool[]{true});
}


public void SetHeadQuarter(Player player, int menuItemIndex)
{
if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);
PilotsInGame[i].connectedHeadquarter = Headquarters[menuItemIndex-1];
if (player.Place() != null)
connectToHeadquarterSpeech((player.Place() as AiAircraft), PilotsInGame[i].connectedHeadquarter);
}

}


public override void OnOrderMissionMenuSelected(Player player, int ID, int menuItemIndex)
{


if (ID == 0)
{ // main menu
if (menuItemIndex == 1)
{
SetRadarMenu(player);
}
//if (menuItemIndex == 2)
//{


//}
//if (menuItemIndex == 3)
//{


//}
}


if (ID == 100)
{
//Radar Menu
if (menuItemIndex == 1)
{
SetConnectToHeadquarterMenu(player);
}

if (menuItemIndex == 2)
{
checkSectors(player);
SetMainMenu(player);
}
if (menuItemIndex == 0)
SetMainMenu(player);
}

if (ID == 101)
{
//Radar Menu
if (menuItemIndex == 0)
SetRadarMenu(player);
else
{
SetHeadQuarter(player, menuItemIndex);
SetRadarMenu(player);
}
}
}
}


Example mission:
Three planes in sectors (at beginning all in sectors from RadPoe) one figther, one Bomber and a Bf110 below 600m (this should not be 'found' at the beginning)

[PARTS]
core.100
bob.100
[MAIN]
MAP Land$Online_Cobra_(8x8)
BattleArea 8704 14465 64640 59199 10000
TIME 12
WeatherIndex 0
CloudsHeight 1000
BreezeActivity 10
ThermalActivity 10
player BoB_RAF_F_FatCat_Early.000
[GlobalWind_0]
Power 3.000 0.000 0.000
BottomBound 0.00
TopBound 1500.00
GustPower 5
GustAngle 45
[splines]
[AirGroups]
BoB_RAF_F_FatCat_Early.01
BoB_LW_LG2_I.02
BoB_LW_LG2_I.04
BoB_LW_LG2_I.11
[BoB_RAF_F_FatCat_Early.01]
Flight0 1
Class Aircraft.SpitfireMkIIa
Formation VIC3
CallSign 26
Fuel 100
Weapons 1
Skill 0.3 0.3 0.3 0.3 0.3 0.3 0.3 0.3
Person0_0 0 0.3 0.3 0.3 0.3 0.3 0.3 0.3 0.3 1
[BoB_RAF_F_FatCat_Early.01_Way]
TAKEOFF 40128.00 20864.00 0 0
[BoB_LW_LG2_I.02]
Flight1 11
Class Aircraft.Bf-109E-4
Formation FINGERFOUR
CallSign 26
Fuel 100
Weapons 1
Skill 0.3 0.3 0.3 0.3 0.3 0.3 0.3 0.3
[BoB_LW_LG2_I.02_Way]
NORMFLY 31360.00 43456.00 1000.00 300.00
NORMFLY 31360.00 21696.00 1000.00 300.00
NORMFLY 22464.00 21440.00 1000.00 300.00
NORMFLY 22272.00 43008.00 1000.00 300.00
[BoB_LW_LG2_I.04]
Flight2 21
Class Aircraft.He-111H-2
Formation FINGERFOUR
CallSign 26
Fuel 100
Weapons 1
Skill 0.3 0.3 0.3 0.3 0.3 0.3 0.3 0.3
[BoB_LW_LG2_I.04_Way]
NORMFLY 33536.56 29249.82 2500.00 300.00
NORMFLY 45119.69 29953.77 2500.00 300.00
NORMFLY 54143.01 37825.18 2500.00 300.00
NORMFLY 61502.45 49920.27 2500.00 300.00
[BoB_LW_LG2_I.11]
Flight0 1
Class Aircraft.Bf-110C-4
Formation FINGERFOUR
CallSign 26
Fuel 100
Weapons 1
Skill 0.3 0.3 0.3 0.3 0.3 0.3 0.3 0.3
[BoB_LW_LG2_I.11_Way]
NORMFLY 40448.00 44097.40 500.00 300.00
NORMFLY 32960.00 32129.40 500.00 300.00
NORMFLY 27008.00 26817.40 1000.00 300.00
NORMFLY 17664.00 22529.40 1000.00 300.00
[CustomChiefs]
[Stationary]
[Buildings]
[BuildingsLinks]

5./JG27.Farber
04-12-2012, 05:32 PM
I tested your mission. It works fine. However its like the aircraft has radar.

Could the command sectors be tied to an ingame object? Then it could take its readings from there instead of mesuring from the aircraft. Also this means radar can be destroyed.

Could radar tell the difference between fighters and bombers?

Osprey
04-12-2012, 06:08 PM
It was going to be one of my suggestions, however I have grave reservations about the ability to destroy radar. Although yes, a tactical war campaign that is perfectly sensible the impact would be:

1. Not historical. During the entire BoB, RDF was lost for under 2 hours in one sector only.
2. Lost RDF means that, on such a large map, that you are unlikely to meet each other in the sky, which is the whole point.

If it came to that, the first thing one would do would be to send out the entire squadron to each station and strike the object with 1 jabo. Job done. In reality an entire bombing group had little success. When we had Radar in the USL I made it so that it wasn't worth the attempt since it would detract from the historical nature of the missions that were developed.

FG28_Kodiak
04-12-2012, 06:12 PM
Hm a sector is 10*10Km or more, so i don't think its to easy, and if no symbols in options a pilot must know where he is himself :rolleyes:, and there will be a time delay between request and answer, and a delay before a pilot can request again, so a player could not ask every 5 seconds. (not implemented yet).

But i can create Radars, each radar then has it's sectors to observe and is attached to a HQ. If a radar is destroyed the HQ is blind in this sectors.
Must test if a radar is an Actor, so i can 'see' if it is destroyed (the destroy with bombs ;)).

5./JG27.Farber
04-12-2012, 06:20 PM
It was going to be one of my suggestions, however I have grave reservations about the ability to destroy radar. Although yes, a tactical war campaign that is perfectly sensible the impact would be:

1. Not historical. During the entire BoB, RDF was lost for under 2 hours in one sector only.
2. Lost RDF means that, on such a large map, that you are unlikely to meet each other in the sky, which is the whole point.

Radar stations were knocked out in BoB. It is well documented. You wont be able to miss 100+ aircraft :)
Mobile units were used when this happened and were not as effective due to their short hieght which effect wave length and the distance they could "see".

Hm a sector is 10*10Km or more, so i don't think its to easy, and if no symbols in options a pilot must know where he is himself :rolleyes:, and there will be a time delay between request and answer, and a delay before a pilot can request again, so a player could not ask every 5 seconds. (not implemented yet).

But i can create Radars, each radar then has it's sectors to observe and is attached to a HQ. If a radar is destroyed the HQ is blind in this sectors.
Must test if a radar is an Actor, so i can 'see' if it is destroyed (the destroy with bombs ;)).

The English Radar 1, it is one object made of 4 towers, it would make the ideal actor. It can assigned a side (red/blue) and be destroyed with bombs. ;)

Osprey
04-13-2012, 06:46 AM
One 250kg bomb from a single fight will knock out that radar. The towers in the BoB weren't destroyed anywhere despite several attempts by entire bomber squadrons. There were a couple of single events where radar was damaged, one because a bomb happened to land on one of the essential buildings in the complex.

I'm really not sure where this is going, what is the bigger objective of the campaign? I am seeing long term a tactical reason for this. From my perspective I honestly don't care if you bomb the crap out of every target as long as we get to meet you in the skies in the same interception fashion as the boys of the day did, but if you knock out our eyes then that won't happen - you can come in from anywhere, anytime across a big map.

5./JG27.Farber
04-13-2012, 08:45 AM
Dont worry Osprey, it will be fine. I cant give to much away ;)

Osprey
04-13-2012, 11:02 AM
Forgive my panickyness......

FG28_Kodiak
04-13-2012, 12:27 PM
Ok Problem, this Radars are not normal Actors, so i can't register their creation nor their destroying via script, must add TGroundDestroyed-Triggers to every radar. More unconfortable for mission-builder. :rolleyes:

41Sqn_Banks
04-13-2012, 12:56 PM
Ok Problem, this Radars are not normal Actors, so i can't register their creation nor their destroying via script, must add TGroundDestroyed-Triggers to every radar. More unconfortable for mission-builder. :rolleyes:

Didn't know that TGroundDestroyed is fired for "strange actors", thanks for the info, comes handy for IL2DCE.

You could parse the mission file OnBattleStart (or one of the init events before) and create a mission file that contains triggers for each radar station and then postload the trigger file (remember to set MissionNumberListener = -1;). The script has to know the path to the mission file though.

FG28_Kodiak
04-13-2012, 01:04 PM
Yes i could, but would :rolleyes: :grin:

Gib ihnen den kleinen Finger ... :rolleyes:

FG28_Kodiak
04-14-2012, 06:03 PM
Ok added the possibility to destroy Radars so the HQ gets blind in attached sectors.
For triggers i scan the mis file for the Radarstations:

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


public class Mission : AMission
{

private static string userdocpath = Environment.GetFolderPath(Environment.SpecialFolde r.MyDocuments);
private static string FILE_PATH = userdocpath + @"\1C SoftClub\il-2 sturmovik cliffs of dover\missions\";
private static string MISSION_FILE = FILE_PATH + @"RadarTest\RadarTest.mis";


private List<string> getRadarCreationStrings(string filename)
{
List<string> list = new List<string>();

using (StreamReader reader = new StreamReader(filename))
{
string line;
while ((line = reader.ReadLine()) != null)
{
if (line.Contains("Stationary.Radar"))
{
list.Add(line);
}
}
}
return list;
}



private ISectionFile createRadarTriggers()
{
ISectionFile trigger = GamePlay.gpCreateSectionFile();

List<string> Radarstations = getRadarCreationStrings(MISSION_FILE);

if (Radarstations.Count > 0)
{
int i = 0;
Radarstations.ForEach(item =>
{
item = item.TrimStart(' ');
string[] splittet = item.Split(' ');
//Radar02Destroyed TGroundDestroyed 50 23438 20849 750
string keyTr, valueTr;

keyTr = "Radar" + string.Format("{0:00}", i) +"Destroyed";
valueTr = " TGroundDestroyed 50 " + splittet[3] + " " + splittet[4] + " 500"; //TGroundDestroyedTrigger 50% destroyed in radius 500m
trigger.add("Trigger", keyTr, valueTr);

Headquarters.ForEach(hq => { hq.AddTriggerToObserver(splittet[0], keyTr); });
i++;
});
}
return trigger;
}


public override void OnBattleStarted()
{
base.OnBattleStarted();
MissionNumberListener = -1;
GamePlay.gpPostMissionLoad(createRadarTriggers());

}


#region class Menu

internal class Menu
{
internal class MenuEntry
{
internal string MenuName { get; set; }
internal bool active { get; set; }
}

internal List<MenuEntry> menuEntries = new List<MenuEntry>();

public void AddMenuEntry(string description, bool active)
{
MenuEntry NewMenuEntry = new MenuEntry();

NewMenuEntry.MenuName = description;
NewMenuEntry.active = active;

menuEntries.Add(NewMenuEntry);
}

public string[] GetMenuDescriptions()
{
List<string> Descriptions = new List<string>();

menuEntries.ForEach(item =>
{
Descriptions.Add(item.MenuName);
});

return Descriptions.ToArray();
}

public bool[] GetActives()
{
List<bool> Actives = new List<bool>();

menuEntries.ForEach(item =>
{
Actives.Add(item.active);
});

return Actives.ToArray();
}

public bool IsValid()
{
if (menuEntries == null || menuEntries.Count < 1)
return false;
else
return true;

}

}

#endregion


internal class Pilot
{
public Player player { get; set; }
public LocalHeadquarters connectedHeadquarter;

public Pilot(Player player)
{
this.player = player;
}
}


internal List<Pilot> PilotsInGame = new List<Pilot>();


internal class LocalHeadquarters
{
public string Name { get; set; }
public Point2d LocationCoords { get; set; }
public string Callsign { get; set; }
private List<ObserverStation> AttachedObservers = new List<ObserverStation>();

public LocalHeadquarters(string name, string callsign, double x, double y, params ObserverStation[] observerStations)
{
this.Name = name;
this.Callsign = callsign;
this.LocationCoords = new Point2d(x, y);

if (observerStations != null)
AttachedObservers.AddRange(observerStations);
}

public bool ObserveSector(string sectorName)
{

List<string> AttachedSectors = new List<string>();

if (AttachedObservers.Count > 0)
{
AttachedObservers.ForEach(item =>
{
if (item.IsActive)
AttachedSectors.AddRange(item.observedSectors);
});
}

if (AttachedSectors.Exists(item => item.Equals(sectorName)))
return true;
else
return false;
}

public List<ObserverStation> GetObservers()
{

return AttachedObservers;
}


public string GetNotObservedSectors()
{
string sectorList = "";

if (AttachedObservers.Exists(item => item.IsActive == false))
{
AttachedObservers[AttachedObservers.FindIndex(item => item.IsActive == false)].observedSectors.ForEach(sector =>
{
sectorList += sector + " ";
});

sectorList = sectorList.Replace(',', '/');
sectorList = sectorList.TrimEnd(' ');
}
return sectorList;
}


public void SetObserverInactive(string triggerName)
{

if (AttachedObservers.Exists(item => item.TriggerName == triggerName))
{
AttachedObservers[AttachedObservers.FindIndex(item => item.TriggerName == triggerName)].IsActive = false;
}
}


public void AddTriggerToObserver(string staticName, string triggerName)
{
if (AttachedObservers.Exists(item => item.StaticName == staticName))
{
AttachedObservers[AttachedObservers.FindIndex(item => item.StaticName == staticName)].TriggerName = triggerName;
}
}
}


internal class ObserverStation
{
public bool IsActive { get; set; }
public string StaticName { get; set; }
public string TriggerName { get; set; }

public List<string> observedSectors = new List<string>();

public ObserverStation(string staticName, params string[] sectorNames)
{
this.StaticName = staticName;
this.IsActive = true;

if (sectorNames != null)
observedSectors.AddRange(sectorNames);
}


public void SetTriggerName(string triggerName)
{
this.TriggerName = triggerName;
}

}


List<LocalHeadquarters> Headquarters = new List<LocalHeadquarters>{
{new LocalHeadquarters("Luton", "CallSign24", 100.0, 100.0, new ObserverStation( "Static0","A,1","A,2","A,3"), new ObserverStation( "Static1","B,1","B,2","B,3"))},
{new LocalHeadquarters("RadPoe", "CallSign32", 200.0, 200.0, new ObserverStation( "Static2","C,1","C,2","C,3"), new ObserverStation( "Static3","D,1","D,2","D,3"))},
{new LocalHeadquarters("Forest", "CallSign15", 300.0, 300.0, new ObserverStation( "Static4","E,1","E,2","E,3"), new ObserverStation( "Static5","F,1","F,2","F,3"))}
};


public void checkSectors(Player player)
{

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);

if (PilotsInGame[i].connectedHeadquarter != null)
{
if (Headquarters.Exists(hq => hq == PilotsInGame[i].connectedHeadquarter))
{
LocalHeadquarters localHQ = PilotsInGame[i].connectedHeadquarter;
AiAirGroup[] EnemyAirgroups = GamePlay.gpAirGroups((player.Army() == 1) ? 2 : 1);

if (EnemyAirgroups != null)
{
bool foundEnemy = false;

foreach (AiAirGroup aag in EnemyAirgroups)
{

string sectorName = GamePlay.gpSectorName(aag.Pos().x, aag.Pos().y);

if (localHQ.ObserveSector(sectorName) && aag.Pos().z > 600.00)
{
foundEnemy = true;
string type = "";

if (aag.isAircraftType(AircraftType.Bomber) || aag.isAircraftType(AircraftType.DiveBomber))
type = "Bombers";
else if (aag.isAircraftType(AircraftType.Fighter))
type = "Fighters";

GamePlay.gpLogServer(new[] { player }, "{0} Enemy {1} in Sector: {2}", new object[] { aag.NOfAirc, type, sectorName });
}
}


if (localHQ.GetNotObservedSectors() != "")
{
GamePlay.gpLogServer(null, "Attention Informations for Sectors {0} not available!", new[] { localHQ.GetNotObservedSectors() });
if (!foundEnemy)
GamePlay.gpLogServer(new[] { player }, "No Enemy in other available Sectors spottet", null);

}
else if(!foundEnemy)
GamePlay.gpLogServer(new[] { player }, "No Enemy in Range", null);

}
}
}
}
}


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


if (!PilotsInGame.Exists(item => item.player == player))
{
PilotsInGame.Add(new Pilot(player));
}


SetMainMenu(player);
}


private void connectToHeadquarterSpeech(AiAircraft aircraft, LocalHeadquarters localHQ)
{
double initTime = 0.0;


aircraft.SayToGroup(aircraft.AirGroup(), "Hello_guys");

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "This_is");
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), localHQ.Callsign);
});


Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "n2"); // to is missing as ogg
});



Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), aircraft.CallSign());
});



Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "leader__");
});


}



public void SetMainMenu(Player player)
{
if (player.Army() == 1) // red Side
GamePlay.gpSetOrderMissionMenu(player, false, 0, new string[] { "Radaroperations" }, new bool[] { true });
else
GamePlay.gpSetOrderMissionMenu(player, false, 0, new string[] { "Not Available" }, new bool[] { true });
}


public void SetRadarMenu(Player player)
{
string headQuarter = "Select Headquarter";
string connectedTo = "";

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);

if (PilotsInGame[i].connectedHeadquarter != null)
connectedTo = string.Format(" (connected: {0})", PilotsInGame[i].connectedHeadquarter.Name);
}
headQuarter += connectedTo;

GamePlay.gpSetOrderMissionMenu(player, true, 100, new string[] { headQuarter, "Get Radar Information" }, new bool[] { true, true });

}


public void SetConnectToHeadquarterMenu(Player player)
{
Menu NewMenu = new Menu();
LocalHeadquarters headQuarter = null;

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);
headQuarter = PilotsInGame[i].connectedHeadquarter;
}

Headquarters.ForEach(item =>
{
if (headQuarter != null && item == headQuarter)
{
NewMenu.AddMenuEntry(item.Name + " *", true);
}
else
NewMenu.AddMenuEntry(item.Name, true);
});

if (NewMenu.IsValid())
GamePlay.gpSetOrderMissionMenu(player, true, 101, NewMenu.GetMenuDescriptions() , NewMenu.GetActives());
else
GamePlay.gpSetOrderMissionMenu(player, true, 101, new string[]{ "Not available" }, new bool[]{true});
}


public void SetHeadQuarter(Player player, int menuItemIndex)
{
if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);
PilotsInGame[i].connectedHeadquarter = Headquarters[menuItemIndex-1];
if (player.Place() != null)
connectToHeadquarterSpeech((player.Place() as AiAircraft), PilotsInGame[i].connectedHeadquarter);
}

}


public override void OnOrderMissionMenuSelected(Player player, int ID, int menuItemIndex)
{
if (ID == 0)
{ // main menu
if (menuItemIndex == 1)
{
SetRadarMenu(player);
}
}


if (ID == 100)
{
//Radar Menu
if (menuItemIndex == 1)
{
SetConnectToHeadquarterMenu(player);
}

if (menuItemIndex == 2)
{
checkSectors(player);
SetMainMenu(player);
}
if (menuItemIndex == 0)
SetMainMenu(player);
}

if (ID == 101)
{
//Radar Menu
if (menuItemIndex == 0)
SetRadarMenu(player);
else
{
SetHeadQuarter(player, menuItemIndex);
SetRadarMenu(player);
}
}
}


public override void OnTrigger(int missionNumber, string shortName, bool active)
{
base.OnTrigger(missionNumber, shortName, active);


if (shortName.StartsWith("Radar") && shortName.EndsWith("Destroyed"))
{
GamePlay.gpLogServer(null, "Radar destroyed", null);

Headquarters.ForEach(item =>
{
item.SetObserverInactive(shortName);
});
}
}

}


Example in the attached file. Change the MISSION_FILE variable in the cs to your needs, please.

5./JG27.Farber
04-15-2012, 04:29 PM
I tested it.

Luton is the only station that picks up the bombers. I assume Luton is the station which gets bombed. It continues to function after been blown to pieces.

The other stations pick up nothing.

I altered the mission slightly. I airspawned myself in ad crashlanded near the towers.

Tab menu is disabled on killed.

FG28_Kodiak
04-15-2012, 05:14 PM
Luton has two radarstations attached one for sectors A1..A3 and one for B1..B3, the Bombers destroyed this for A1..A3, so there should no informations from these sectors be available. But this is not the case?
Do you changed the MISSION_FILE variable to your needs? If it is not found there are no triggers for the Radars are generated, which are needed to register the destroying of it.
My testversion is in ..\Documents\1C SoftClub\il-2 sturmovik cliffs of dover\missions\RadarTest


It's normal that the mission menu is vanished after death or parachute.

5./JG27.Farber
04-15-2012, 06:32 PM
I will test again.

I did not change anything, only where I spawned.

5./JG27.Farber
04-15-2012, 06:53 PM
ok I tested it. Luton channel still works when the radar is destroyed. Is this becase each channel has two radars? :confused:

I changed the flight path of the bombers to go accross all radars. This works and you have to change channel to keep up with them. :-P

FG28_Kodiak
04-15-2012, 09:00 PM
Which path do you have the mission?
Is there a message "Radar destroyed" in the chat bar, if the radar is hit? If not the missionpath is wrong, if yes i there is a bug elsewhere :rolleyes:

Memo to myself: Add a message if the mission file is not found ;)

5./JG27.Farber
04-15-2012, 09:21 PM
Ahh ok, I dont script. Sorry, I ddint realise it was s o specific.

Yes now in the right directory it works! A message comes up saying Radar Destroyed. When you ask for information after this it says, No enemy in other available sectors spottet. Maybe after its destroyed and a request is made it should read - Negative, Radar Blind?

So we just add grids in the .cs file for the grids we want it to be able to see?

Could radar actually tell the difference between fighters and bombers? Maybe it should say aircraft?

FG28_Kodiak
04-16-2012, 04:23 AM
So we just add grids in the .cs file for the grids we want it to be able to see?
If you look at the code you will find

List<LocalHeadquarters> Headquarters = new List<LocalHeadquarters>{
{new LocalHeadquarters("Luton", "CallSign24", 100.0, 100.0, new ObserverStation( "Static0","A,1","A,2","A,3"), new ObserverStation( "Static1","B,1","B,2","B,3"))},
{new LocalHeadquarters("RadPoe", "CallSign32", 200.0, 200.0, new ObserverStation( "Static2","C,1","C,2","C,3"), new ObserverStation( "Static3","D,1","D,2","D,3"))},
{new LocalHeadquarters("Forest", "CallSign15", 300.0, 300.0, new ObserverStation( "Static4","E,1","E,2","E,3"), new ObserverStation( "Static5","F,1","F,2","F,3"))}
};

there you can add/remove the observed sectors from a "Radarstation".


new LocalHeadquarters("Luton", "CallSign24", 100.0, 100.0, ..

Here you can modify the Headquarter, first the name of it, second its callsign, third and fourth the Position of it (needed later for "in range communication"). The next parameters are the attached Radarstations to this sectors.

new ObserverStation( "Static0","A,1","A,2","A,3")

first parameter is the name of the object from the mis-file, the next parameters are the sectors which are observed, you can add so many sectors you like.
You can also add so many Radarstations to a HQ you need.



The message texts are only placeholders at the moment.

5./JG27.Farber
04-16-2012, 07:08 AM
Very good explination, thank you Kodiak!

ok, so now we have a working example, how about 4 min delay and 4 min lock out? ;)

FG28_Kodiak
04-16-2012, 07:41 AM
Working on it. Time delay is no problem.
i've added number, course and height information to the messages. May be i will add a message for Intercept course to the nearest or biggest enemy group, or make it able to select the target via mission menu and then get the course settings. :rolleyes:

5./JG27.Farber
04-16-2012, 07:49 AM
I dont think Radar could give much details, other than, aircraft 10+,30+,50+ location and heading. Thats it I think.

FG28_Kodiak
04-16-2012, 10:59 AM
Other question, which mile the RAF use
1.609,3426m = 1mile?

At the moment i generate messages like
6 Enemy in Sector: A,3 heading 180
7906m 350° from Smalltown
but i will
7 miles North from Smaltown (Smalltown only a example, i added Landmarks to the script).
but i wanna know the compass terms like N, NE, E, SE, S, SW, W, NW are enough or are more needed NNW, NNE, etc? And the heading was it geografical or magnetic?

bolox
04-16-2012, 12:37 PM
correct- RAF used 'English' miles upto Sept '45 when they converted to Nautical miles (knots), involving the change of every asi:!:

AFAIK headings were usually given as 'vectors' in degrees (10 deg increments)
I THINK this was magnetic.
(there was a second format using 'bearing' and iirc this used the other North, however i may be mixing the 2 up)
need to find reference to this

FG28_Kodiak
04-20-2012, 06:00 AM
Ok new version to test:
Added Landmarks to the mission, now you get messages like
27 Enemy 15 miles West from Calais at Angels 13, Heading 320
18 Enemy 13 miles South East from New Romney at Angels 8, Heading 0
in the chatbar

To do:
-Add more 'fuzzy' to the messages but at moment i don't know how exact the british radar was.
-merge the Airgroups better if they are in 'pulks'.


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


public class Mission : AMission
{

private static string userdocpath = Environment.GetFolderPath(Environment.SpecialFolde r.MyDocuments);
private static string CLODO_FILE_PATH = userdocpath + @"\1C SoftClub\il-2 sturmovik cliffs of dover\";
private static string FILE_PATH = @"missions\RadarTest\Radar.mis"; // adjust to your needs
private static string MISSION_FILE = CLODO_FILE_PATH + FILE_PATH;

List<LocalHeadquarters> Headquarters = new List<LocalHeadquarters>{
{new LocalHeadquarters("Luton (Ash)", "CallSign24", 242928.64, 251773.16,
new ObserverStation( "Static0","AN,16","AN,17","AN,18","AN,19","AO,16","AO,17","AO,18","AO,19","AP,16","AP,17","AP,18","AP,19","AQ,16","AQ,17","AQ,18","AQ,19"), //Dover
new ObserverStation( "Static2","AL,20","AL,21","AL,22","AM,20","AM,21","AM,22","AN,20","AN,21","AN,22","AO,20","AO,21","AO,22"))}, // Dunkirk
{new LocalHeadquarters("RadPoe (Woodchurch)", "CallSign32", 208162.91, 227726.61,
new ObserverStation( "Static1","AJ,15","AJ,16","AJ,17","AJ,18","AK,15","AK,16","AK,17","AK,18","AL,15","AL,16","AL,17","AL,18","AM,15","AM,16","AM,17","AM,18"), //Rye
new ObserverStation( "Royal Observer Corps I","AK,19","AK,20","AK,21","AL,19","AL,20","AL,21","AM,19","AM,20","AM,21"))}, // fake simulation of Royal Observer Corps, none destructable
//{new LocalHeadquarters("Forest (Woodchurch)", "CallSign15", 208162.91, 227726.61, new ObserverStation( "Static4","E,1","E,2","E,3"), new ObserverStation( "Static5","F,1","F,2","F,3"))}
};


LandMarkHandling LandMarks = new LandMarkHandling(
new LandMark("Dover", 245577.51, 234521.20),
new LandMark("Folkestone", 235333.17, 229568.64),
new LandMark("St.Magarets's at Cliffe", 250423.81, 238001.64),
new LandMark("Deal", 250985.14, 244801.80),
new LandMark("Dymchurch", 223734.62, 222437.79),
new LandMark("New Romney", 220005.71, 218110.40),
new LandMark("Rye", 205758.66, 213525.22),
new LandMark("Hastings", 195366.28, 203132.83),
new LandMark("Eastbourne", 174755.54, 191963.14),
new LandMark("Brighton", 144909.86, 197927.40),
new LandMark("Calais", 284654.87, 215707.54)
);


Random random = new Random();


internal class LandMark
{
public string LandMarkName { get; set; }
public Point2d LandMarkPosition { get; set; }

public LandMark(string landMarkName, double x, double y)
{
this.LandMarkName = landMarkName;
this.LandMarkPosition = new Point2d(x, y);
}
}


internal class LandMarkHandling
{
List<LandMark> LandMarkList = new List<LandMark>();

public LandMarkHandling(params LandMark[] mark)
{
if (mark != null)
LandMarkList.AddRange(mark);
}


public LandMark getNearestLandMarkTo(Point3d position)
{

if (!(LandMarkList.Count > 0))
return null;

LandMark NearestLandMark = null;
Point2d currentPosition = new Point2d(position.x, position.y);

LandMarkList.ForEach(item =>
{
if (NearestLandMark != null)
{
if (NearestLandMark.LandMarkPosition.distance(ref currentPosition) > item.LandMarkPosition.distance(ref currentPosition))
NearestLandMark = item;
}
else NearestLandMark = item;
});

return NearestLandMark;
}
}


private List<string> getRadarCreationStrings(string filename)
{
List<string> list = new List<string>();

if (!File.Exists(filename))
GamePlay.gpLogServer(new Player[] { GamePlay.gpPlayer() }, "Missionfile {0} not found! Please check settings", new object[] { FILE_PATH });

using (StreamReader reader = new StreamReader(filename))
{
string line;
while ((line = reader.ReadLine()) != null)
{
if (line.Contains("Stationary.Radar"))
{
list.Add(line);
}
}
}
return list;
}


private ISectionFile createRadarTriggers()
{
ISectionFile trigger = GamePlay.gpCreateSectionFile();

List<string> Radarstations = getRadarCreationStrings(MISSION_FILE);

if (Radarstations.Count > 0)
{
int i = 0;
Radarstations.ForEach(item =>
{
item = item.TrimStart(' ');
string[] splittet = item.Split(' ');
string keyTr, valueTr;

keyTr = "Radar" + string.Format("{0:00}", i) +"Destroyed";
valueTr = " TGroundDestroyed 50 " + splittet[3] + " " + splittet[4] + " 100"; //TGroundDestroyedTrigger 50% destroyed in radius 100m
trigger.add("Trigger", keyTr, valueTr);

Headquarters.ForEach(hq => { hq.AddTriggerToObserver(splittet[0], keyTr); });
i++;
});
}
return trigger;
}


#region CourseCalculations


private int ToAngels(double altitude)
{
double altAngels = (altitude / 0.3048) / 1000;

if (altAngels > 1)
altAngels = Math.Round(altAngels, MidpointRounding.AwayFromZero);
else
altAngels = 1;

return (int)altAngels;
}


private int ToMiles(double distance)
{
double distanceMiles = 0;
distanceMiles = Math.Round(((distance / 1609.3426)), 0, MidpointRounding.AwayFromZero); // distance in Miles

return (int)distanceMiles;
}


private string degreesToWindRose(double degrees)
{
String[] directions = { "North", "North East", "East", "South East", "South", "South West", "West", "North West", "North" };
return directions[(int)Math.Round((((double)degrees % 360) / 45))];
}

// to get the correct bearing its nessesary to make a litte enter the matrix operation.
// the Vector2d.direction() (same as atan2) has 0° at the x-axis and goes counter clockwise, but we need 0° at the y-axis
// and clockwise direction
// so to convert it we need
// |0 1| |x| |0*x + 1*y| |y|
// | | | | = | | = | | // ok not very surprising ;)
// |1 0| |y| |1*x + 0*y| |x|

private double calculateBearingDegree(Vector3d vector)
{
Vector2d matVector = new Vector2d(vector.y, vector.x);
// the value of direction is in rad so we need *180/Pi to get the value in degrees
return matVector.direction() * 180.0 / Math.PI;
}


private double calculateBearingDegree(Vector2d vector)
{
Vector2d newVector = new Vector2d(vector.y, vector.x);

return newVector.direction() * 180.0 / Math.PI;
}


private double calculateBearingFromOrigin(Point2d targetLocation, Point2d originLocation)
{

double deltaX = targetLocation.x - originLocation.x;
double deltaY = targetLocation.y - originLocation.y;

double bearing = Math.Atan2(deltaX, deltaY);
bearing = bearing * (180.0 / Math.PI);

return (bearing > 0.0 ? bearing : (360.0 + bearing));
}


private double calculateBearingFromOrigin(Point3d targetLocation, Point3d originLocation)
{

double deltaX = targetLocation.x - originLocation.x;
double deltaY = targetLocation.y - originLocation.y;


double bearing = Math.Atan2(deltaX, deltaY);
bearing = bearing * (180.0 / Math.PI);

return (bearing > 0.0 ? bearing : (360.0 + bearing));
}


private int getDegreesIn10Step(double degrees)
{
degrees = Math.Round((degrees / 10), MidpointRounding.AwayFromZero) * 10;

return (int) degrees;
}



#endregion


internal class LocalHeadquarters
{
public string Name { get; set; }
public Point2d LocationCoords { get; set; }
public string Callsign { get; set; }
private List<ObserverStation> AttachedObservers = new List<ObserverStation>();

public LocalHeadquarters(string name, string callsign, double x, double y, params ObserverStation[] observerStations)
{
this.Name = name;
this.Callsign = callsign;
this.LocationCoords = new Point2d(x, y);

if (observerStations != null)
AttachedObservers.AddRange(observerStations);
}


public bool ObserveSector(string sectorName)
{

List<string> AttachedSectors = new List<string>();

if (AttachedObservers.Count > 0)
{
AttachedObservers.ForEach(item =>
{
if (item.IsActive)
AttachedSectors.AddRange(item.observedSectors);
});
}

if (AttachedSectors.Exists(item => item.Equals(sectorName)))
return true;
else
return false;
}

public List<ObserverStation> GetObservers()
{

return AttachedObservers;
}



public List<string> GetObservedSectors()
{
List<string> sectorList = new List<string>();

AttachedObservers.ForEach(item =>
{
if (item.IsActive)
{
item.observedSectors.ForEach(sector =>
{
if (!sectorList.Exists(newsector => sector == newsector))
sectorList.Add(sector);
});
};
});
return sectorList;
}


public List<string> GetLostObservedSectors()
{

List<string> sectorList = new List<string>();

AttachedObservers.ForEach(item =>
{
if (!item.IsActive)
{
item.observedSectors.ForEach(sector =>
{
if (!sectorList.Exists(newsector => sector == newsector))
sectorList.Add(sector);
});
};
});

if (sectorList.Count > 0)
{
List<string> CurrentObservedSectors = GetObservedSectors();

if (CurrentObservedSectors.Count > 0)
{
CurrentObservedSectors.ForEach(item =>
{
if (sectorList.Exists(sector => sector == item))
{
sectorList.RemoveAll(sector => sector == item);
}
});

}

}
return sectorList;
}


public void SetObserverInactive(string triggerName)
{

if (AttachedObservers.Exists(item => item.TriggerName == triggerName))
{
AttachedObservers[AttachedObservers.FindIndex(item => item.TriggerName == triggerName)].IsActive = false;
}
}


public void AddTriggerToObserver(string staticName, string triggerName)
{
if (AttachedObservers.Exists(item => item.StaticName == staticName))
{
AttachedObservers[AttachedObservers.FindIndex(item => item.StaticName == staticName)].TriggerName = triggerName;
}
}
}


internal class ObserverStation
{
public bool IsActive { get; set; }
public string StaticName { get; set; }
public string TriggerName { get; set; }

public List<string> observedSectors = new List<string>();

public ObserverStation(string staticName, params string[] sectorNames)
{
this.StaticName = staticName;
this.IsActive = true;

if (sectorNames != null)
observedSectors.AddRange(sectorNames);
}


public ObserverStation(string staticName, string triggerName, params string[] sectorNames)
{
this.StaticName = staticName;
this.TriggerName = triggerName;
this.IsActive = true;

if (sectorNames != null)
observedSectors.AddRange(sectorNames);
}


public void SetTriggerName(string triggerName)
{
this.TriggerName = triggerName;
}

}


internal class Pilot
{
public Player player { get; set; }
public LocalHeadquarters connectedHeadquarter;
public DateTime TimeStamp { get; set; }

public Pilot(Player player)
{
this.player = player;
}
}


internal List<Pilot> PilotsInGame = new List<Pilot>();


public void checkSectors(Player player)
{

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);

if (PilotsInGame[i].connectedHeadquarter != null)
{
if (Headquarters.Exists(hq => hq == PilotsInGame[i].connectedHeadquarter))
{
LocalHeadquarters localHQ = PilotsInGame[i].connectedHeadquarter;

AiAirGroup[] EnemyAirgroups = base.GamePlay.gpAirGroups((player.Army() == 1) ? 2 : 1);

if (EnemyAirgroups != null)
{
bool foundEnemy = false;

Dictionary<string, int> Messages = new Dictionary<string, int>();

foreach (AiAirGroup aag in EnemyAirgroups)
{
string sectorName = GamePlay.gpSectorName(aag.Pos().x, aag.Pos().y);

if (localHQ.ObserveSector(sectorName) && aag.Pos().z > 600.00)
{
foundEnemy = true;
string message ="";

Point3d destinationPoint = aag.Pos();
LandMark NearestLandMark = LandMarks.getNearestLandMarkTo(aag.Pos());
Point2d AirgroupPos = new Point2d(aag.Pos().x, aag.Pos().y);
Vector2d AirgroupVector = new Vector2d(aag.Vwld().x, aag.Vwld().y);

if (NearestLandMark != null)
message = string.Format("Enemy {0} miles {1} from {2} at Angels {3}, Heading {4}", ToMiles(NearestLandMark.LandMarkPosition.distance( ref AirgroupPos)), degreesToWindRose(calculateBearingFromOrigin(Airgr oupPos, NearestLandMark.LandMarkPosition)), NearestLandMark.LandMarkName, ToAngels(aag.Pos().z), getDegreesIn10Step(calculateBearingDegree(Airgroup Vector)));
else
message = string.Format("Enemy in Sector: {1} heading {2}", sectorName, getDegreesIn10Step(calculateBearingDegree(Airgroup Vector)));

int nrOfAircraft;

if (Messages.TryGetValue(message, out nrOfAircraft))
{
Messages[message] = nrOfAircraft + aag.NOfAirc;
}
else
{
Messages.Add(message, aag.NOfAirc);
}
}
}

Timeout(3, () =>
{

foreach (var msg in Messages)
{
GamePlay.gpLogServer(new[] { player }, "{0} {1}", new object[] { msg.Value, msg.Key });
}

if (localHQ.GetLostObservedSectors().Count > 0)
{
string lostSectors = "";

localHQ.GetLostObservedSectors().ForEach(item =>
{
lostSectors += item + " ";
});

lostSectors = lostSectors.TrimEnd(' ');

GamePlay.gpLogServer(null, "Radar blind for Sectors {0}!", new[] { lostSectors });
if (!foundEnemy)
GamePlay.gpLogServer(new[] { player }, "No Enemy in other available Sectors spottet", null);
}
else if (!foundEnemy)
GamePlay.gpLogServer(new[] { player }, "No Enemy in Range", null);
});
}
}
}
}
}


private void connectToHeadquarterSpeech(AiAircraft aircraft, LocalHeadquarters localHQ)
{
double initTime = 0.0;

aircraft.SayToGroup(aircraft.AirGroup(), "Hello_guys");

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "This_is");
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), localHQ.Callsign);
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "n2"); // to is missing as ogg
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), aircraft.CallSign());
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "leader__");
});
}


#region mission menus

#region class Menu

internal class Menu
{
internal class MenuEntry
{
internal string MenuName { get; set; }
internal bool active { get; set; }
}

internal List<MenuEntry> menuEntries = new List<MenuEntry>();

public void AddMenuEntry(string description, bool active)
{
MenuEntry NewMenuEntry = new MenuEntry();

NewMenuEntry.MenuName = description;
NewMenuEntry.active = active;

menuEntries.Add(NewMenuEntry);
}

public string[] GetMenuDescriptions()
{
List<string> Descriptions = new List<string>();

menuEntries.ForEach(item =>
{
Descriptions.Add(item.MenuName);
});

return Descriptions.ToArray();
}

public bool[] GetActives()
{
List<bool> Actives = new List<bool>();

menuEntries.ForEach(item =>
{
Actives.Add(item.active);
});

return Actives.ToArray();
}

public bool IsValid()
{
if (menuEntries == null || menuEntries.Count < 1)
return false;
else
return true;

}

}

#endregion


public void SetMainMenu(Player player)
{
if (player.Army() == 1) // red Side
GamePlay.gpSetOrderMissionMenu(player, false, 0, new string[] { "Radaroperations" }, new bool[] { true });
else
GamePlay.gpSetOrderMissionMenu(player, false, 0, new string[] { "Not Available" }, new bool[] { true });
}


public void SetRadarMenu(Player player)
{
string headQuarter = "Select Headquarter";
string connectedTo = "";

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);

if (PilotsInGame[i].connectedHeadquarter != null)
connectedTo = string.Format(" (connected: {0})", PilotsInGame[i].connectedHeadquarter.Name);
}
headQuarter += connectedTo;

GamePlay.gpSetOrderMissionMenu(player, true, 100, new string[] { headQuarter, "Get Radar Information" }, new bool[] { true, true });

}


public void SetConnectToHeadquarterMenu(Player player)
{
Menu NewMenu = new Menu();
LocalHeadquarters headQuarter = null;

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);
headQuarter = PilotsInGame[i].connectedHeadquarter;
}

Headquarters.ForEach(item =>
{
if (headQuarter != null && item == headQuarter)
{
NewMenu.AddMenuEntry(item.Name + " *", true);
}
else
NewMenu.AddMenuEntry(item.Name, true);
});

if (NewMenu.IsValid())
GamePlay.gpSetOrderMissionMenu(player, true, 101, NewMenu.GetMenuDescriptions() , NewMenu.GetActives());
else
GamePlay.gpSetOrderMissionMenu(player, true, 101, new string[]{ "Not available" }, new bool[]{true});
}


public void SetHeadQuarter(Player player, int menuItemIndex)
{
if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);
PilotsInGame[i].connectedHeadquarter = Headquarters[menuItemIndex-1];
if (player.Place() != null)
connectToHeadquarterSpeech((player.Place() as AiAircraft), PilotsInGame[i].connectedHeadquarter);
}

}


public override void OnOrderMissionMenuSelected(Player player, int ID, int menuItemIndex)
{
if (ID == 0)
{ // main menu
if (menuItemIndex == 1)
{
SetRadarMenu(player);
}
}

if (ID == 100)
{
//Radar Menu
if (menuItemIndex == 1)
{
SetConnectToHeadquarterMenu(player);
}

if (menuItemIndex == 2)
{
checkSectors(player);
SetMainMenu(player);
}
if (menuItemIndex == 0)
SetMainMenu(player);
}

if (ID == 101)
{
//Radar Menu
if (menuItemIndex == 0)
SetRadarMenu(player);
else
{
SetHeadQuarter(player, menuItemIndex);
SetRadarMenu(player);
}
}
}

#endregion


public override void OnBattleStarted()
{
base.OnBattleStarted();
MissionNumberListener = -1;
GamePlay.gpPostMissionLoad(createRadarTriggers());
}


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

if (!PilotsInGame.Exists(item => item.player == player))
{
PilotsInGame.Add(new Pilot(player));
}

SetMainMenu(player);
}


public override void OnTrigger(int missionNumber, string shortName, bool active)
{
base.OnTrigger(missionNumber, shortName, active);

if (shortName.StartsWith("Radar") && shortName.EndsWith("Destroyed"))
{
GamePlay.gpLogServer(null, "Radar destroyed", null);

Headquarters.ForEach(item =>
{
item.SetObserverInactive(shortName);
});
}
}

}

csThor
04-20-2012, 07:43 AM
I've read something of a 300% inaccuracy but that sounds grossly overstated to me. I do have a suggestion, however, and that pertains to the reported number of planes. IIRC the numbers were never that accurate, mostly it was 10+ or 20+ ... so I wonder if it would be better to use this term (which also adds a bit of insecurity as 10+ can mean 10 or 19) ...

Osprey
04-20-2012, 07:43 AM
I dont think Radar could give much details, other than, aircraft 10+,30+,50+ location and heading. Thats it I think.


It could give heading when 2 or more plots were registered. It could easily tell who was friend or foe from the signal and FC (as part of the 4 minute delay) would be able to give an interception vector. You are correct on the aircraft numbers, and this could be implemented with a switch statement in code.

FG28_Kodiak
04-20-2012, 08:18 AM
You are correct on the aircraft numbers, and this could be implemented with a switch statement in code.

No switch statement needed a simple Math.Round will do the job too and better :rolleyes:
So how the numbers of aircraft was given

1, 2, 5+ 10+ 15+ 20+ etc?

5./JG27.Farber
04-20-2012, 11:59 AM
Bravo Kodiak! This is a real work of art!

Single or few aircraft were hard to detect I belive. Think it would be more:

5+, 10+ 20+ 30+


How did you find the location for each town? Place a static in each town and look in the .mis file?

FG28_Kodiak
04-20-2012, 03:50 PM
How did you find the location for each town? Place a static in each town and look in the .mis file?

Yes a neutral missionmarker :rolleyes:

ATAG_Colander
04-20-2012, 08:21 PM
I've read something of a 300% inaccuracy ...

From what I've read about the Chain Home radar, the altitude was measured comparing the strength of two receivers at different heights in the tower. Because of this, they where measuring angles.

Since the angles where not exact, the altitude error increased with the distance. I think this could be emulated with simple trigonometry using random errors for the angle.

http://theairtacticalassaultgroup.com/banner.png

5./JG27.Farber
04-26-2012, 02:03 PM
Is this the final evolution of the script Kodiak? We are eger to implement it and iron out the wrinkles on our own maps. What about the 4 min delay?

Thanks for this,
Farber.

FG28_Kodiak
04-26-2012, 02:18 PM
No its not ;)
Current Version, added routine to merge Airgroups better, to avoid 'splittering' added a short time delay (for testing)
To Do: complete the Radar Mission Menu, hope i finish it this weekend, so the script is usable on servers.
Future: Create a DLL and Configuration via XML-file, so the handling will be easier for missionbuilders.

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


public class Mission : AMission
{

private static string userdocpath = Environment.GetFolderPath(Environment.SpecialFolde r.MyDocuments);
private static string CLODO_FILE_PATH = userdocpath + @"\1C SoftClub\il-2 sturmovik cliffs of dover\";
private static string FILE_PATH = @"missions\RadarTest\Radar.mis"; // adjust to your needs
private static string MISSION_FILE = CLODO_FILE_PATH + FILE_PATH;

List<LocalHeadquarters> Headquarters = new List<LocalHeadquarters>{
{new LocalHeadquarters("Luton (Ash)", "CallSign24", 242928.64, 251773.16,
new ObserverStation( "Static0","AN,16","AN,17","AN,18","AN,19","AO,16","AO,17","AO,18","AO,19","AP,16","AP,17","AP,18","AP,19","AQ,16","AQ,17","AQ,18","AQ,19"), //Dover
new ObserverStation( "Static2","AL,20","AL,21","AL,22","AM,20","AM,21","AM,22","AN,20","AN,21","AN,22","AO,20","AO,21","AO,22"))}, // Dunkirk
{new LocalHeadquarters("RadPoe (Woodchurch)", "CallSign32", 208162.91, 227726.61,
new ObserverStation( "Static1","AJ,15","AJ,16","AJ,17","AJ,18","AK,15","AK,16","AK,17","AK,18","AL,15","AL,16","AL,17","AL,18","AM,15","AM,16","AM,17","AM,18"), //Rye
new ObserverStation( "Royal Observer Corps I","AK,19","AK,20","AK,21","AL,19","AL,20","AL,21","AM,19","AM,20","AM,21"))}, // fake simulation of Royal Observer Corps, none destructable
//{new LocalHeadquarters("Forest (Woodchurch)", "CallSign15", 208162.91, 227726.61, new ObserverStation( "Static4","E,1","E,2","E,3"), new ObserverStation( "Static5","F,1","F,2","F,3"))}
};


LandMarkHandling LandMarks = new LandMarkHandling(
new LandMark("Dover", 245577.51, 234521.20),
new LandMark("Folkestone", 235333.17, 229568.64),
new LandMark("St.Magarets's at Cliffe", 250423.81, 238001.64),
new LandMark("Deal", 250985.14, 244801.80),
new LandMark("Dymchurch", 223734.62, 222437.79),
new LandMark("New Romney", 220005.71, 218110.40),
new LandMark("Rye", 205758.66, 213525.22),
new LandMark("Hastings", 195366.28, 203132.83),
new LandMark("Eastbourne", 174755.54, 191963.14),
new LandMark("Brighton", 144909.86, 197927.40),
new LandMark("Calais", 284654.87, 215707.54)
);


Random random = new Random();


internal class LandMark
{
public string LandMarkName { get; set; }
public Point2d LandMarkPosition { get; set; }

public LandMark(string landMarkName, double x, double y)
{
this.LandMarkName = landMarkName;
this.LandMarkPosition = new Point2d(x, y);
}
}


internal class LandMarkHandling
{
List<LandMark> LandMarkList = new List<LandMark>();

public LandMarkHandling(params LandMark[] mark)
{
if (mark != null)
LandMarkList.AddRange(mark);
}


public LandMark getNearestLandMarkTo(Point3d position)
{

if (!(LandMarkList.Count > 0))
return null;

LandMark NearestLandMark = null;
Point2d currentPosition = new Point2d(position.x, position.y);

LandMarkList.ForEach(item =>
{
if (NearestLandMark != null)
{
if (NearestLandMark.LandMarkPosition.distance(ref currentPosition) > item.LandMarkPosition.distance(ref currentPosition))
NearestLandMark = item;
}
else NearestLandMark = item;
});

return NearestLandMark;
}
}


private List<string> getRadarCreationStrings(string filename)
{
List<string> list = new List<string>();

if (!File.Exists(filename))
GamePlay.gpLogServer(new Player[] { GamePlay.gpPlayer() }, "Missionfile {0} not found! Please check settings", new object[] { FILE_PATH });

using (StreamReader reader = new StreamReader(filename))
{
string line;
while ((line = reader.ReadLine()) != null)
{
if (line.Contains("Stationary.Radar"))
{
list.Add(line);
}
}
}
return list;
}


private ISectionFile createRadarTriggers()
{
ISectionFile trigger = GamePlay.gpCreateSectionFile();

List<string> Radarstations = getRadarCreationStrings(MISSION_FILE);

if (Radarstations.Count > 0)
{
int i = 0;
Radarstations.ForEach(item =>
{
item = item.TrimStart(' ');
string[] splittet = item.Split(' ');
string keyTr, valueTr;

keyTr = "Radar" + string.Format("{0:00}", i) +"Destroyed";
valueTr = " TGroundDestroyed 50 " + splittet[3] + " " + splittet[4] + " 100"; //TGroundDestroyedTrigger 50% destroyed in radius 100m
trigger.add("Trigger", keyTr, valueTr);

Headquarters.ForEach(hq => { hq.AddTriggerToObserver(splittet[0], keyTr); });
i++;
});
}
return trigger;
}


#region Calculations

private int ToAngels(double altitude)
{
double altAngels = (altitude / 0.3048) / 1000;

if (altAngels > 1)
altAngels = Math.Round(altAngels, MidpointRounding.AwayFromZero);
else
altAngels = 1;

return (int)altAngels;
}


private int ToMiles(double distance)
{
double distanceMiles = 0;
distanceMiles = Math.Round(((distance / 1609.3426)), 0, MidpointRounding.AwayFromZero); // distance in Miles

return (int)distanceMiles;
}


private string degreesToWindRose(double degrees)
{
String[] directions = { "North", "North East", "East", "South East", "South", "South West", "West", "North West", "North" };
return directions[(int)Math.Round((((double)degrees % 360) / 45))];
}

// to get the correct bearing its nessesary to make a litte enter the matrix operation.
// the Vector2d.direction() (same as atan2) has 0° at the x-axis and goes counter clockwise, but we need 0° at the y-axis
// and clockwise direction
// so to convert it we need
// |0 1| |x| |0*x + 1*y| |y|
// | | | | = | | = | | // ok not very surprising ;)
// |1 0| |y| |1*x + 0*y| |x|

private double calculateBearingDegree(Vector3d vector)
{
Vector2d matVector = new Vector2d(vector.y, vector.x);
// the value of direction is in rad so we need *180/Pi to get the value in degrees
return matVector.direction() * 180.0 / Math.PI;
}


private double calculateBearingDegree(Vector2d vector)
{
Vector2d newVector = new Vector2d(vector.y, vector.x);

return newVector.direction() * 180.0 / Math.PI;
}


private double calculateBearingFromOrigin(Point2d targetLocation, Point2d originLocation)
{

double deltaX = targetLocation.x - originLocation.x;
double deltaY = targetLocation.y - originLocation.y;

double bearing = Math.Atan2(deltaX, deltaY);
bearing = bearing * (180.0 / Math.PI);

return (bearing > 0.0 ? bearing : (360.0 + bearing));
}


private double calculateBearingFromOrigin(Point3d targetLocation, Point3d originLocation)
{

double deltaX = targetLocation.x - originLocation.x;
double deltaY = targetLocation.y - originLocation.y;


double bearing = Math.Atan2(deltaX, deltaY);
bearing = bearing * (180.0 / Math.PI);

return (bearing > 0.0 ? bearing : (360.0 + bearing));
}


private int getDegreesIn10Step(double degrees)
{
degrees = Math.Round((degrees / 10), MidpointRounding.AwayFromZero) * 10;

if ((int)degrees == 360)
degrees = 0.0;

return (int) degrees;
}

private int noOfAircraft(int number)
{
int firstDecimal = 0;
int higherDecimal = 0;

higherDecimal = Math.DivRem(number, 10, out firstDecimal);

if (firstDecimal > 3 && firstDecimal <= 8)
firstDecimal = 5;
else if (firstDecimal > 8)
higherDecimal += 1;

if (higherDecimal > 0)
return (int)higherDecimal * 10;
else
return (int)firstDecimal;
}

#endregion


internal class LocalHeadquarters
{
public string Name { get; set; }
public Point2d LocationCoords { get; set; }
public string Callsign { get; set; }
private List<ObserverStation> AttachedObservers = new List<ObserverStation>();

public LocalHeadquarters(string name, string callsign, double x, double y, params ObserverStation[] observerStations)
{
this.Name = name;
this.Callsign = callsign;
this.LocationCoords = new Point2d(x, y);

if (observerStations != null)
AttachedObservers.AddRange(observerStations);
}


public bool ObserveSector(string sectorName)
{

List<string> AttachedSectors = new List<string>();

if (AttachedObservers.Count > 0)
{
AttachedObservers.ForEach(item =>
{
if (item.IsActive)
AttachedSectors.AddRange(item.observedSectors);
});
}

if (AttachedSectors.Exists(item => item.Equals(sectorName)))
return true;
else
return false;
}


public List<ObserverStation> GetObservers()
{
return AttachedObservers;
}


public List<string> GetObservedSectors()
{
List<string> sectorList = new List<string>();

AttachedObservers.ForEach(item =>
{
if (item.IsActive)
{
item.observedSectors.ForEach(sector =>
{
if (!sectorList.Exists(newsector => sector == newsector))
sectorList.Add(sector);
});
};
});
return sectorList;
}


public List<string> GetLostObservedSectors()
{
List<string> sectorList = new List<string>();

AttachedObservers.ForEach(item =>
{
if (!item.IsActive)
{
item.observedSectors.ForEach(sector =>
{
if (!sectorList.Exists(newsector => sector == newsector))
sectorList.Add(sector);
});
};
});

if (sectorList.Count > 0)
{
List<string> CurrentObservedSectors = GetObservedSectors();

if (CurrentObservedSectors.Count > 0)
{
CurrentObservedSectors.ForEach(item =>
{
if (sectorList.Exists(sector => sector == item))
{
sectorList.RemoveAll(sector => sector == item);
}
});

}

}
return sectorList;
}

public void SetObserverInactive(string triggerName)
{

if (AttachedObservers.Exists(item => item.TriggerName == triggerName))
{
AttachedObservers[AttachedObservers.FindIndex(item => item.TriggerName == triggerName)].IsActive = false;
}
}


public void AddTriggerToObserver(string staticName, string triggerName)
{
if (AttachedObservers.Exists(item => item.StaticName == staticName))
{
AttachedObservers[AttachedObservers.FindIndex(item => item.StaticName == staticName)].TriggerName = triggerName;
}
}


public double GetDistance(Player player)
{
Point2d playerLocation = new Point2d(player.Place().Pos().x, player.Place().Pos().y);

double distance = LocationCoords.distance(ref playerLocation);

return distance;
}

}

internal class ObserverStation
{
public bool IsActive { get; set; }
public string StaticName { get; set; }
public string TriggerName { get; set; }

public List<string> observedSectors = new List<string>();

public ObserverStation(string staticName, params string[] sectorNames)
{
this.StaticName = staticName;
this.IsActive = true;

if (sectorNames != null)
observedSectors.AddRange(sectorNames);
}


public ObserverStation(string staticName, string triggerName, params string[] sectorNames)
{
this.StaticName = staticName;
this.TriggerName = triggerName;
this.IsActive = true;

if (sectorNames != null)
observedSectors.AddRange(sectorNames);
}


public void SetTriggerName(string triggerName)
{
this.TriggerName = triggerName;
}

}


internal class Pilot
{
public Player player { get; set; }
public LocalHeadquarters connectedHeadquarter;
public DateTime TimeStamp { get; set; }
public bool RadarUsed { get; set; }

public Pilot(Player player)
{
this.player = player;
this.RadarUsed = false;
}
}


internal List<Pilot> PilotsInGame = new List<Pilot>();


public void checkSectors(Player player)
{
if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);

if (PilotsInGame[i].RadarUsed) return;

PilotsInGame[i].RadarUsed = true;

if (PilotsInGame[i].connectedHeadquarter != null)
{
if (Headquarters.Exists(hq => hq == PilotsInGame[i].connectedHeadquarter))
{
LocalHeadquarters localHQ = PilotsInGame[i].connectedHeadquarter;
Dictionary<AiAirGroup, int> planePulks = getPulks(GamePlay.gpAirGroups((player.Army() == 1) ? 2 : 1), 1600.0); //all Airgroups in 1600m radius count as one

Dictionary<string, string> Messages = new Dictionary<string, string>();
//List<string> Messages = new List<string>();

if (planePulks != null && planePulks.Count > 0)
{
bool foundEnemy = false;

foreach (var kvp in planePulks)
{
string sectorName = GamePlay.gpSectorName(kvp.Key.Pos().x, kvp.Key.Pos().y);

if (localHQ.ObserveSector(sectorName) && kvp.Key.Pos().z > 600.00)
{
foundEnemy = true;
string tmpKey = "";
string message = "";
Point3d destinationPoint = kvp.Key.Pos();
LandMark NearestLandMark = LandMarks.getNearestLandMarkTo(kvp.Key.Pos());
Point2d AirgroupPos = new Point2d(kvp.Key.Pos().x, kvp.Key.Pos().y);
Vector2d AirgroupVector = new Vector2d(kvp.Key.Vwld().x, kvp.Key.Vwld().y);

if (kvp.Value > 3) // only if more than 3 planes in a pulk generate a message
{
if (NearestLandMark != null)
{
tmpKey = string.Format("Enemy {0} miles {1} from {2} at Angels {3}, Heading {4}", ToMiles(NearestLandMark.LandMarkPosition.distance( ref AirgroupPos)), degreesToWindRose(calculateBearingFromOrigin(Airgr oupPos, NearestLandMark.LandMarkPosition)), NearestLandMark.LandMarkName, ToAngels(kvp.Key.Pos().z), getDegreesIn10Step(calculateBearingDegree(Airgroup Vector)));
message = string.Format("{0}+ Enemy {1} miles {2} from {3} at Angels {4}, Heading {5}", noOfAircraft(kvp.Value), ToMiles(NearestLandMark.LandMarkPosition.distance( ref AirgroupPos)), degreesToWindRose(calculateBearingFromOrigin(Airgr oupPos, NearestLandMark.LandMarkPosition)), NearestLandMark.LandMarkName, ToAngels(kvp.Key.Pos().z), getDegreesIn10Step(calculateBearingDegree(Airgroup Vector)));
}
else
{
tmpKey = string.Format("Enemy in Sector: {0} heading {1}", noOfAircraft(kvp.Value), sectorName, getDegreesIn10Step(calculateBearingDegree(Airgroup Vector)));
message = string.Format("{0}+ Enemy in Sector: {1} heading {2}", noOfAircraft(kvp.Value), sectorName, getDegreesIn10Step(calculateBearingDegree(Airgroup Vector)));
}
if (!Messages.ContainsKey(tmpKey))
{
Messages.Add(tmpKey, message);
}
}

}
}

Timeout(3, () =>
{
GamePlay.gpLogServer(new[] { player }, "From {0}:", new object[]{localHQ.Name });

if (Messages.Count > 0)
foreach(var kvp in Messages)
{
GamePlay.gpLogServer(new[] { player }, kvp.Value, null);
};

if (localHQ.GetLostObservedSectors().Count > 0)
{
string lostSectors = "";

localHQ.GetLostObservedSectors().ForEach(item =>
{
lostSectors += item + " ";
});

lostSectors = lostSectors.TrimEnd(' ');
GamePlay.gpLogServer(null, "Radar blind for Sectors {0}!", new[] { lostSectors });

if (!foundEnemy)
GamePlay.gpLogServer(new[] { player }, "No Enemy in other available Sectors spottet", null);
}
else if (!foundEnemy)
GamePlay.gpLogServer(new[] { player }, "No Enemy in Range", null);
});
}
}
}
Timeout(13.0, () => // delay for next use
{
PilotsInGame[i].RadarUsed = false;
});
}
}


private Dictionary<AiAirGroup, int> getPulks(AiAirGroup[] airgroups, double maxdistance)
{
Dictionary<AiAirGroup, List<AiAirGroup>> Pulks = new Dictionary<AiAirGroup, List<AiAirGroup>>();

if (airgroups != null && airgroups.Length > 1)
{
Pulks.Add(airgroups[0], new List<AiAirGroup>()); //leaderGroup //'attached' groups (in radius maxdistance)

for (int i = 1; i < airgroups.Length; i++)
{
bool airgroupAdded = false;
foreach (var kvp in Pulks)
{
Point3d airgroupPos = kvp.Key.Pos();

if (airgroups[i].Pos().distance(ref airgroupPos) < maxdistance)
{
kvp.Value.Add(airgroups[i]);
airgroupAdded = true;
}
}
if (!airgroupAdded)
Pulks.Add(airgroups[i], new List<AiAirGroup>());
}
}

Dictionary<AiAirGroup, int> planePulks = new Dictionary<AiAirGroup, int>();

foreach (var kvp in Pulks)
{
int numberOfPlanes = 0;

numberOfPlanes += kvp.Key.NOfAirc;

if (kvp.Value.Count > 0)
kvp.Value.ForEach(item =>
{
numberOfPlanes += item.NOfAirc;
});

planePulks.Add(kvp.Key, numberOfPlanes);
}

return planePulks;
}


private void connectToHeadquarterSpeech(AiAircraft aircraft, LocalHeadquarters localHQ)
{
double initTime = 0.0;

aircraft.SayToGroup(aircraft.AirGroup(), "Hello_guys");

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "This_is");
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), localHQ.Callsign);
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "n2"); // to is missing as ogg
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), aircraft.CallSign());
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "leader__");
});
}


#region mission menus

#region class Menu

internal class Menu
{
internal class MenuEntry
{
internal string MenuName { get; set; }
internal bool active { get; set; }
}

internal List<MenuEntry> menuEntries = new List<MenuEntry>();

public void AddMenuEntry(string description, bool active)
{
MenuEntry NewMenuEntry = new MenuEntry();

NewMenuEntry.MenuName = description;
NewMenuEntry.active = active;

menuEntries.Add(NewMenuEntry);
}

public string[] GetMenuDescriptions()
{
List<string> Descriptions = new List<string>();

menuEntries.ForEach(item =>
{
Descriptions.Add(item.MenuName);
});

return Descriptions.ToArray();
}

public bool[] GetActives()
{
List<bool> Actives = new List<bool>();

menuEntries.ForEach(item =>
{
Actives.Add(item.active);
});

return Actives.ToArray();
}

public bool IsValid()
{
if (menuEntries == null || menuEntries.Count < 1)
return false;
else
return true;

}

}

#endregion


public void SetMainMenu(Player player)
{
if (player.Army() == 1) // red Side
GamePlay.gpSetOrderMissionMenu(player, false, 0, new string[] { "Radaroperations" }, new bool[] { true });
else
GamePlay.gpSetOrderMissionMenu(player, false, 0, new string[] { "Not Available" }, new bool[] { true });
}


public void SetRadarMenu(Player player)
{
string headQuarter = "Select Headquarter";
string connectedTo = "";

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);

if (PilotsInGame[i].connectedHeadquarter != null)
connectedTo = string.Format(" (connected: {0})", PilotsInGame[i].connectedHeadquarter.Name);
}
headQuarter += connectedTo;

if(PilotsInGame.Exists(item=> item.player == player))
{
int i = PilotsInGame.FindIndex(item=> item.player == player);
if(PilotsInGame[i].RadarUsed)
GamePlay.gpSetOrderMissionMenu(player, true, 100, new string[] { headQuarter, "Get Radar Information" }, new bool[] { true, false });
else
GamePlay.gpSetOrderMissionMenu(player, true, 100, new string[] { headQuarter, "Get Radar Information" }, new bool[] { true, true });
}
}


public void SetConnectToHeadquarterMenu(Player player, double maxdistanceToHq)
{
Menu NewMenu = new Menu();
LocalHeadquarters headQuarter = null;

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);
headQuarter = PilotsInGame[i].connectedHeadquarter;
}

Headquarters.ForEach(item =>
{
if (headQuarter != null && item == headQuarter && item.GetDistance(player) < maxdistanceToHq)
{
NewMenu.AddMenuEntry(item.Name + " *", true);
}
else
NewMenu.AddMenuEntry(item.Name, true);
});

if (NewMenu.IsValid())
GamePlay.gpSetOrderMissionMenu(player, true, 101, NewMenu.GetMenuDescriptions() , NewMenu.GetActives());
else
GamePlay.gpSetOrderMissionMenu(player, true, 101, new string[]{ "Not available" }, new bool[]{true});
}


public void SetHeadQuarter(Player player, int menuItemIndex)
{
if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);
PilotsInGame[i].connectedHeadquarter = Headquarters[menuItemIndex-1];
if (player.Place() != null)
connectToHeadquarterSpeech((player.Place() as AiAircraft), PilotsInGame[i].connectedHeadquarter);
}

}


public override void OnOrderMissionMenuSelected(Player player, int ID, int menuItemIndex)
{
if (ID == 0)
{ // main menu
if (menuItemIndex == 1)
{
SetRadarMenu(player);
}
}

if (ID == 100)
{
//Radar Menu
if (menuItemIndex == 1)
{
SetConnectToHeadquarterMenu(player, 50000.0);
}

if (menuItemIndex == 2)
{
checkSectors(player);
SetMainMenu(player);
}
if (menuItemIndex == 0)
SetMainMenu(player);
}

if (ID == 101)
{
//Radar Menu
if (menuItemIndex == 0)
SetRadarMenu(player);
else
{
SetHeadQuarter(player, menuItemIndex);
SetRadarMenu(player);
}
}
}

#endregion


public override void OnBattleStarted()
{
base.OnBattleStarted();
MissionNumberListener = -1;
GamePlay.gpPostMissionLoad(createRadarTriggers());
}


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

if (!PilotsInGame.Exists(item => item.player == player))
{
PilotsInGame.Add(new Pilot(player));
}

SetMainMenu(player);
}


public override void OnTrigger(int missionNumber, string shortName, bool active)
{
base.OnTrigger(missionNumber, shortName, active);

if (shortName.StartsWith("Radar") && shortName.EndsWith("Destroyed"))
{
GamePlay.gpLogServer(null, "Radar destroyed", null);

Headquarters.ForEach(item =>
{
item.SetObserverInactive(shortName);
});
}
}

}

5./JG27.Farber
04-26-2012, 02:24 PM
Speedy reply!

You sir, are a credit to the community and our forth coming campaign!

S! :cool:

von Brühl
04-26-2012, 03:31 PM
Could radar actually tell the difference between fighters and bombers? Maybe it should say aircraft?

Ok, you've asked this several times and I haven't seen an answer. Yes, they could, but not in the sense that the radar operator would say "Ooo, look, 15 He-111s just popped up over Calais!" Instead, they would see the size of the return, and plot the speed, maybe see how fast it changed direction/altitude, and determine if it was a bomber or fighter. (Although I've read somewhere it could be fooled with very tight formations) With your 4 minute returns, I think that would give the Home Chain enough time to determine roughly what they were looking at.

Osprey
04-26-2012, 06:49 PM
Credit where credit is due Kodiak, this is an epic program that shoud add a big dimension to immersion. ~S~ !!

5./JG27.Farber
04-29-2012, 01:57 PM
What pattern would a radar "emit"?

Which pattern is closets to reality?



http://i1020.photobucket.com/albums/af321/farber82/campaign%20pics%20etc/radar3.jpg

http://i1020.photobucket.com/albums/af321/farber82/campaign%20pics%20etc/radar2.jpg

http://i1020.photobucket.com/albums/af321/farber82/campaign%20pics%20etc/radar1.jpg

Osprey
04-29-2012, 04:06 PM
Here's the coverage but this is 2D in a 3D world. The stations overlapped to complete the coverage.

http://ww2db.com/images/battle_britain9.jpg


Or are you asking what Kodiak's RDF covers?

5./JG27.Farber
04-29-2012, 05:35 PM
No, Kodiaks system covers what you tell it to. I think they were like the third example but more elipse and over lapping like you said... I dont intend to use 20 radar to cover the South East like real BoB, as you reds will have to switch channels constantly and with one shot every four minutes that would not be much good. Also there was Chain Home high and low for different altitudes. So its simpler and better to use high and low combined with say 4 radar operating... Covering an unrealistic area...

Otherwise one radar might only be high or low and only cover 40km wide and 120km long... So you would check in with that radar and be locked out for 4 mins! There would in all likelyness be nothing in that slim sector and therefore the radar would be useless.

So, in clonclusion its better, faster and simpler to let one radar cover a bigger area both high and low... You might argue its unrealistic but any gap in the field is still a gap and an entire air armada can be sailed through a 40km gap! So its more realistc to fudge it. Also Kodiak has included Air Obsever Corps, so any inland grid sqaures can be set to them. Think Ill go with something like picture 2. - but wider with AOC taking up the slck near the cone point.!

FG28_Kodiak
04-29-2012, 05:45 PM
You can let them overlapp.
So if one Radarstation is destroyed your HQ lost not all sectors from this observer, only the unique.

FG28_Kodiak
04-29-2012, 07:03 PM
So the menu should now work correctly, but i was to busy to test it properly, so i hope you will testing it for me ;)
At the moment i use a time delay from 30sec. for the station answer, and a additional 10sec before the radar can be reused (for testing no problem to change the timings) . Please check the messages, i don't know how the radar communication exactly was, so maybe i must change them.
The communication is only possible if in range to the station for testing 50Km at the moment, you can only choose the HQs in range and you can lost the connection.
Only airgroups larger than 3 are detected, 5+ (4-7), 10+ (8-17planes) 20+(18-27) 30+ (28-37) etc.


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


public class Mission : AMission
{

private static string userdocpath = Environment.GetFolderPath(Environment.SpecialFolde r.MyDocuments);
private static string CLODO_FILE_PATH = userdocpath + @"\1C SoftClub\il-2 sturmovik cliffs of dover\";
private static string FILE_PATH = @"missions\RadarTest\Radar.mis"; // adjust to your needs
private static string MISSION_FILE = CLODO_FILE_PATH + FILE_PATH;

List<LocalHeadquarters> Headquarters = new List<LocalHeadquarters>{
{new LocalHeadquarters("Luton (Ash)", "CallSign24", 50000.0, 242928.64, 251773.16,
new ObserverStation( "Static0","AN,16","AN,17","AN,18","AN,19","AO,16","AO,17","AO,18","AO,19","AP,16","AP,17","AP,18","AP,19","AQ,16","AQ,17","AQ,18","AQ,19"), //Dover
new ObserverStation( "Static2","AL,20","AL,21","AL,22","AM,20","AM,21","AM,22","AN,20","AN,21","AN,22","AO,20","AO,21","AO,22"))}, // Dunkirk
{new LocalHeadquarters("RadPoe (Woodchurch)", "CallSign32", 50000.0, 208162.91, 227726.61,
new ObserverStation( "Static1","AJ,15","AJ,16","AJ,17","AJ,18","AK,15","AK,16","AK,17","AK,18","AL,15","AL,16","AL,17","AL,18","AM,15","AM,16","AM,17","AM,18"), //Rye
new ObserverStation( "Royal Observer Corps I","AK,19","AK,20","AK,21","AL,19","AL,20","AL,21","AM,19","AM,20","AM,21"))}, // fake simulation of Royal Observer Corps, none destructable
//{new LocalHeadquarters("Forest (Woodchurch)", "CallSign15", 208162.91, 227726.61, new ObserverStation( "Static4","E,1","E,2","E,3"), new ObserverStation( "Static5","F,1","F,2","F,3"))}
};


LandMarkHandling LandMarks = new LandMarkHandling(
new LandMark("Dover", 245577.51, 234521.20),
new LandMark("Folkestone", 235333.17, 229568.64),
new LandMark("St.Magarets's at Cliffe", 250423.81, 238001.64),
new LandMark("Deal", 250985.14, 244801.80),
new LandMark("Dymchurch", 223734.62, 222437.79),
new LandMark("New Romney", 220005.71, 218110.40),
new LandMark("Rye", 205758.66, 213525.22),
new LandMark("Hastings", 195366.28, 203132.83),
new LandMark("Eastbourne", 174755.54, 191963.14),
new LandMark("Brighton", 144909.86, 197927.40),
new LandMark("Calais", 284654.87, 215707.54)
);


Random random = new Random();


internal class LandMark
{
public string LandMarkName { get; set; }
public Point2d LandMarkPosition { get; set; }

public LandMark(string landMarkName, double x, double y)
{
this.LandMarkName = landMarkName;
this.LandMarkPosition = new Point2d(x, y);
}
}


internal class LandMarkHandling
{
List<LandMark> LandMarkList = new List<LandMark>();

public LandMarkHandling(params LandMark[] mark)
{
if (mark != null)
LandMarkList.AddRange(mark);
}


public LandMark getNearestLandMarkTo(Point3d position)
{

if (!(LandMarkList.Count > 0))
return null;

LandMark NearestLandMark = null;
Point2d currentPosition = new Point2d(position.x, position.y);

LandMarkList.ForEach(item =>
{
if (NearestLandMark != null)
{
if (NearestLandMark.LandMarkPosition.distance(ref currentPosition) > item.LandMarkPosition.distance(ref currentPosition))
NearestLandMark = item;
}
else NearestLandMark = item;
});

return NearestLandMark;
}
}


private List<string> getRadarCreationStrings(string filename)
{
List<string> list = new List<string>();

if (!File.Exists(filename))
GamePlay.gpLogServer(new Player[] { GamePlay.gpPlayer() }, "Missionfile {0} not found! Please check settings", new object[] { FILE_PATH });

using (StreamReader reader = new StreamReader(filename))
{
string line;
while ((line = reader.ReadLine()) != null)
{
if (line.Contains("Stationary.Radar"))
{
list.Add(line);
}
}
}
return list;
}


private ISectionFile createRadarTriggers()
{
ISectionFile trigger = GamePlay.gpCreateSectionFile();

List<string> Radarstations = getRadarCreationStrings(MISSION_FILE);

if (Radarstations.Count > 0)
{
int i = 0;
Radarstations.ForEach(item =>
{
item = item.TrimStart(' ');
string[] splittet = item.Split(' ');
string keyTr, valueTr;

keyTr = "Radar" + string.Format("{0:00}", i) +"Destroyed";
valueTr = " TGroundDestroyed 50 " + splittet[3] + " " + splittet[4] + " 100"; //TGroundDestroyedTrigger 50% destroyed in radius 100m
trigger.add("Trigger", keyTr, valueTr);

Headquarters.ForEach(hq => hq.AddTriggerToObserver(splittet[0], keyTr));
i++;
});
}
return trigger;
}


#region Calculations

private int ToAngels(double altitude)
{
double altAngels = (altitude / 0.3048) / 1000;

if (altAngels > 1)
altAngels = Math.Round(altAngels, MidpointRounding.AwayFromZero);
else
altAngels = 1;

return (int)altAngels;
}


private int ToMiles(double distance)
{
double distanceMiles = 0;
distanceMiles = Math.Round(((distance / 1609.3426)), 0, MidpointRounding.AwayFromZero); // distance in Miles

return (int)distanceMiles;
}


private string degreesToWindRose(double degrees)
{
String[] directions = { "North", "North East", "East", "South East", "South", "South West", "West", "North West", "North" };
return directions[(int)Math.Round((((double)degrees % 360) / 45))];
}

// to get the correct bearing its nessesary to make a litte enter the matrix operation.
// the Vector2d.direction() (same as atan2) has 0° at the x-axis and goes counter clockwise, but we need 0° at the y-axis
// and clockwise direction
// so to convert it we need
// |0 1| |x| |0*x + 1*y| |y|
// | | | | = | | = | | // ok not very surprising ;)
// |1 0| |y| |1*x + 0*y| |x|

private double calculateBearingDegree(Vector3d vector)
{
Vector2d matVector = new Vector2d(vector.y, vector.x);
// the value of direction is in rad so we need *180/Pi to get the value in degrees
return matVector.direction() * 180.0 / Math.PI;
}


private double calculateBearingDegree(Vector2d vector)
{
Vector2d newVector = new Vector2d(vector.y, vector.x);

return newVector.direction() * 180.0 / Math.PI;
}


private double calculateBearingFromOrigin(Point2d targetLocation, Point2d originLocation)
{

double deltaX = targetLocation.x - originLocation.x;
double deltaY = targetLocation.y - originLocation.y;

double bearing = Math.Atan2(deltaX, deltaY);
bearing = bearing * (180.0 / Math.PI);

return (bearing > 0.0 ? bearing : (360.0 + bearing));
}


private double calculateBearingFromOrigin(Point3d targetLocation, Point3d originLocation)
{

double deltaX = targetLocation.x - originLocation.x;
double deltaY = targetLocation.y - originLocation.y;


double bearing = Math.Atan2(deltaX, deltaY);
bearing = bearing * (180.0 / Math.PI);

return (bearing > 0.0 ? bearing : (360.0 + bearing));
}


private int getDegreesIn10Step(double degrees)
{
degrees = Math.Round((degrees / 10), MidpointRounding.AwayFromZero) * 10;

if ((int)degrees == 360)
degrees = 0.0;

return (int) degrees;
}

private int noOfAircraft(int number)
{
int firstDecimal = 0;
int higherDecimal = 0;

higherDecimal = Math.DivRem(number, 10, out firstDecimal);

if (firstDecimal > 3 && firstDecimal <= 8)
firstDecimal = 5;
else if (firstDecimal > 8)
higherDecimal += 1;

if (higherDecimal > 0)
return (int)higherDecimal * 10;
else
return (int)firstDecimal;
}

#endregion


internal class LocalHeadquarters
{
public string Name { get; set; }
public Point2d LocationCoords { get; set; }
public string Callsign { get; set; }

public double MaxRange { get; set; }

private List<ObserverStation> AttachedObservers = new List<ObserverStation>();

public LocalHeadquarters(string name, string callsign, double maxRange, double x, double y, params ObserverStation[] observerStations)
{
this.Name = name;
this.Callsign = callsign;
this.LocationCoords = new Point2d(x, y);
this.MaxRange = maxRange;

if (observerStations != null)
AttachedObservers.AddRange(observerStations);
}


public bool CheckInRange(Player player)
{
if (player.Place() == null) return false;

bool inRange = false;
Point2d currentPos = new Point2d(player.Place().Pos().x, player.Place().Pos().y);

if (LocationCoords.distance(ref currentPos) < MaxRange)
return true;

return false;
}


public bool ObserveSector(string sectorName)
{

List<string> AttachedSectors = new List<string>();

if (AttachedObservers.Count > 0)
{
AttachedObservers.ForEach(item =>
{
if (item.IsActive)
AttachedSectors.AddRange(item.observedSectors);
});
}

if (AttachedSectors.Exists(item => item.Equals(sectorName)))
return true;
else
return false;
}


public List<ObserverStation> GetObservers()
{
return AttachedObservers;
}


public List<string> GetObservedSectors()
{
List<string> sectorList = new List<string>();

AttachedObservers.ForEach(item =>
{
if (item.IsActive)
{
item.observedSectors.ForEach(sector =>
{
if (!sectorList.Exists(newsector => sector == newsector))
sectorList.Add(sector);
});
};
});
return sectorList;
}


public List<string> GetLostObservedSectors()
{
List<string> sectorList = new List<string>();

AttachedObservers.ForEach(item =>
{
if (!item.IsActive)
{
item.observedSectors.ForEach(sector =>
{
if (!sectorList.Exists(newsector => sector == newsector))
sectorList.Add(sector);
});
};
});

if (sectorList.Count > 0)
{
List<string> CurrentObservedSectors = GetObservedSectors();

if (CurrentObservedSectors.Count > 0)
{
CurrentObservedSectors.ForEach(item =>
{
if (sectorList.Exists(sector => sector == item))
{
sectorList.RemoveAll(sector => sector == item);
}
});

}

}
return sectorList;
}

public void SetObserverInactive(string triggerName)
{

if (AttachedObservers.Exists(item => item.TriggerName == triggerName))
{
AttachedObservers[AttachedObservers.FindIndex(item => item.TriggerName == triggerName)].IsActive = false;
}
}


public void AddTriggerToObserver(string staticName, string triggerName)
{
if (AttachedObservers.Exists(item => item.StaticName == staticName))
{
AttachedObservers[AttachedObservers.FindIndex(item => item.StaticName == staticName)].TriggerName = triggerName;
}
}


public double GetDistance(Player player)
{
Point2d playerLocation = new Point2d(player.Place().Pos().x, player.Place().Pos().y);

double distance = LocationCoords.distance(ref playerLocation);

return distance;
}

}

internal class ObserverStation
{
public bool IsActive { get; set; }
public string StaticName { get; set; }
public string TriggerName { get; set; }

public List<string> observedSectors = new List<string>();

public ObserverStation(string staticName, params string[] sectorNames)
{
this.StaticName = staticName;
this.IsActive = true;

if (sectorNames != null)
observedSectors.AddRange(sectorNames);
}


public ObserverStation(string staticName, string triggerName, params string[] sectorNames)
{
this.StaticName = staticName;
this.TriggerName = triggerName;
this.IsActive = true;

if (sectorNames != null)
observedSectors.AddRange(sectorNames);
}


public void SetTriggerName(string triggerName)
{
this.TriggerName = triggerName;
}

}


internal class Pilot
{
public Player player { get; set; }
public LocalHeadquarters ConnectedHeadquarter;
public DateTime TimeStamp { get; set; }
public bool RadarUsed { get; set; }

public Pilot(Player player)
{
this.player = player;
this.RadarUsed = false;
}
}


internal List<Pilot> PilotsInGame = new List<Pilot>();


public void checkSectors(Player player)
{

double timeDelay = 0.0;

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);

if (PilotsInGame[i].RadarUsed) return;



if (PilotsInGame[i].ConnectedHeadquarter != null && PilotsInGame[i].ConnectedHeadquarter.CheckInRange(player))
{
PilotsInGame[i].RadarUsed = true;

if (Headquarters.Exists(hq => hq == PilotsInGame[i].ConnectedHeadquarter))
{
LocalHeadquarters localHQ = PilotsInGame[i].ConnectedHeadquarter;

Timeout(timeDelay += 2.0, () =>
{
GamePlay.gpLogServer(new[]{player},"From {0} to {1}:", new object[]{localHQ.Name, player.Name()});
GamePlay.gpLogServer(new[] { player }, "Collecting available informations - stay patient", null);
});

Dictionary<AiAirGroup, int> planePulks = getPulks(GamePlay.gpAirGroups((player.Army() == 1) ? 2 : 1), 1600.0); //all Airgroups in 1600m radius count as one

Dictionary<string, string> Messages = new Dictionary<string, string>();


if (planePulks != null && planePulks.Count > 0)
{
bool foundEnemy = false;

foreach (var kvp in planePulks)
{
string sectorName = GamePlay.gpSectorName(kvp.Key.Pos().x, kvp.Key.Pos().y);

if (localHQ.ObserveSector(sectorName) && kvp.Key.Pos().z > 600.00)
{
foundEnemy = true;
string tmpKey = "";
string message = "";
Point3d destinationPoint = kvp.Key.Pos();
LandMark nearestLandMark = LandMarks.getNearestLandMarkTo(kvp.Key.Pos());
Point2d airgroupPos = new Point2d(kvp.Key.Pos().x, kvp.Key.Pos().y);
Vector2d airgroupVector = new Vector2d(kvp.Key.Vwld().x, kvp.Key.Vwld().y);

if (kvp.Value > 3) // only if more than 3 planes in a pulk generate a message
{
if (nearestLandMark != null)
{
tmpKey = string.Format("Enemy {0} miles {1} from {2} at Angels {3}, Heading {4}", ToMiles(nearestLandMark.LandMarkPosition.distance( ref airgroupPos)), degreesToWindRose(calculateBearingFromOrigin(airgr oupPos, nearestLandMark.LandMarkPosition)), nearestLandMark.LandMarkName, ToAngels(kvp.Key.Pos().z), getDegreesIn10Step(calculateBearingDegree(airgroup Vector)));
message = string.Format("{0}+ Enemy {1} miles {2} from {3} at Angels {4}, Heading {5}", noOfAircraft(kvp.Value), ToMiles(nearestLandMark.LandMarkPosition.distance( ref airgroupPos)), degreesToWindRose(calculateBearingFromOrigin(airgr oupPos, nearestLandMark.LandMarkPosition)), nearestLandMark.LandMarkName, ToAngels(kvp.Key.Pos().z), getDegreesIn10Step(calculateBearingDegree(airgroup Vector)));
}
else
{
tmpKey = string.Format("Enemy in Sector: {0} heading {1}", noOfAircraft(kvp.Value), sectorName, getDegreesIn10Step(calculateBearingDegree(airgroup Vector)));
message = string.Format("{0}+ Enemy in Sector: {1} heading {2}", noOfAircraft(kvp.Value), sectorName, getDegreesIn10Step(calculateBearingDegree(airgroup Vector)));
}
if (!Messages.ContainsKey(tmpKey))
{
Messages.Add(tmpKey, message);
}
}

}
}

Timeout(timeDelay += 28, () =>
{
GamePlay.gpLogServer(new[] { player }, "From {0}:", new object[]{localHQ.Name });

if (Messages.Count > 0)
foreach(var kvp in Messages)
{
GamePlay.gpLogServer(new[] { player }, kvp.Value, null);
};

if (localHQ.GetLostObservedSectors().Count > 0)
{
string lostSectors = "";

localHQ.GetLostObservedSectors().ForEach(item =>
{
lostSectors += item + " ";
});

lostSectors = lostSectors.TrimEnd(' ');
GamePlay.gpLogServer(null, "Radar blind for Sectors {0}!", new[] { lostSectors });

if (!foundEnemy)
GamePlay.gpLogServer(new[] { player }, "No Enemy in other available Sectors spottet", null);
}
else if (!foundEnemy)
GamePlay.gpLogServer(new[] { player }, "No Enemy in Range", null);
});
}
}
}
else
{
GamePlay.gpLogServer(new[] { player }, "Connection to HQ lost (out of Range)", null);
}




Timeout(timeDelay += 10.0, () => // delay for next use
{
PilotsInGame[i].RadarUsed = false;
});
}
}


private Dictionary<AiAirGroup, int> getPulks(AiAirGroup[] airgroups, double maxdistance)
{
Dictionary<AiAirGroup, List<AiAirGroup>> Pulks = new Dictionary<AiAirGroup, List<AiAirGroup>>();

if (airgroups != null && airgroups.Length > 1)
{
Pulks.Add(airgroups[0], new List<AiAirGroup>()); //leaderGroup //'attached' groups (in radius maxdistance)

for (int i = 1; i < airgroups.Length; i++)
{
bool airgroupAdded = false;
foreach (var kvp in Pulks)
{
Point3d airgroupPos = kvp.Key.Pos();

if (airgroups[i].Pos().distance(ref airgroupPos) < maxdistance)
{
kvp.Value.Add(airgroups[i]);
airgroupAdded = true;
}
}
if (!airgroupAdded)
Pulks.Add(airgroups[i], new List<AiAirGroup>());
}
}

Dictionary<AiAirGroup, int> planePulks = new Dictionary<AiAirGroup, int>();

foreach (var kvp in Pulks)
{
int numberOfPlanes = 0;

numberOfPlanes += kvp.Key.NOfAirc;

if (kvp.Value.Count > 0)
kvp.Value.ForEach(item =>
{
numberOfPlanes += item.NOfAirc;
});

planePulks.Add(kvp.Key, numberOfPlanes);
}

return planePulks;
}


private void connectToHeadquarterSpeech(AiAircraft aircraft, LocalHeadquarters localHQ)
{
double initTime = 0.0;

aircraft.SayToGroup(aircraft.AirGroup(), "Hello_guys");

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "This_is");
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), localHQ.Callsign);
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "n2"); // to is missing as ogg
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), aircraft.CallSign());
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "leader__");
});
}


#region mission menus

#region class Menu

internal class Menu
{
internal class MenuEntry
{
internal string MenuName { get; set; }
internal bool active { get; set; }
}

internal List<MenuEntry> menuEntries = new List<MenuEntry>();

public void AddMenuEntry(string description, bool active)
{
MenuEntry NewMenuEntry = new MenuEntry();

NewMenuEntry.MenuName = description;
NewMenuEntry.active = active;

menuEntries.Add(NewMenuEntry);
}

public string[] GetMenuDescriptions()
{
List<string> Descriptions = new List<string>();

menuEntries.ForEach(item =>
{
Descriptions.Add(item.MenuName);
});

return Descriptions.ToArray();
}

public bool[] GetActives()
{
List<bool> Actives = new List<bool>();

menuEntries.ForEach(item =>
{
Actives.Add(item.active);
});

return Actives.ToArray();
}

public bool IsValid()
{
if (menuEntries == null || menuEntries.Count < 1)
return false;
else
return true;

}

}

#endregion


public void SetMainMenu(Player player)
{
if (player.Army() == 1) // red Side
GamePlay.gpSetOrderMissionMenu(player, false, 0, new string[] { "Fighter Command HQs" }, new bool[] { true });
//else // blue Side
// GamePlay.gpSetOrderMissionMenu(player, false, 0, new string[] { "Nothing Available" }, new bool[] { true });
}


public void SetRadarMenu(Player player)
{
if (player.Army() != 1) return;

string headQuarter = "Select Headquarter";
string connectedTo = "";

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);

if (PilotsInGame[i].ConnectedHeadquarter != null)
connectedTo = string.Format(" (connected: {0})", PilotsInGame[i].ConnectedHeadquarter.Name);
}
headQuarter += connectedTo;

if(PilotsInGame.Exists(item=> item.player == player))
{
int i = PilotsInGame.FindIndex(item=> item.player == player);
if (PilotsInGame[i].ConnectedHeadquarter == null)
GamePlay.gpSetOrderMissionMenu(player, true, 100, new string[] { headQuarter, "Get Radar Information" }, new bool[] { true, false });
else if (PilotsInGame[i].RadarUsed)
GamePlay.gpSetOrderMissionMenu(player, true, 100, new string[] { headQuarter, "Get Radar Information" }, new bool[] { false, false });
else
GamePlay.gpSetOrderMissionMenu(player, true, 100, new string[] { headQuarter, "Get Radar Information" }, new bool[] { true, true });
}
}


public void SetConnectToHeadquarterMenu(Player player)
{

Menu NewMenu = new Menu();
LocalHeadquarters headQuarter = null;
int i = -1;

if (PilotsInGame.Exists(item => item.player == player))
{
i = PilotsInGame.FindIndex(item => item.player == player);
headQuarter = PilotsInGame[i].ConnectedHeadquarter;
}

if (i < 0 && PilotsInGame[i].RadarUsed == true) return;


Headquarters.ForEach(item =>
{
if (headQuarter != null && item == headQuarter && headQuarter.CheckInRange(player))
{
NewMenu.AddMenuEntry(item.Name + " *", true);
}
else if (item.CheckInRange(player))
NewMenu.AddMenuEntry(item.Name, true);
});

if (NewMenu.IsValid())
GamePlay.gpSetOrderMissionMenu(player, true, 101, NewMenu.GetMenuDescriptions() , NewMenu.GetActives());
else
GamePlay.gpSetOrderMissionMenu(player, true, 101, new string[]{ "None available" }, new bool[]{false});
}


public void SetHeadQuarter(Player player, int menuItemIndex)
{
if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);

if (Headquarters[menuItemIndex - 1].CheckInRange(player) && !PilotsInGame[i].RadarUsed)
{
PilotsInGame[i].ConnectedHeadquarter = Headquarters[menuItemIndex - 1];
if (player.Place() != null)
{
// connectToHeadquarterSpeech((player.Place() as AiAircraft), PilotsInGame[i].ConnectedHeadquarter);
Timeout(2.0, () =>
{
GamePlay.gpLogServer(new[] { player }, "From {0} to {1}:", new object[] { PilotsInGame[i].ConnectedHeadquarter.Name, player.Name() });
GamePlay.gpLogServer(new[] { player }, "You are welcome!", null);
});
}
}
PilotsInGame[i].RadarUsed = false;
}

}




public void MenuPartRadarOperations(Player player, int id, int menuItemIndex)
{
if (id == 100)
{
if (PilotsInGame.Exists(item => item.player == player))
{
if (!PilotsInGame[PilotsInGame.FindIndex(item => item.player == player)].RadarUsed)
{
//Radar Menu
if (menuItemIndex == 1)
{
SetConnectToHeadquarterMenu(player);
}

if (menuItemIndex == 2)
{

checkSectors(player);
SetMainMenu(player);
}
}
else
{
SetMainMenu(player);
}
}
}

if (id == 101)
{
//Radar Menu
if (menuItemIndex == 0)
SetRadarMenu(player);
else
{
SetHeadQuarter(player, menuItemIndex);
SetRadarMenu(player);
}
}
}








public override void OnOrderMissionMenuSelected(Player player, int ID, int menuItemIndex)
{
base.OnOrderMissionMenuSelected(player, ID, menuItemIndex);


if (ID == 0)
{ // main menu
if (menuItemIndex == 1)
{
SetRadarMenu(player);
}
}


MenuPartRadarOperations(player, ID, menuItemIndex);

}

#endregion


public override void OnBattleStarted()
{
base.OnBattleStarted();
MissionNumberListener = -1;
GamePlay.gpPostMissionLoad(createRadarTriggers());
}


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

if (!PilotsInGame.Exists(item => item.player == player))
{
PilotsInGame.Add(new Pilot(player));
}

SetMainMenu(player);
}


public override void OnTrigger(int missionNumber, string shortName, bool active)
{
base.OnTrigger(missionNumber, shortName, active);

if (shortName.StartsWith("Radar") && shortName.EndsWith("Destroyed"))
{
GamePlay.gpLogServer(null, "Radar destroyed", null);

Headquarters.ForEach(item =>
{
item.SetObserverInactive(shortName);
});
}
}

}

5./JG27.Farber
04-29-2012, 07:37 PM
Sounds good. We will test if for you. :-P

Thanks Kodiak, you really are a diamond!

csThor
04-30-2012, 06:31 AM
No, Kodiaks system covers what you tell it to. I think they were like the third example but more elipse and over lapping like you said... I dont intend to use 20 radar to cover the South East like real BoB, as you reds will have to switch channels constantly and with one shot every four minutes that would not be much good. Also there was Chain Home high and low for different altitudes. So its simpler and better to use high and low combined with say 4 radar operating... Covering an unrealistic area...

Otherwise one radar might only be high or low and only cover 40km wide and 120km long... So you would check in with that radar and be locked out for 4 mins! There would in all likelyness be nothing in that slim sector and therefore the radar would be useless.

So, in clonclusion its better, faster and simpler to let one radar cover a bigger area both high and low... You might argue its unrealistic but any gap in the field is still a gap and an entire air armada can be sailed through a 40km gap! So its more realistc to fudge it. Also Kodiak has included Air Obsever Corps, so any inland grid sqaures can be set to them. Think Ill go with something like picture 2. - but wider with AOC taking up the slck near the cone point.!

Actually - as dictated by logic - any kind of emitted wave with a finite range will form a cone with a circular edge just as in the second sketch (IIRC - the darn proxy at work blocks your picture host :( ). I don't know exactly whether CH stations covered a 180° cone but IIRC they did so you would get what was almost a half-hemisphere where it could detect aircraft (but of course with a minimum altitude due to earth curve ;) ).

As for gaps and detection or not: A player should register with a Sector and not an individual radar. Each sector (IIRC No. 11 Group had six or seven) had access to several radars and of course the Observer Corps. There's a nice overview available here: http://www.raf.mod.uk/history/fightercontrolsystem.cfm

FG28_Kodiak
04-30-2012, 07:12 AM
You connect to a sector HQ, with several radars and Observer Corps in my script ;)

csThor
04-30-2012, 07:34 AM
Oh, then I misunderstood. Gotta have another coffee. ;)

Osprey
04-30-2012, 08:49 AM
Kodiak, this really is epic. This isn't just one RDF setup for one campaign, this really is the kind of immersion required for servers generally. It will be very interesting to see how this works, particularly post patch, and I think it is something that can also be extended on those experiences. In the longer term I can see this being part of a library which includes new sound recordings from pretty English WAAFs which can be installed to clients and accepted on servers to give realistic direction to flight leaders, if Steam allows it all. Still, i'm getting ahead of myself........

@Farber, I agree, we can't have the complexity of the real system. My only concern is the 'realism' of knocking them out, but still, you can't knock out observer corps personnel can you? ;)

5./JG27.Farber
04-30-2012, 09:29 AM
Nope :)

The OC is everything from little school boys, police men and enlisted men sitting in trenches with binoculars... AAA guns, barrage balloons, etc etc... It would be very hard to knock that out.

Hmmm maybe the observer should only be able to see low aircraft, like upto 4000m's... It would also be interesting to know if there could be a chnace the AOC didnt spot anything?

Osprey
04-30-2012, 03:53 PM
Well they would see them, reckon at least bomber or fighter, and perhaps count numbers, heading and height but it would get harder with height, it would be worth investigating the kind of work they did for the sake of accuracy. They used apparatus to help them and may have been hooked into the network of RDF or passed information about incoming raids in order to help gain more information.

With regards to knocking out radar, the RAF system had mobile units with a shorter range which could be moved to plug the gap. I believe this happened on August 12th and the screen was back up inside an hour as a result. On that day they though they were more successful and decided to test with Stuka's off North Foreland. 501 got stuck into them and slaughtered them.

5./JG27.Farber
04-30-2012, 03:57 PM
It was kind of like a hand over from the radar. They reported to the sector plotters. I think it would be easier to see large formations at any height. However 4 109's at 5000m's - I think very hard.

5./JG27.Farber
05-01-2012, 09:56 AM
ok, your script is working in FMB, for some reason we could not spawn in dedi server to test it there and we ran out of time last night. I dont know if it was because of server problem or just a problem with the game. We had added a red spawn point but could not spawn. :confused:


Before this we tried to use the script on our mission with our parameters but there was some errors. Can you see any mistakes here?

private static string userdocpath = Environment.GetFolderPath(Environment.SpecialFolde r.MyDocuments);
private static string CLODO_FILE_PATH = userdocpath + @"\1C SoftClub\il-2 sturmovik cliffs of dover\";
private static string FILE_PATH = @"missions\campaign\campaign3.mis"; // adjust to your needs
private static string MISSION_FILE = CLODO_FILE_PATH + FILE_PATH;

List<LocalHeadquarters> Headquarters = new List<LocalHeadquarters>{
{new LocalHeadquarters("Luton(Eastbourne)", "CallSign24", 50000.0, 178852.52, 197246.94,

new ObserverStation( "Static1","A,4","B,4","C,4","D,4","E,4","A,3","B,3","C,3","D,3","E,3","A,2","B,2","C,2","D,2","E,2","A,1","B,1","C,1","D,1","E,1"))}, //Dover

{new LocalHeadquarters("RadPoe (Rye)", "CallSign23", 50000.0, 206102.81, 212379.97,

new ObserverStation( "Static37","F,6","G,6","H,6","I,6","F,5","G,5","H,5","I,5","J,5","F,4","G,4","H,4","I,4","J,4","K,4","F,3","G,3","H,3","I,3","J,3","K,3","L,3","F,2","G,2","H,2","I,2","J,2","K,2","L,2","F,1","G,1","H,1","I,1","J,1","K,1","L,1"))}, //Rye

{new LocalHeadquarters("Dover (Ash)", "CallSign22", 50000.0, 246758.98, 235779.38,

new ObserverStation( "Static38","I,7","J,7","K,8","L,8","M,11","N,11","O,11","J,6","K,7","L,7","M,10","N,10","O1,0","L,5","L,6","K,6","K,5","L,6","L,5","L,4","L,3","L,2","M,9","M,8","M,7","M,6","N,9","N,8","L,10","L,11","L,12","O,9","O,8"))}, //Dover

{new LocalHeadquarters("Dunkirk (Woodchurch)", "CallSign32", 50000.0, 220622.50, 252385.53,

new ObserverStation( "Static3","A,12","B,12","C,12","D,12","E,12","F,12","G,12","H,12","I,12","J,12","K,12","A,11","B,11","C,11","D,11","E,11","F,11","G,11","H,11","I,11","J,11","A,10","B,10","C,10","D,10","E,10","F,10","G,10","H,10","I,10","J,10","A,9","B,9","C,9","D,9","E,9","F,9","G,9","H,9","I,9","J,9","A,8","B,8","C,8","D,8","E,8","F,8","G,8","A,7","B,7","C,7","D,7","E,7","F,7","G,7","A,6","B,6","C,6","D,6","E,6","B,5","C,5"))}, //Rye

//{new LocalHeadquarters("Forest (Woodchurch)", "CallSign15", 208162.91, 227726.61, new ObserverStation( "Static4","E,1","E,2","E,3"), new ObserverStation( "Static5","F,1","F,2","F,3"))}
};


LandMarkHandling LandMarks = new LandMarkHandling(
new LandMark("Dover", 245577.51, 234521.20),
new LandMark("Folkestone", 235333.17, 229568.64),
new LandMark("St.Magarets's at Cliffe", 250423.81, 238001.64),
new LandMark("Deal", 250985.14, 244801.80),

The problem was some of the headquarters not showing up. Also when you selected HQ2 it would give you HQ1, Select HQ3 it would give you HQ2. Also the last HQ would not show in the menu. :confused:

FG28_Kodiak
05-01-2012, 03:37 PM
Ok will take a look at it.

- Bug (wrong HQ selection) found and eliminated ;)

5./JG27.Farber
05-01-2012, 05:15 PM
Awesome thanks. How did you find all the callsigns numbers? -Just count them in game?

FG28_Kodiak
05-01-2012, 06:34 PM
Select a callsign for a group and take a look in the mis file after saving:
Example:

[BoB_LW_StG2_II.03]
..
..
CallSign 27
..
..

this would be CallSign27 in code.

- Found an other 'Bug' - if there are no Enemy planes available in mission, i forgot to create a "no enemy in range" message - fixed. :oops:

5./JG27.Farber
05-01-2012, 07:55 PM
Did I do my triggers wrong or is it the .cs?

FG28_Kodiak
05-01-2012, 09:46 PM
You must add an 'action part' to the OnTrigger in the script, at the moment there is none. (pure radar script) ;)

AiAction action = GamePlay.gpGetAction(shortName);
if (action != null)
action.Do();


Its always nessesary if OnTrigger is overriden.

5./JG27.Farber
05-01-2012, 09:54 PM
ahh I used this:

http://forum.1cpublishing.eu/showthread.php?t=28454

by you :-P


It didnt mention .cs file. So when I put in the new .cs I deleted it?

FG28_Kodiak
05-02-2012, 02:30 AM
Only if OnTrigger is overriden if not you dont need to add the action part, in the script i use OnTrigger to detect the destruction of a radar via trigger.

public override void OnTrigger(int missionNumber, string shortName, bool active)
{
base.OnTrigger(missionNumber, shortName, active);

if (shortName.StartsWith("Radar") && shortName.EndsWith("Destroyed"))
{
GamePlay.gpLogServer(null, "Radar destroyed", null); //testing only

Headquarters.ForEach(item =>
{
item.SetObserverInactive(shortName);
});
}

}


So if you use Actions you must add

AiAction action = GamePlay.gpGetAction(shortName);
if (action != null)
action.Do();

to get them to work.

So the Ontrigger should look like:

public override void OnTrigger(int missionNumber, string shortName, bool active)
{
base.OnTrigger(missionNumber, shortName, active);

if (shortName.StartsWith("Radar") && shortName.EndsWith("Destroyed"))
{
GamePlay.gpLogServer(null, "Radar destroyed", null); //testing only

Headquarters.ForEach(item =>
{
item.SetObserverInactive(shortName);
});
}

AiAction action = GamePlay.gpGetAction(shortName);
if (action != null)
action.Do();
}

5./JG27.Farber
05-02-2012, 09:15 AM
ok so all the "ontrigger parts" go together.

5./JG27.Farber
05-03-2012, 12:34 PM
I tried putting that piece of code in and it didnt work :(

FG28_Kodiak
05-03-2012, 01:14 PM
Must make some tests, but i am too busy at the moment.

5./JG27.Farber
05-03-2012, 01:18 PM
I understand. I will wait patiently. ;)

FG28_Kodiak
05-07-2012, 01:46 PM
So new version:

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


public class Mission : AMission
{

private static string userdocpath = Environment.GetFolderPath(Environment.SpecialFolde r.MyDocuments);
private static string CLODO_FILE_PATH = userdocpath + @"\1C SoftClub\il-2 sturmovik cliffs of dover\";
private static string FILE_PATH = @"missions\RadarTest\Radar.mis"; // adjust to your needs
private static string MISSION_FILE = CLODO_FILE_PATH + FILE_PATH;

//List<LocalHeadquarters> Headquarters = new List<LocalHeadquarters>{
// {new LocalHeadquarters("Luton (Ash)", "CallSign24", 50000.0, 242928.64, 251773.16,
// new ObserverStation( "Static0","AN,16","AN,17","AN,18","AN,19","AO,16","AO,17","AO,18","AO,19","AP,16","AP,17","AP,18","AP,19","AQ,16","AQ,17","AQ,18","AQ,19"), //Dover
// new ObserverStation( "Static2","AL,20","AL,21","AL,22","AM,20","AM,21","AM,22","AN,20","AN,21","AN,22","AO,20","AO,21","AO,22"))}, // Dunkirk
// {new LocalHeadquarters("RadPoe (Woodchurch)", "CallSign32", 50000.0, 208162.91, 227726.61,
// new ObserverStation( "Static1","AJ,15","AJ,16","AJ,17","AJ,18","AK,15","AK,16","AK,17","AK,18","AL,15","AL,16","AL,17","AL,18","AM,15","AM,16","AM,17","AM,18"), //Rye
// new ObserverStation( "Royal Observer Corps I","AK,19","AK,20","AK,21","AL,19","AL,20","AL,21","AM,19","AM,20","AM,21"))}, // fake simulation of Royal Observer Corps, none destructable
// //{new LocalHeadquarters("Forest (Woodchurch)", "CallSign15", 208162.91, 227726.61, new ObserverStation( "Static4","E,1","E,2","E,3"), new ObserverStation( "Static5","F,1","F,2","F,3"))}
//};


List<LocalHeadquarters> Headquarters = new List<LocalHeadquarters>{
{new LocalHeadquarters("Luton(Eastbourne)", "CallSign24", 50000.0, 178852.52, 197246.94,
new ObserverStation( "Static1","A,4","B,4","C,4","D,4","E,4","A,3","B,3","C,3","D,3","E,3","A,2","B,2","C,2","D,2","E,2","A,1","B,1","C,1","D,1","E,1"))}, //Dover
{new LocalHeadquarters("RadPoe (Rye)", "CallSign23", 50000.0, 206102.81, 212379.97,
new ObserverStation( "Static37","F,6","G,6","H,6","I,6","F,5","G,5","H,5","I,5","J,5","F,4","G,4","H,4","I,4","J,4","K,4","F,3","G,3","H,3","I,3","J,3","K,3","L,3","F,2","G,2","H,2","I,2","J,2","K,2","L,2","F,1","G,1","H,1","I,1","J,1","K,1","L,1"))}, //Rye
{new LocalHeadquarters("Dover (Ash)", "CallSign22", 50000.0, 246758.98, 235779.38,
new ObserverStation( "Static38","I,7","J,7","K,8","L,8","M,11","N,11","O,11","J,6","K,7","L,7","M,10","N,10","O,10","L,5","L,6","K,6","K,5","L,6","L,5","L,4","L,3","L,2","M,9","M,8","M,7","M,6","N,9","N,8","L,10","L,11","L,12","O,9","O,8"))}, //Dover
{new LocalHeadquarters("Dunkirk (Woodchurch)", "CallSign32", 50000.0, 220622.50, 252385.53,
new ObserverStation( "Static3","A,12","B,12","C,12","D,12","E,12","F,12","G,12","H,12","I,12","J,12","K,12","A,11","B,11","C,11","D,11","E,11","F,11","G,11","H,11","I,11","J,11","A,10","B,10","C,10","D,10","E,10","F,10","G,10","H,10","I,10","J,10","A,9","B,9","C,9","D,9","E,9","F,9","G,9","H,9","I,9","J,9","A,8","B,8","C,8","D,8","E,8","F,8","G,8","A,7","B,7","C,7","D,7","E,7","F,7","G,7","A,6","B,6","C,6","D,6","E,6","B,5","C,5"))}, //Rye
};





LandMarkHandling LandMarks = new LandMarkHandling(
new LandMark("Dover", 245577.51, 234521.20),
new LandMark("Folkestone", 235333.17, 229568.64),
new LandMark("St.Magarets's at Cliffe", 250423.81, 238001.64),
new LandMark("Deal", 250985.14, 244801.80),
new LandMark("Dymchurch", 223734.62, 222437.79),
new LandMark("New Romney", 220005.71, 218110.40),
new LandMark("Rye", 205758.66, 213525.22),
new LandMark("Hastings", 195366.28, 203132.83),
new LandMark("Eastbourne", 174755.54, 191963.14),
new LandMark("Brighton", 144909.86, 197927.40),
new LandMark("Calais", 284654.87, 215707.54)
);


Random random = new Random();


internal class LandMark
{
public string LandMarkName { get; set; }
public Point2d LandMarkPosition { get; set; }

public LandMark(string landMarkName, double x, double y)
{
this.LandMarkName = landMarkName;
this.LandMarkPosition = new Point2d(x, y);
}
}


internal class LandMarkHandling
{
List<LandMark> LandMarkList = new List<LandMark>();

public LandMarkHandling(params LandMark[] mark)
{
if (mark != null)
LandMarkList.AddRange(mark);
}


public LandMark getNearestLandMarkTo(Point3d position)
{

if (!(LandMarkList.Count > 0))
return null;

LandMark NearestLandMark = null;
Point2d currentPosition = new Point2d(position.x, position.y);

LandMarkList.ForEach(item =>
{
if (NearestLandMark != null)
{
if (NearestLandMark.LandMarkPosition.distance(ref currentPosition) > item.LandMarkPosition.distance(ref currentPosition))
NearestLandMark = item;
}
else NearestLandMark = item;
});

return NearestLandMark;
}
}


private List<string> getRadarCreationStrings(string filename)
{
List<string> list = new List<string>();

if (!File.Exists(filename))
GamePlay.gpLogServer(new Player[] { GamePlay.gpPlayer() }, "Missionfile {0} not found! Please check settings", new object[] { FILE_PATH });

using (StreamReader reader = new StreamReader(filename))
{
string line;
while ((line = reader.ReadLine()) != null)
{
if (line.Contains("Stationary.Radar"))
{
list.Add(line);
}
}
}
return list;
}


private ISectionFile createRadarTriggers()
{
ISectionFile trigger = GamePlay.gpCreateSectionFile();

List<string> Radarstations = getRadarCreationStrings(MISSION_FILE);

if (Radarstations.Count > 0)
{
int i = 0;
Radarstations.ForEach(item =>
{
item = item.TrimStart(' ');
string[] splittet = item.Split(' ');
string keyTr, valueTr;

keyTr = "Radar" + string.Format("{0:00}", i) +"Destroyed";
valueTr = " TGroundDestroyed 50 " + splittet[3] + " " + splittet[4] + " 100"; //TGroundDestroyedTrigger 50% destroyed in radius 100m
trigger.add("Trigger", keyTr, valueTr);

Headquarters.ForEach(hq => hq.AddTriggerToObserver(splittet[0], keyTr));
i++;
});
}
return trigger;
}


#region Calculations

private int ToAngels(double altitude)
{
double altAngels = (altitude / 0.3048) / 1000;

if (altAngels > 1)
altAngels = Math.Round(altAngels, MidpointRounding.AwayFromZero);
else
altAngels = 1;

return (int)altAngels;
}


private int ToMiles(double distance)
{
double distanceMiles = 0;
distanceMiles = Math.Round(((distance / 1609.3426)), 0, MidpointRounding.AwayFromZero); // distance in Miles

return (int)distanceMiles;
}


private string degreesToWindRose(double degrees)
{
String[] directions = { "North", "North East", "East", "South East", "South", "South West", "West", "North West", "North" };
return directions[(int)Math.Round((((double)degrees % 360) / 45))];
}

// to get the correct bearing its nessesary to make a litte enter the matrix operation.
// the Vector2d.direction() (same as atan2) has 0° at the x-axis and goes counter clockwise, but we need 0° at the y-axis
// and clockwise direction
// so to convert it we need
// |0 1| |x| |0*x + 1*y| |y|
// | | | | = | | = | | // ok not very surprising ;)
// |1 0| |y| |1*x + 0*y| |x|

private double calculateBearingDegree(Vector3d vector)
{
Vector2d matVector = new Vector2d(vector.y, vector.x);
// the value of direction is in rad so we need *180/Pi to get the value in degrees
return matVector.direction() * 180.0 / Math.PI;
}


private double calculateBearingDegree(Vector2d vector)
{
Vector2d newVector = new Vector2d(vector.y, vector.x);

return newVector.direction() * 180.0 / Math.PI;
}


private double calculateBearingFromOrigin(Point2d targetLocation, Point2d originLocation)
{

double deltaX = targetLocation.x - originLocation.x;
double deltaY = targetLocation.y - originLocation.y;

double bearing = Math.Atan2(deltaX, deltaY);
bearing = bearing * (180.0 / Math.PI);

return (bearing > 0.0 ? bearing : (360.0 + bearing));
}


private double calculateBearingFromOrigin(Point3d targetLocation, Point3d originLocation)
{

double deltaX = targetLocation.x - originLocation.x;
double deltaY = targetLocation.y - originLocation.y;


double bearing = Math.Atan2(deltaX, deltaY);
bearing = bearing * (180.0 / Math.PI);

return (bearing > 0.0 ? bearing : (360.0 + bearing));
}


private int getDegreesIn10Step(double degrees)
{
degrees = Math.Round((degrees / 10), MidpointRounding.AwayFromZero) * 10;

if ((int)degrees == 360)
degrees = 0.0;

return (int) degrees;
}

private int noOfAircraft(int number)
{
int firstDecimal = 0;
int higherDecimal = 0;

higherDecimal = Math.DivRem(number, 10, out firstDecimal);

if (firstDecimal > 3 && firstDecimal <= 8)
firstDecimal = 5;
else if (firstDecimal > 8)
higherDecimal += 1;

if (higherDecimal > 0)
return (int)higherDecimal * 10;
else
return (int)firstDecimal;
}

#endregion


internal class LocalHeadquarters
{
public string Name { get; set; }
public Point2d LocationCoords { get; set; }
public string Callsign { get; set; }

public double MaxRange { get; set; }

private List<ObserverStation> AttachedObservers = new List<ObserverStation>();

public LocalHeadquarters(string name, string callsign, double maxRange, double x, double y, params ObserverStation[] observerStations)
{
this.Name = name;
this.Callsign = callsign;
this.LocationCoords = new Point2d(x, y);
this.MaxRange = maxRange;

if (observerStations != null)
AttachedObservers.AddRange(observerStations);
}


public bool CheckInRange(Player player)
{
if (player.Place() == null) return false;

bool inRange = false;
Point2d currentPos = new Point2d(player.Place().Pos().x, player.Place().Pos().y);

if (LocationCoords.distance(ref currentPos) < MaxRange)
return true;

return false;
}


public bool ObserveSector(string sectorName)
{

List<string> AttachedSectors = new List<string>();

if (AttachedObservers.Count > 0)
{
AttachedObservers.ForEach(item =>
{
if (item.IsActive)
AttachedSectors.AddRange(item.observedSectors);
});
}

if (AttachedSectors.Exists(item => item.Equals(sectorName)))
return true;
else
return false;
}


public List<ObserverStation> GetObservers()
{
return AttachedObservers;
}


public List<string> GetObservedSectors()
{
List<string> sectorList = new List<string>();

AttachedObservers.ForEach(item =>
{
if (item.IsActive)
{
item.observedSectors.ForEach(sector =>
{
if (!sectorList.Exists(newsector => sector == newsector))
sectorList.Add(sector);
});
};
});
return sectorList;
}


public List<string> GetLostObservedSectors()
{
List<string> sectorList = new List<string>();

AttachedObservers.ForEach(item =>
{
if (!item.IsActive)
{
item.observedSectors.ForEach(sector =>
{
if (!sectorList.Exists(newsector => sector == newsector))
sectorList.Add(sector);
});
};
});

if (sectorList.Count > 0)
{
List<string> CurrentObservedSectors = GetObservedSectors();

if (CurrentObservedSectors.Count > 0)
{
CurrentObservedSectors.ForEach(item =>
{
if (sectorList.Exists(sector => sector == item))
{
sectorList.RemoveAll(sector => sector == item);
}
});

}

}
return sectorList;
}

public void SetObserverInactive(string triggerName)
{

if (AttachedObservers.Exists(item => item.TriggerName == triggerName))
{
AttachedObservers[AttachedObservers.FindIndex(item => item.TriggerName == triggerName)].IsActive = false;
}
}


public void AddTriggerToObserver(string staticName, string triggerName)
{
if (AttachedObservers.Exists(item => item.StaticName == staticName))
{
AttachedObservers[AttachedObservers.FindIndex(item => item.StaticName == staticName)].TriggerName = triggerName;
}
}


public double GetDistance(Player player)
{
Point2d playerLocation = new Point2d(player.Place().Pos().x, player.Place().Pos().y);

double distance = LocationCoords.distance(ref playerLocation);

return distance;
}

}

internal class ObserverStation
{
public bool IsActive { get; set; }
public string StaticName { get; set; }
public string TriggerName { get; set; }

public List<string> observedSectors = new List<string>();

public ObserverStation(string staticName, params string[] sectorNames)
{
this.StaticName = staticName;
this.IsActive = true;

if (sectorNames != null)
observedSectors.AddRange(sectorNames);
}


public ObserverStation(string staticName, string triggerName, params string[] sectorNames)
{
this.StaticName = staticName;
this.TriggerName = triggerName;
this.IsActive = true;

if (sectorNames != null)
observedSectors.AddRange(sectorNames);
}


public void SetTriggerName(string triggerName)
{
this.TriggerName = triggerName;
}

}


internal class Pilot
{
public Player player { get; set; }
public LocalHeadquarters ConnectedHeadquarter;
public DateTime TimeStamp { get; set; }
public bool RadarUsed { get; set; }

public Pilot(Player player)
{
this.player = player;
this.RadarUsed = false;
}
}


internal List<Pilot> PilotsInGame = new List<Pilot>();


public void checkSectors(Player player)
{

double timeDelay = 0.0;

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);

if (PilotsInGame[i].RadarUsed) return;


if (PilotsInGame[i].ConnectedHeadquarter != null && PilotsInGame[i].ConnectedHeadquarter.CheckInRange(player))
{
PilotsInGame[i].RadarUsed = true;

if (Headquarters.Exists(hq => hq == PilotsInGame[i].ConnectedHeadquarter))
{
LocalHeadquarters localHQ = PilotsInGame[i].ConnectedHeadquarter;

Timeout(timeDelay += 2.0, () =>
{
GamePlay.gpLogServer(new[]{player},"From {0} to {1}:", new object[]{localHQ.Name, player.Name()});
GamePlay.gpLogServer(new[] { player }, "Collecting available informations - stay patient", null);
});

Dictionary<AiAirGroup, int> planePulks = getPulks(GamePlay.gpAirGroups((player.Army() == 1) ? 2 : 1), 1600.0); //all Airgroups in 1600m radius count as one

Dictionary<string, string> Messages = new Dictionary<string, string>();


if (planePulks != null) //&& planePulks.Count > 0)
{
bool foundEnemy = false;

foreach (var kvp in planePulks)
{
string sectorName = GamePlay.gpSectorName(kvp.Key.Pos().x, kvp.Key.Pos().y);

if (localHQ.ObserveSector(sectorName) && kvp.Key.Pos().z > 600.00)
{
string tmpKey = "";
string message = "";
Point3d destinationPoint = kvp.Key.Pos();
LandMark nearestLandMark = LandMarks.getNearestLandMarkTo(kvp.Key.Pos());
Point2d airgroupPos = new Point2d(kvp.Key.Pos().x, kvp.Key.Pos().y);
Vector2d airgroupVector = new Vector2d(kvp.Key.Vwld().x, kvp.Key.Vwld().y);

if (kvp.Value > 3) // only if more than 3 planes in a pulk generate a message
{
foundEnemy = true;
if (nearestLandMark != null)
{
tmpKey = string.Format("Enemy {0} miles {1} from {2} at Angels {3}, Heading {4}", ToMiles(nearestLandMark.LandMarkPosition.distance( ref airgroupPos)), degreesToWindRose(calculateBearingFromOrigin(airgr oupPos, nearestLandMark.LandMarkPosition)), nearestLandMark.LandMarkName, ToAngels(kvp.Key.Pos().z), getDegreesIn10Step(calculateBearingDegree(airgroup Vector)));
message = string.Format("{0}+ Enemy {1} miles {2} from {3} at Angels {4}, Heading {5}", noOfAircraft(kvp.Value), ToMiles(nearestLandMark.LandMarkPosition.distance( ref airgroupPos)), degreesToWindRose(calculateBearingFromOrigin(airgr oupPos, nearestLandMark.LandMarkPosition)), nearestLandMark.LandMarkName, ToAngels(kvp.Key.Pos().z), getDegreesIn10Step(calculateBearingDegree(airgroup Vector)));
}
else
{
tmpKey = string.Format("Enemy in Sector: {0} heading {1}", noOfAircraft(kvp.Value), sectorName, getDegreesIn10Step(calculateBearingDegree(airgroup Vector)));
message = string.Format("{0}+ Enemy in Sector: {1} heading {2}", noOfAircraft(kvp.Value), sectorName, getDegreesIn10Step(calculateBearingDegree(airgroup Vector)));
}
if (!Messages.ContainsKey(tmpKey))
{
Messages.Add(tmpKey, message);
}
}

}
}

Timeout(timeDelay += 28, () =>
{
GamePlay.gpLogServer(new[] { player }, "From {0}:", new object[]{localHQ.Name });

if (Messages.Count > 0)
foreach(var kvp in Messages)
{
GamePlay.gpLogServer(new[] { player }, kvp.Value, null);
};

if (localHQ.GetLostObservedSectors().Count > 0)
{
string lostSectors = "";

localHQ.GetLostObservedSectors().ForEach(item =>
{
lostSectors += item + " ";
});

lostSectors = lostSectors.TrimEnd(' ');
GamePlay.gpLogServer(null, "Radar blind for Sectors {0}!", new[] { lostSectors });

if (!foundEnemy)
GamePlay.gpLogServer(new[] { player }, "No Enemy in other available Sectors spottet", null);
}
else if (!foundEnemy)
GamePlay.gpLogServer(new[] { player }, "No Enemy in Range", null);
});
}
}
}
else
{
GamePlay.gpLogServer(new[] { player }, "Connection to HQ lost (out of Range)", null);
}




Timeout(timeDelay += 10.0, doTimeout: () => // delay for next use
{
PilotsInGame[i].RadarUsed = false;
});
}
}


private Dictionary<AiAirGroup, int> getPulks(AiAirGroup[] airgroups, double maxdistance)
{
Dictionary<AiAirGroup, List<AiAirGroup>> Pulks = new Dictionary<AiAirGroup, List<AiAirGroup>>();

if (airgroups != null && airgroups.Length > 1)
{
Pulks.Add(airgroups[0], new List<AiAirGroup>()); //leaderGroup //'attached' groups (in radius maxdistance)

for (int i = 1; i < airgroups.Length; i++)
{
bool airgroupAdded = false;
foreach (var kvp in Pulks)
{
Point3d airgroupPos = kvp.Key.Pos();

if (airgroups[i].Pos().distance(ref airgroupPos) < maxdistance)
{
kvp.Value.Add(airgroups[i]);
airgroupAdded = true;
}
}
if (!airgroupAdded)
Pulks.Add(airgroups[i], new List<AiAirGroup>());
}
}

Dictionary<AiAirGroup, int> planePulks = new Dictionary<AiAirGroup, int>();

foreach (var kvp in Pulks)
{
int numberOfPlanes = 0;

numberOfPlanes += kvp.Key.NOfAirc;

if (kvp.Value.Count > 0)
kvp.Value.ForEach(item =>
{
numberOfPlanes += item.NOfAirc;
});

planePulks.Add(kvp.Key, numberOfPlanes);
}

return planePulks;
}


private void connectToHeadquarterSpeech(AiAircraft aircraft, LocalHeadquarters localHQ)
{
double initTime = 0.0;

aircraft.SayToGroup(aircraft.AirGroup(), "Hello_guys");

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "This_is");
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), localHQ.Callsign);
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "n2"); // to is missing as ogg
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), aircraft.CallSign());
});

Timeout(initTime += 2, () =>
{
aircraft.SayToGroup(aircraft.AirGroup(), "leader__");
});
}


#region mission menus

#region class Menu

internal class Menu
{
internal class MenuEntry
{
internal string MenuName { get; set; }
internal bool active { get; set; }
}

internal List<MenuEntry> menuEntries = new List<MenuEntry>();

public void AddMenuEntry(string description, bool active)
{
MenuEntry NewMenuEntry = new MenuEntry();

NewMenuEntry.MenuName = description;
NewMenuEntry.active = active;

menuEntries.Add(NewMenuEntry);
}

public string[] GetMenuDescriptions()
{
List<string> Descriptions = new List<string>();

menuEntries.ForEach(item =>
{
Descriptions.Add(item.MenuName);
});

return Descriptions.ToArray();
}

public bool[] GetActives()
{
List<bool> Actives = new List<bool>();

menuEntries.ForEach(item =>
{
Actives.Add(item.active);
});

return Actives.ToArray();
}

public bool IsValid()
{
if (menuEntries == null || menuEntries.Count < 1)
return false;
else
return true;

}

}

#endregion


public void SetMainMenu(Player player)
{
if (player.Army() == 1) // red Side
GamePlay.gpSetOrderMissionMenu(player, false, 0, new string[] { "Fighter Command HQs" }, new bool[] { true });
//else // blue Side
// GamePlay.gpSetOrderMissionMenu(player, false, 0, new string[] { "Nothing Available" }, new bool[] { true });
}


public void SetRadarMenu(Player player)
{
if (player.Army() != 1) return;

string headQuarter = "Select Headquarter";
string connectedTo = "";

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);

if (PilotsInGame[i].ConnectedHeadquarter != null)
connectedTo = string.Format(" (connected: {0})", PilotsInGame[i].ConnectedHeadquarter.Name);
}
headQuarter += connectedTo;

if(PilotsInGame.Exists(item=> item.player == player))
{
int i = PilotsInGame.FindIndex(item=> item.player == player);
if (PilotsInGame[i].ConnectedHeadquarter == null)
GamePlay.gpSetOrderMissionMenu(player, true, 100, new string[] { headQuarter, "Get Radar Information" }, new bool[] { true, false });
else if (PilotsInGame[i].RadarUsed)
GamePlay.gpSetOrderMissionMenu(player, true, 100, new string[] { headQuarter, "Get Radar Information" }, new bool[] { false, false });
else
GamePlay.gpSetOrderMissionMenu(player, true, 100, new string[] { headQuarter, "Get Radar Information" }, new bool[] { true, true });
}
}


public void SetConnectToHeadquarterMenu(Player player)
{

Menu NewMenu = new Menu();
LocalHeadquarters headQuarter = null;
int i = -1;

if (PilotsInGame.Exists(item => item.player == player))
{
i = PilotsInGame.FindIndex(item => item.player == player);
headQuarter = PilotsInGame[i].ConnectedHeadquarter;
}

if (i < 0 && PilotsInGame[i].RadarUsed == true) return;


Headquarters.ForEach(item =>
{
if (headQuarter != null && item == headQuarter && headQuarter.CheckInRange(player))
{
NewMenu.AddMenuEntry(item.Name + " *", true);
}
else if (item.CheckInRange(player))
NewMenu.AddMenuEntry(item.Name, true);
});

if (NewMenu.IsValid())
GamePlay.gpSetOrderMissionMenu(player, true, 101, NewMenu.GetMenuDescriptions() , NewMenu.GetActives());
else
GamePlay.gpSetOrderMissionMenu(player, true, 101, new string[]{ "None available" }, new bool[]{false});
}


public void SetHeadQuarter(Player player, int menuItemIndex)
{


List<LocalHeadquarters> AvailableHQs = new List<LocalHeadquarters>();

Headquarters.ForEach(item =>
{
if (item.CheckInRange(player))
AvailableHQs.Add(item);
});

if (menuItemIndex == 0 || AvailableHQs.Count == 0) return;

if (PilotsInGame.Exists(item => item.player == player))
{
int i = PilotsInGame.FindIndex(item => item.player == player);

if (AvailableHQs[menuItemIndex - 1].CheckInRange(player) && !PilotsInGame[i].RadarUsed)
{
PilotsInGame[i].ConnectedHeadquarter = AvailableHQs[menuItemIndex - 1];
if (player.Place() != null)
{
// connectToHeadquarterSpeech((player.Place() as AiAircraft), PilotsInGame[i].ConnectedHeadquarter);
Timeout(2.0, () =>
{
GamePlay.gpLogServer(new[] { player }, "From {0} to {1}:", new object[] { PilotsInGame[i].ConnectedHeadquarter.Name, player.Name() });
GamePlay.gpLogServer(new[] { player }, "You are welcome!", null);
});
}
}
PilotsInGame[i].RadarUsed = false;
}

}




public void MenuPartRadarOperations(Player player, int id, int menuItemIndex)
{
if (id == 100)
{
if (PilotsInGame.Exists(item => item.player == player))
{
if (!PilotsInGame[PilotsInGame.FindIndex(item => item.player == player)].RadarUsed)
{
//Radar Menu
if (menuItemIndex == 1)
{
SetMainMenu(player);
}
if (menuItemIndex == 1)
{
SetConnectToHeadquarterMenu(player);
}

if (menuItemIndex == 2)
{

checkSectors(player);
SetMainMenu(player);
}
}
else
{
SetMainMenu(player);
}
}
}

if (id == 101)
{
//Radar Menu
if (menuItemIndex == 0)
SetRadarMenu(player);
else
{
SetHeadQuarter(player, menuItemIndex);
SetRadarMenu(player);
}
}
}








public override void OnOrderMissionMenuSelected(Player player, int ID, int menuItemIndex)
{
base.OnOrderMissionMenuSelected(player, ID, menuItemIndex);


if (ID == 0)
{ // main menu
if (menuItemIndex == 1)
{
SetRadarMenu(player);
}
}


MenuPartRadarOperations(player, ID, menuItemIndex);

}

#endregion


public override void OnBattleStarted()
{
base.OnBattleStarted();
MissionNumberListener = -1;
GamePlay.gpPostMissionLoad(createRadarTriggers());
}


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


if (!PilotsInGame.Exists(item => item.player == player))
{
PilotsInGame.Add(new Pilot(player));
}

SetMainMenu(player);
}


public override void OnTrigger(int missionNumber, string shortName, bool active)
{
base.OnTrigger(missionNumber, shortName, active);

if (shortName.StartsWith("Radar") && shortName.EndsWith("Destroyed"))
{
GamePlay.gpLogServer(null, "Radar destroyed", null); //testing only

Headquarters.ForEach(item =>
{
item.SetObserverInactive(shortName);
});
}

AiAction action = GamePlay.gpGetAction(shortName);
if (action != null)
action.Do();
}

}

41Sqn_Banks
05-07-2012, 01:51 PM
Did you notice that it is now possible to request targets from ground control?

FG28_Kodiak
05-07-2012, 02:15 PM
Yepp :rolleyes:

5./JG27.Farber
05-09-2012, 05:13 PM
Thanks Kodiak,
were not ignoring you its just the patch came out and we have been getting our games to work again and testing that. We will test this new script out later this week.

:-P

Gromic
05-12-2012, 07:16 AM
Interesting read...

Farber, check six....um...I mean PM please....

Cheers

Grom

5./JG27.Farber
05-12-2012, 07:42 AM
I did Gromic but its easier to talk to you than type to you but you were not around. Ill catch up with you on your TS tonight if I can but we have training at 8GMT. ;)

_79_dev
05-16-2012, 10:05 PM
We just tested last version of Your script Kodiak, it works great ....

One thing I noticed:

private void connectToHeadquarterSpeech(AiAircraft aircraft, LocalHeadquarters localHQ)

this is not working, there is no speech any more....

_79_dev
05-16-2012, 11:51 PM
However not all radio stations are visible in the menu. Spawning at EastchurchI can see only Dover and Dunkirk, spawning at Hawkinge I see Dover, Dunkirk and Rad Poe. I cant see Eastbourne from any locations...that may be something to do with brackets there. Can You check it for me Kodiak Please...


List<LocalHeadquarters> Headquarters = new List<LocalHeadquarters>{
{new LocalHeadquarters("Eastbourne", "CallSign24", 50000.0, 178852.52, 197246.94,

new ObserverStation( "Static1","A,4","B,4","C,4","D,4","E,4","A,3","B,3","C,3","D,3","E,3","A,2","B,2","C,2","D,2","E,2","A,1","B,1","C,1","D,1","E,1"))}, //Dover

{new LocalHeadquarters("RadPoe ", "CallSign23", 50000.0, 206102.81, 212379.97,

new ObserverStation( "Static37","F,6","G,6","H,6","I,6","F,5","G,5","H,5","I,5","J,5","F,4","G,4","H,4","I,4","J,4","K,4","F,3","G,3","H,3","I,3","J,3","K,3","L,3","F,2","G,2","H,2","I,2","J,2","K,2","L,2","F,1","G,1","H,1","I,1","J,1","K,1","L,1"))}, //Rye

{new LocalHeadquarters("Dover ", "CallSign22", 50000.0, 246758.98, 235779.38,

new ObserverStation( "Static38","I,7","J,7","K,8","L,8","M,11","N,11","O,11","J,6","K,7","L,7","M,10","N,10","O1,0","L,5","L,6","K,6","K,5","L,6","L,5","L,4","L,3","L,2","M,9","M,8","M,7","M,6","N,9","N,8","L,10","L,11","L,12","O,9","O,8"))}, //Dover

{new LocalHeadquarters("Dunkirk ", "CallSign32", 50000.0, 220622.50, 252385.53,

new ObserverStation( "Static3","A,12","B,12","C,12","D,12","E,12","F,12","G,12","H,12","I,12","J,12","K,12","A,11","B,11","C,11","D,11","E,11","F,11","G,11","H,11","I,11","J,11","A,10","B,10","C,10","D,10","E,10","F,10","G,10","H,10","I,10","J,10","A,9","B,9","C,9","D,9","E,9","F,9","G,9","H,9","I,9","J,9","A,8","B,8","C,8","D,8","E,8","F,8","G,8","A,7","B,7","C,7","D,7","E,7","F,7","G,7","A,6","B,6","C,6","D,6","E,6","B,5","C,5"), //Rye
new ObserverStation( "Royal Observer Corps I","A1,2","B,12","C,12","D,12","E,12","F,12","G,12","H,12","I,12","J,12","K,12","A,11","B,11","C,11","D,11","E,11","F,11","G,11","H,11","I,11","J,11","A,10","B,10","C,10","D,10","E,10","F,10","G,10","H,10","I,10","J,10","A,9","B,9","C,9","D,9","E,9","F,9","G,9","H,9","I,9","J,9","A,8","B,8","C,8","D,8","E,8","F,8","G,8","A,7","B,7","C,7","D,7","E,7","F,7","G,7","A,6","B,6","C,6","D,6","E,6","B,5","C,5"))}, // fake simulation of Royal Observer Corps, none destructable
};

FG28_Kodiak
05-17-2012, 03:34 AM
This is a feature not a bug :rolleyes:

You get only the HQs you are in Range, you can change the max distance there
(for example)
LocalHeadquarters("Eastbourne", "CallSign24", 50000.0 //<--- max Range of communication (50 km)

_79_dev
05-17-2012, 08:08 AM
ok thanks for quick answer...

5./JG27.Farber
05-22-2012, 11:01 AM
Great script Kodiak, it seems to be working fine with no complaints when set up correctly ;) However we neglected the Germans! They had an AOC, can you implement one for them in the same script or would we have to repeat the script tailored to blue?

5./JG27.Farber
07-18-2012, 06:16 PM
Trying to group together 3 radars under one menu entry but now it only replies "no enemy in range". I think it is only scanning the first sector - Eastbourne.

new ObserverStation( "Static1","A,4","B,4","C,4","D,4","E,4","A,3","B,3","C,3","D,3","E,3","A,2","B,2","C,2","D,2","E,2","A,1","B,1","C,1","D,1","E,1"), //Eastbourne

For example:

List<LocalHeadquarters> Headquarters = new List<LocalHeadquarters>{
{new LocalHeadquarters("Luton(SouthEast)", "CallSign24", 100000.0, 178852.52, 197246.94,

new ObserverStation( "Static1","A,4","B,4","C,4","D,4","E,4","A,3","B,3","C,3","D,3","E,3","A,2","B,2","C,2","D,2","E,2","A,1","B,1","C,1","D,1","E,1"), //Eastbourne
new ObserverStation( "Static37","F,6","G,6","H,6","I,6","F,5","G,5","H,5","I,5","J,5","F,4","G,4","H,4","I,4","J,4","K,4","F,3","G,3","H,3","I,3","J,3","K,3","L,3","F,2","G,2","H,2","I,2","J,2","K,2","L,2","F,1","G,1","H,1","I,1","J,1","K,1","L,1"), //rye
new ObserverStation( "Static38","I,7","J,7","K,8","L,8","M,11","N,11","O,11","J,6","K,7","L,7","M,10","N,10","O,10","L,5","L,6","K,6","K,5","L,6","L,5","L,4","L,3","L,2","M,9","M,8","M,7","M,6","N,9","N,8","L,10","L,11","L,12","O,9","O,8"))}, //Dover

{new LocalHeadquarters("Dunkirk (North and AOC)", "CallSign32", 50000.0, 220622.50, 252385.53,

new ObserverStation( "Static3","A,12","B,12","C,12","D,12","E,12","F,12","G,12","H,12","I,12","J,12","K,12","A,11","B,11","C,11","D,11","E,11","F,11","G,11","H,11","I,11","J,11","A,10","B,10","C,10","D,10","E,10","F,10","G,10","H,10","I,10","J,10","A,9","B,9","C,9","D,9","E,9","F,9","G,9","H,9","I,9","J,9","A,8","B,8","C,8","D,8","E,8","F,8","G,8","A,7","B,7","C,7","D,7","E,7","F,7","G,7","A,6","B,6","C,6","D,6","E,6","B,5","C,5","A,12","B,12","C,12","D,12","E,12","F,12","G,12","H,12","I,12","J,12","K,12","A,11","B,11","C,11","D,11","E,11","F,11","G,11","H,11","I,11","J,11","A,10","B,10","C,10","D,10","E,10","F,10","G,10","H,10","I,10","J,10","A,9","B,9","C,9","D,9","E,9","F,9","G,9","H,9","I,9","J,9","A,8","B,8","C,8","D,8","E,8","F,8","G,8","A,7","B,7","C,7","D,7","E,7","F,7","G,7","A,6","B,6","C,6","D,6","E,6","B,5","C,5"))},
};

The coords for the other stations are:

{new LocalHeadquarters("Rye ", "CallSign23", 100000.0, 206102.81, 212379.97,

{new LocalHeadquarters("Dover ", "CallSign22", 100000.0, 246758.98, 235779.38,

{new LocalHeadquarters("Dunkirk , "CallSign32", 100000.0, 220622.50, 252385.53,



EDIT, I always assumed the coords of each radar (220622.50, 252385.53,) was to identify it but looking at it is to see if the radar is in range of the player.