PDA

View Full Version : Scripting methods for mission operation


Smokeynz
02-27-2012, 12:23 AM
Method for mission listing within script

Method for Random selection of missions

Method for Triggered missions for Player

Method for Objective mission loading for Player

Firstly I’d like to point out that I am in a learning curve with C# and the methods outlined may be or may not be the best or most efficient way. In many cases I have revised and improved concepts as I go. Secondly much of the core key aspects or syntax is learned from browsing these forums where I have modified for my own purpose or concepts, as such I wanted to return these alternatives I have been working on to the forums. Thirdly got to thank Kodiak for help on the really sticky bits of C# and how they work with CLOD. Finally, this is work in progress, but at a stage where I’d like some feedback on the scripting.

The main purpose of this script is supply a dynamical feel to a mission, by their very nature missions (especially from IL2) have felt scripted and a little sterile. As it happens in my day job I have been developing water management irrigation which simulates rain, the world is chaotic and it in that field applying with chaos changes the reactions more in line with reality. So I am attempting to apply that thought pattern to scripting to simulate a feeling of unknown results. You still have missions, but when they are loaded is unknown.

The second purpose of my methods layout is to make life easier on those who are completely lost with scripting, I am attempting to create scripts which are easy to add missions of users own design, and the script is just an engine that more or less can be just dropped in.
As Kodiak has pointed out in another thread, use of visual express is advisable. It is free for our purpose so why not, it is just a tool to help nothing more.

For the new user of C#, the following layout is setup in blocks of importance (for me anyway), in some cases the order or where the block of script is wrapped is important, but most cases you can place sections just about anywhere and it will still function. Think of the code as lots of small separate scripts talking to each other in a group than a sequential path. (although technically all code is sequential somewhere along the line).

First bit of useful code is to do with C# and visual express
Setting up regions where you can place blocks of scripting makes editing scripts easier. Visual express sees these (CLOD ignores them), so you can hide sections that are not being worked on.

You can use as many as you like

# Region [region title here]

//Script block here

#End region

Ok this shows the regions with title of what is contained, if you click the little (-) or (+) to the left of the screen it opens and closes the regions. Note I also add simple txt separators as well for those who may edit with note pad, the line separators block for visual reference.

This shows the first part of my methods, “Simplified paths”, here I have the main mission path and sub mission paths, and the type is called a “string” with the abbreviated name I called them “MP”, “MPsub” and ” MPtm”. In the core engine we just call MP, MPsub or MPtm as required, but if the user needs to edit the path they only have to edit the main string.



/*=========================================*/

#region Mission paths

string MP = "missions/Multi/Dogfight/ScrimBase/";//simplifying Txt MissionPath
string MPsub = "missions/Multi/Dogfight/ScrimBase/sub/";//simplifying Txt MissionPath sub missions
string MPtm = "missions/Multi/Dogfight/ScrimBase/tm/";//simplifying Txt MissionPath triggered missions

#endregion

/*=========================================*/

Smokeynz
02-27-2012, 12:24 AM
Next block is all the mission lists; here I am using a part of C# “lists”. Here I am making life easier for a user by a simple layout to add user missions, the syntax is quite simple. Also note I have places for mission and messages. To add missions just add a new line within code block brackets and add a coma for each new line, noting the last list line does not have a coma.

new SubM (mission, Message all, Message Red, Message Blue)


/*=========================================*/

#region Inputs for Missions, Triggered Missions & Messages

//NOTE: place sub missions into "sub" folder
//NOTE: sub mis 0~99 series nums
//If you add sub missions to input region, add new lines to list below(index numbering)
//Mis Input layout: new SubM(Mission, Message all, Message Red, Message Blue)

List<SubM> SubMs = new List<SubM>
{
new SubM ("Sub00.mis", "Hurricanes on Patrol", "Hun Patrol !!", "Auctung Hurricanes !"),
new SubM ("Sub01.mis", "Messers on Patrol", "JG on Patrol", "RAF Search !!"),
new SubM ("Sub02.mis", "Bomber Command", "Bomber Escort required", "Auctung Englander Bombers"),
new SubM ("Sub03.mis", "KG Forming up", "Scramble Bombers inbound", "Angels 8"),
new SubM ("Subnull.mis", "random null Sub4", null, null),
new SubM ("Subnull.mis", "random null Sub5", null, null),
new SubM ("Subnull.mis", "random null Sub6", null, null),
new SubM ("Subnull.mis", "random null Sub7", null, null),
new SubM ("Subnull.mis", "random null Sub8", null, null),
new SubM ("Subnull.mis", "random null Sub9", null, null),
new SubM ("Subnull.mis", "random null Sub10", null, null)
};

/*=========================================*/

// Triggered Mission inputs and setup
//NOTE: place triggered missions into "tm" folder

/*=========================================*/

//Triggered Mission lists for UK
//NOTE: UK = 100~149 series num
//If you add missions to input region, add new lines to list below(index numbering)

List<TM100> TM100s = new List<TM100>
{
new TM100 ("t100.mis", "Triggered mission 100", "Intercept the blue hound", "Intercepters are coming Blue"),
new TM100 ("t101.mis", "Triggered mission 101", "Intercept the blue hound", "Intercepters are coming Blue"),
new TM100 ("t102.mis", "Triggered mission 102", "Intercept the blue hound", "Intercepters are coming Blue"),
new TM100 ("t103.mis", "Triggered mission 103", "Intercept the blue hound", "Intercepters are coming Blue"),// add comma for each new line
new TM100 ("t104.mis", "Triggered mission 104", "Intercept the blue hound", "Intercepters are coming Blue"),
new TM100 ("tnull.mis", "random null TM5", null, null),
new TM100 ("tnull.mis", "random null TM6", null, null),
new TM100 ("tnull.mis", "random null TM7", null, null),
new TM100 ("tnull.mis", "random null TM8", null, null),
new TM100 ("tnull.mis", "random null TM9", null, null),
new TM100 ("tnull.mis", "random null TM10", null, null)
};

/*=========================================*/

//Objective Triggered Mission lists for UK
//NOTE: UK = 150~199 series num
//If you add missions to input region, add new lines to list below(index numbering)

List<TM150> TM150s = new List<TM150>
{
new TM150 ("t150.mis", "Objective mission 150 KILL BILL", "Bill has fled the building fire, get him!", "Save Bill!"),
new TM150 ("t151.mis", "Objective mission 151 KILL BILL", "Bill has fled the building fire, get him!", "Save Bill!"),
new TM150 ("t152.mis", "Objective mission 152 KILL BILL", "Bill has fled the building fire, get him!", "Save Bill!"),
new TM150 ("t153.mis", "Objective mission 153 KILL BILL", "Bill has fled the building fire, get him!", "Save Bill!"),// add comma for each new line
new TM150 ("t154.mis", "Objective mission 154 KILL BILL", "Bill has fled the building fire, get him!", "Save Bill!")
};

/*=========================================*/

// Triggered Mission lists for DE
//NOTE: DE = 200~249 series num
//If you add missions to input region, add new lines to list below(index numbering)

List<TM200> TM200s = new List<TM200>
{
new TM200 ("t200.mis", "Triggered mission 200", "Interceptors coming RED", "Intercept the Red devil"),
new TM200 ("t201.mis", "Triggered mission 201", "Interceptors coming RED", "Intercept the Red devil"),
new TM200 ("t202.mis", "Triggered mission 202", "Interceptors coming RED", "Intercept the Red devil"),
new TM200 ("t203.mis", "Triggered mission 203", "Interceptors coming RED", "Intercept the Red devil"),// add comma for each new line
new TM200 ("t204.mis", "Triggered mission 204", "Interceptors coming RED", "Intercept the Red devil"),
new TM200 ("tnull.mis", "random null TM5", null, null),
new TM200 ("tnull.mis", "random null TM6", null, null),
new TM200 ("tnull.mis", "random null TM7", null, null),
new TM200 ("tnull.mis", "random null TM8", null, null),
new TM200 ("tnull.mis", "random null TM9", null, null),
new TM200 ("tnull.mis", "random null TM10", null, null)

};

/*=========================================*/

// Triggered Objective missions lists for DE uk
//NOTE: DE = 250~299 series num
//If you add missions to input region, add new lines to list below(index numbering)

List<TM250> TM250s = new List<TM250>
{
new TM250 ("t250.mis", "Objective mission 250 KILL BOB", "Save BOB!", "BOB has fled the building fire, get him!"),
new TM250 ("t251.mis", "Objective mission 251 KILL BOB", "Save BOB!", "BOB has fled the building fire, get him!"),
new TM250 ("t252.mis", "Objective mission 252 KILL BOB", "Save BOB!", "BOB has fled the building fire, get him!"),
new TM250 ("t253.mis", "Objective mission 253 KILL BOB", "Save BOB!", "BOB has fled the building fire, get him!"),// add comma for each new line
new TM250 ("t254.mis", "Objective mission 254 KILL BOB", "Save BOB!", "BOB has fled the building fire, get him!")
};

#endregion

/*=========================================*/



The observant will have noticed I have some null missions aswell, this is the neat bit of random dynamic play. Note I have in the sub missions folder a null mission of the map in use, but nothing else. If you call a mission even a null you need to load something or the server will create an error, so hence we load just the map name.

Why? You may ask; this is about chance, there will be a chance that on the time period nothing happens. Lists actually numbers missions by their input location, their actual name is irrelevant, and so you can actually list the same mission several times as I have done with the null mission.

Example of chance, if you are calling the listed missions by random chance and you had 100 missions where 50 were nulls, 10 were mission x and the rest were single missions. There is 50% chance nothing will happen on the selection time period, there is also 10% chance mission x will load and 1% chance any of the others will load. Basically you can weight the chances of what mission will load by having more of the same mission in the list, but note keeping null missions where nothing happens is important as well. The reason is this allows the frequency of the main timers to run faster, but then not overload the map with too many missions running at once.

All missions in the script are randomised selection, I have lists for, Sub missions (SubM), Triggered missions UK (TM100), DE (TM200) and objective lists UK(TM150) and DE(TM250).

Note, I use fairly generic naming for all subs, triggered and objective missions and only name the main mission for the title name. Long names in code just wastes space, also makes editing harder.

Smokeynz
02-27-2012, 12:26 AM
The next region block is inputs for timer periods for the core engine, and random input names. If you add more random names, add the random name here. Also the block contains some references to Triggers and 2D locations of trigger locations.

The timers are editable, but note I have setup four periods on fairly tight time periods (1min repeats), where they can overlap at times. Slightly pulsing effect. Again layout is so that edits happen here and you don’t need to touch core engine.


/*=========================================*/

#region Repeats and delays & Random selection varibles

//tick time can alter length due to processing damands, however assume 30/sec
//1 sec = 30 ticks
//1 min = 1800 ticks
//1hour = 108000 ticks

//note, r value must be bigger than d value

private int rt0 = 9000;//5 min repeat
private int dt0 = 1800;//1 min delay

private int rt1 = 9000;//5 min repeat
private int dt1 = 3600;//2 min delay

private int rt2 = 18000;//10 min repeat
private int dt2 = 5400;//3 min delay

private int rt3 = 27000;//15 min repeat
private int dt3 = 7200;//4 min delay

//timer reset(start value, gets randomised 100~900 in map selection,)
//auto reset switch delay stops multible trigger loading, this version resets by random time period(100~900 secs)
//for fixed value unrem the pvt double below, rem out random version in map selection
//private double rsukS1 = 300;

Random rsukS1 = new Random();
private int ukS1 = 1;
Random rsukS2 = new Random();
private int ukS2 = 1;
Random rsukS3 = new Random();
private int ukS3 = 1;

private double rsukS4 = 3600;
private int ukS4 = 1;

Random rsdeS1 = new Random();
private int deS1 = 1;
Random rsdeS2 = new Random();
private int deS2 = 1;
Random rsdeS3 = new Random();
private int deS3 = 1;

private double rsdeS4 = 3600;
private int deS4 = 1;

Random rand = new Random();//random mis selection

#endregion

/*=========================================*/



The next region block actually is part of the lists, these classes pull in the lists into the code to be available for use. If you add more lists, you need another relevant class by name sake to pull the list data into the script.


/*=========================================*/

#region Mission classes

internal class SubM //SubMission
{
public string MN { get; set; }//MissionName
public string MM { get; set; }//MissionMessage
public string MMR { get; set; }//MissionMessageRed
public string MMB { get; set; }//MissionMessageBlue

public SubM(string mn, string mm, string mmr, string mmb)
{
this.MN = mn;
this.MM = mm;
this.MMR = mmr;
this.MMB = mmb;
}
}

internal class TM100 //Triggered missions
{
public string TN { get; set; }//MissionName
public string TM { get; set; }//MissionMessage
public string TMR { get; set; }//MissionMessageRed
public string TMB { get; set; }//MissionMessageBlue

public TM100(string tn, string tm, string tmr, string tmb)
{
this.TN = tn;
this.TM = tm;
this.TMR = tmr;
this.TMB = tmb;
}
}

internal class TM150 //Triggered missions
{
public string TN { get; set; }//MissionName
public string TM { get; set; }//MissionMessage
public string TMR { get; set; }//MissionMessageRed
public string TMB { get; set; }//MissionMessageBlue

public TM150(string tn, string tm, string tmr, string tmb)
{
this.TN = tn;
this.TM = tm;
this.TMR = tmr;
this.TMB = tmb;
}
}

internal class TM200 //Triggered missions
{
public string TN { get; set; }//MissionName
public string TM { get; set; }//MissionMessage
public string TMR { get; set; }//MissionMessageRed
public string TMB { get; set; }//MissionMessageBlue

public TM200(string tn, string tm, string tmr, string tmb)
{
this.TN = tn;
this.TM = tm;
this.TMR = tmr;
this.TMB = tmb;
}
}

internal class TM250 //Triggered missions
{
public string TN { get; set; }//MissionName
public string TM { get; set; }//MissionMessage
public string TMR { get; set; }//MissionMessageRed
public string TMB { get; set; }//MissionMessageBlue

public TM250(string tn, string tm, string tmr, string tmb)
{
this.TN = tn;
this.TM = tm;
this.TMR = tmr;
this.TMB = tmb;
}
}

#endregion

/*=========================================*/

Smokeynz
02-27-2012, 12:27 AM
Next block is constants and parameters, although I have listed some despawntime variables in here as well, these are for altered the despawning time period for AI in game, depending on flight paths and so on and mission design you alter this period for ai time in game before despawn. I have 3 timers, for different conditions. The main, despawntime is set to 2700 or 45mins.



/*=========================================*/

#region const's and params

//Tx's To
private const int All = -1;
private const int Allies = 1;
private const int Axis = 2;

//Despawner time varible
//To times 5min=300 10min=600 15min=900 30min=1800 45min=2700 60min=3600
private double despawntime = 2700;//ai aircraft despawn
private double despawntime2 = 300;//secondary ai aircraft despawn
private double despawntime3 = 2700;//ground ai despawn

//Goes with time indicator, set period for cycle repeat period: value = seconds
//1mins=60sec=1800ticks
private double ts = 60;

//trigger boolean start point condition
bool pDE1 = false;
bool pDE2 = false;
bool pDE3 = false;
bool pDE4 = false;
bool pUK1 = false;
bool pUK2 = false;
bool pUK3 = false;
bool pUK4 = false;
private int rm;
private int bm;
/*
[Trigger]
uk1 TPassThrough 5 2 12025 10315 15000
uk2 TPassThrough 5 2 12025 10315 10000
uk3 TPassThrough 5 2 12025 10315 5000
de1 TPassThrough 5 1 30440 31260 15000
de2 TPassThrough 5 1 30440 31260 10000
de3 TPassThrough 5 1 30440 31260 5000
*/

Point2d DEr1 = new Point2d(30440, 31260);// radar trigger(Red active)
Point2d UKr1 = new Point2d(12025, 10315);// radar trigger(Blue active)

//listen to events from all missions.
public override void OnBattleStarted()
{
base.OnBattleStarted();
MissionNumberListener = -1;
}

#endregion

/*=========================================*/



The next block is the main core engine for sub mission loading on random selection. Before I explain operation here I need to mention alterations to the messaging setup, otherwise it will look complex when it is not.

This is Kodiaks last messaging script, but I have modified with abbreviations, because the other code becomes smaller when you do this. The core change is the abbreviation “STxA” which means Screen text to armies and “Tx”, which means text.



/*=========================================*/

#region STxA Screen Text to Armies

private void STxA(int army, string Tx, 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.gpHUDLogCenter(null, Tx);
else if (Consignees.Exists(item => item.Army() == army))
GamePlay.gpHUDLogCenter(Consignees.FindAll(item => item.Army() == army).ToArray(), Tx);
}

#endregion

/*=========================================*/

Smokeynz
02-27-2012, 12:29 AM
The first part of my core is a simple timer to test time periods, intension is to edit out later but is there to check time mission has played. There is some error because of delays and it does not display every minute due to being overridden by mission loading text.

Now the core engine, note rt0 is repeat and dt0 is delay start, periods setup in previous code,
I set an “int” (interger) “i” as zero as a base of the random selection range, then call the SubM list through the classes input and it becomes SubMs, each mission in the list has its independent list number, this is the random range, zero =>number of missions in list(hence why nulls are important). A mission from the list is randomly selected then loaded, note mission path “MPsub +SubMs[i].MN”, “i” is the list number and MN is mission name, the following line is messages where it selects the mission and relative mission message, MM is mission message. I then wait 2 seconds and display messages to each team, MMR, message to red, MMB message to blue.



/*=========================================*/

#region Main mission operation

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

/*=========================================*/
/* Server timer for mis time testing */
/* rem out when not in use */
/*=========================================*/

// Time. current() in seconds from Battle Start
if (Time.current() >= ts)
{
GamePlay.gpHUDLogCenter("Elapsed Time:Min:" + ts / 60 + " Secs:" + ts + " Ticks:" + ts * 30);
ts = ts + ts;
}

/*=========================================*/

//tick counter mission random loader

if (Time.tickCounter() % rt0 == dt0)
{
int i = 0;
if (SubMs.Count > 0)
i = rand.Next(0, SubMs.Count);
GamePlay.gpPostMissionLoad(MPsub + SubMs[i].MN);
STxA(-1, SubMs[i].MM);
Timeout(2.0, () => { STxA(1, SubMs[i].MMR); STxA(2, SubMs[i].MMB); });
}
if (Time.tickCounter() % rt1 == dt1)
{
int i = 0;
if (SubMs.Count > 0)
i = rand.Next(0, SubMs.Count);
GamePlay.gpPostMissionLoad(MPsub + SubMs[i].MN);
STxA(-1, SubMs[i].MM);
Timeout(2.0, () => { STxA(1, SubMs[i].MMR); STxA(2, SubMs[i].MMB); });
}
if (Time.tickCounter() % rt2 == dt2)
{
int i = 0;
if (SubMs.Count > 0)
i = rand.Next(0, SubMs.Count);
GamePlay.gpPostMissionLoad(MPsub + SubMs[i].MN);
STxA(-1, SubMs[i].MM);
Timeout(2.0, () => { STxA(1, SubMs[i].MMR); STxA(2, SubMs[i].MMB); });
}
if (Time.tickCounter() % rt3 == dt3)
{
int i = 0;
if (SubMs.Count > 0)
i = rand.Next(0, SubMs.Count);
GamePlay.gpPostMissionLoad(MPsub + SubMs[i].MN);
STxA(-1, SubMs[i].MM);
Timeout(2.0, () => { STxA(1, SubMs[i].MMR); STxA(2, SubMs[i].MMB); });
}
}

#endregion

/*=========================================*/




That is the core random engine, but I repeat these four times so as to have four timer periods which because of the timing sometimes happen at the same time. Note: messages that appear at the same will only show the last to load. Just one of those things, you could code around that if you wanted too.
So far the core operates quite well on its own without triggers and objectives, and on appearance in game behaves different every time I test it. This is even though in the test setup we have only 4 sub missions and 7 null missions. I anticipate having 50 or more in sub selection eventually with 50 nulls or something like that. Total user discretion.

Smokeynz
02-27-2012, 12:35 AM
The next block is triggers, this is a nasty area at the moment due to bugs in CLOD. Here though the focus is on Player activity not AI triggering AI. Some guides on scripting for AI has mostly being posted in other threads, in the script here I only use the AI and general trigger function as text to know what is happening and indirectly triggering player triggers.

The setup in this script is representing Radar ranges. In the mission I have a radar unit as the centre piece location and set the triggers as army pass through, 3 triggers same centre different ranges representing zones.

Static46 Stationary.Radar.EnglishRadar1 gb 12025.00 10315.00 50.00
Static44 Stationary.Radar.EnglishRadar1 de 30440.00 31260.00 45.00

[Trigger]
uk1 TPassThrough 5 2 12025 10315 15000
uk2 TPassThrough 5 2 12025 10315 10000
uk3 TPassThrough 5 2 12025 10315 5000
de1 TPassThrough 5 1 30440 31260 15000
de2 TPassThrough 5 1 30440 31260 10000
de3 TPassThrough 5 1 30440 31260 5000

in the script the 2D locations are set as the same centre point.

Point2d DEr1 = new Point2d(30440, 31260);// radar trigger(Red active)
Point2d UKr1 = new Point2d(12025, 10315);// radar trigger(Blue active)

Now the tricky bit,the triggers in the mission activate a list to take note of players within the mission where they are listed as Plebs, then I go through those Plebs and refernce against the 2D location and actual location of the Pleb(player). Then set true or false conditions for each zone range, outside all the zones all are false and nothing happens.

At the end of these checks is a call condition, if any of the above is true then call TrigMis(), this is the random mission selection trigger set up as a private void. The reason this is separate to the Ontrigger is that I also use the triggered loader for objectives aswell.(yes they are randomised aswell).

Before I leave the OnTrigger operation I have typical triggers for AI generating text to players, mainly for checks at this stage, however I do intend to have different missions loading for AI in addition to Player triggering the same zones.



/*=========================================*/

#region Mission triggers

internal double getDistanceFromTo(Point2d startPoint, Point2d endPoint)
{
return startPoint.distance(ref endPoint);
}

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

List<Player> plebs = new List<Player>();
if (GamePlay.gpPlayer() != null)
plebs.Add(GamePlay.gpPlayer());
if (GamePlay.gpRemotePlayers() != null)
plebs.AddRange(GamePlay.gpRemotePlayers());

plebs.ForEach(item =>
{
if (item.Place() != null)
{
Point2d ActorPos = new Point2d(item.Place().Pos().x, item.Place().Pos().y);
//radar circles
if (item.Army() == 1)
{
if (getDistanceFromTo(ActorPos, DEr1) > 15000.1)
{
pDE1 = false;
pDE2 = false;
pDE3 = false;
}

if (getDistanceFromTo(ActorPos, DEr1) < 15000)
if (getDistanceFromTo(ActorPos, DEr1) > 10000.1)
{
pDE1 = true;
pDE2 = false;
pDE3 = false;
}

if (getDistanceFromTo(ActorPos, DEr1) < 10000)
if (getDistanceFromTo(ActorPos, DEr1) > 5000.1)
{
pDE1 = false;
pDE2 = true;
pDE3 = false;
}
if (getDistanceFromTo(ActorPos, DEr1) < 5000)
{
pDE1 = false;
pDE2 = false;
pDE3 = true;
}
}

if (item.Army() == 2)
{
if (getDistanceFromTo(ActorPos, UKr1) > 15000.1)
{
pUK1 = false;
pUK2 = false;
pUK3 = false;
}
if (getDistanceFromTo(ActorPos, UKr1) < 15000)
if (getDistanceFromTo(ActorPos, UKr1) > 10000.1)
{
pUK1 = true;
pUK2 = false;
pUK3 = false;

}
if (getDistanceFromTo(ActorPos, UKr1) < 10000)
if (getDistanceFromTo(ActorPos, UKr1) > 5000.1)
{
pUK1 = false;
pUK2 = true;
pUK3 = false;
}
if (getDistanceFromTo(ActorPos, UKr1) < 5000)
{
pUK1 = false;
pUK2 = false;
pUK3 = true;
}
}
}
});

if ((pDE1 | pDE2 | pDE3 | pUK1 | pUK2 | pUK3) == true)
TrigMis();


// ai as triggers (AI can be treated separately here)
if (shortName.Equals("de1") && active)
{
STxA(-1, shortName + " ai trigger check(red in blue)");// rem out once checks complete
}
if (shortName.Equals("de2") && active)
{
STxA(-1, shortName + " ai trigger check(red in blue)");// rem out once checks complete
}
if (shortName.Equals("de3") && active)
{
STxA(-1, shortName + " ai trigger check(red in blue)");// rem out once checks complete
}
//if (shortName.Equals("de4") && active)
//{
//STxA(-1, shortName + " ai trigger check(blue bomber)");// rem out once checks complete
//}
if (shortName.Equals("uk1") && active)
{
STxA(-1, shortName + " ai trigger check(blue in red)");// rem out once checks complete
}
if (shortName.Equals("uk2") && active)
{
STxA(-1, shortName + " ai trigger check(blue in red)");// rem out once checks complete
}
if (shortName.Equals("uk3") && active)
{
STxA(-1, shortName + " ai trigger check(blue in red)");// rem out once checks complete
}
//if (shortName.Equals("uk4") && active)
//{
//STxA(-1, shortName + " ai trigger check(red bomber)");// rem out once checks complete
//}

if (shortName.Equals("killkubel") && active)
{
Timeout(8.0, () => { STxA(-1, shortName + " YOU KILLED BILL"); });
ukS4 = 1;
}
if (shortName.Equals("safekubel") && active)
{
Timeout(8.0, () => { STxA(-1, shortName + "BILL IS SAFE"); });
}

if (shortName.Equals("killbob") && active)
{
Timeout(8.0, () => { STxA(-1, shortName + " YOU KILLED BOB"); });
deS4 = 1;
}
if (shortName.Equals("safebob") && active)
{
Timeout(8.0, () => { STxA(-1, shortName + "BOB IS SAFE"); });

}
}


private void TrigMis()
{
/*===================de triggered mis=====================*/
if (pDE1 == true)
{
switch (deS1)
{
case 1:
deS1 = 2;//on off switch for trigger operation
Timeout(rsdeS1.Next(100, 901), () => { deS1 = 1; });//random timer reset function
int i = 0;
if (TM200s.Count > 0)
i = rand.Next(0, TM200s.Count);
GamePlay.gpPostMissionLoad(MPtm + TM200s[i].TN);
Timeout(4.0, () => { STxA(-1, TM200s[i].TM); });
Timeout(6.0, () => { STxA(1, TM200s[i].TMR); STxA(2, TM200s[i].TMB); });
break;
case 2:
Timeout(6.0, () => { STxA(-1, "de1 trigger already in action"); });// rem out once checks complete
break;
}
pDE1 = false;
}

if (pDE2 == true)
{
switch (deS2)
{
case 1:
deS2 = 2;//on off switch for trigger operation
Timeout(rsdeS2.Next(100, 901), () => { deS2 = 1; });//random timer reset function
int i = 0;
if (TM200s.Count > 0)
i = rand.Next(0, TM200s.Count);
GamePlay.gpPostMissionLoad(MPtm + TM200s[i].TN);
Timeout(4.0, () => { STxA(-1, TM200s[i].TM); });
Timeout(6.0, () => { STxA(1, TM200s[i].TMR); STxA(2, TM200s[i].TMB); });
break;
case 2:
Timeout(6.0, () => { STxA(-1, "de2 trigger already in action"); });// rem out once checks complete
break;
}
pDE2 = false;
}

if (pDE3 == true)
{
switch (deS3)
{
case 1:
deS3 = 2;//on off switch for trigger operation
Timeout(rsdeS3.Next(100, 901), () => { deS3 = 1; });//random timer reset function
int i = 0;
if (TM200s.Count > 0)
i = rand.Next(0, TM200s.Count);
GamePlay.gpPostMissionLoad(MPtm + TM200s[i].TN);
Timeout(4.0, () => { STxA(-1, TM200s[i].TM); });
Timeout(6.0, () => { STxA(1, TM200s[i].TMR); STxA(2, TM200s[i].TMB); });
break;
case 2:
Timeout(6.0, () => { STxA(-1, "de3 trigger already in action"); });// rem out once checks complete
break;
}
pDE3 = false;
}

/*===================uk triggered mis======================*/

if (pUK1 == true)
{
switch (ukS1)
{
case 1:
ukS1 = 2;//on off switch for trigger operation
Timeout(rsukS1.Next(100, 901), () => { ukS1 = 1; });//random timer reset function
int i = 0;
if (TM100s.Count > 0)
i = rand.Next(0, TM100s.Count);
GamePlay.gpPostMissionLoad(MPtm + TM100s[i].TN);
Timeout(4.0, () => { STxA(-1, TM100s[i].TM); });
Timeout(6.0, () => { STxA(1, TM100s[i].TMR); STxA(2, TM100s[i].TMB); });
break;
case 2:
Timeout(6.0, () => { STxA(-1, "uk1 trigger already in action"); });// rem out once checks complete
break;
}
pUK1 = false;
}

if (pUK2 == true)
{
switch (ukS2)
{
case 1:
ukS2 = 2;//on off switch for trigger operation
Timeout(rsukS2.Next(100, 901), () => { ukS2 = 1; });//random timer reset function
int i = 0;
if (TM100s.Count > 0)
i = rand.Next(0, TM100s.Count);
GamePlay.gpPostMissionLoad(MPtm + TM100s[i].TN);
Timeout(4.0, () => { STxA(-1, TM100s[i].TM); });
Timeout(6.0, () => { STxA(1, TM100s[i].TMR); STxA(2, TM100s[i].TMB); });
break;
case 2:
Timeout(6.0, () => { STxA(-1, "uk2 trigger already in action"); });// rem out once checks complete
break;
}
pUK2 = false;
}

if (pUK3 == true)
{
switch (ukS3)
{
case 1:
ukS3 = 2;//on off switch for trigger operation
Timeout(rsukS3.Next(100, 901), () => { ukS3 = 1; });//random timer reset function
int i = 0;
if (TM100s.Count > 0)
i = rand.Next(0, TM100s.Count);
GamePlay.gpPostMissionLoad(MPtm + TM100s[i].TN);
Timeout(4.0, () => { STxA(-1, TM100s[i].TM); });
Timeout(6.0, () => { STxA(1, TM100s[i].TMR); STxA(2, TM100s[i].TMB); });
break;
case 2:
Timeout(6.0, () => { STxA(-1, "uk3 trigger already in action"); });// rem out once checks complete
break;
}
pUK3 = false;
}

/*===================de objective mis======================*/

if (pDE4 == true)
{
switch (deS4)
{
case 1:
deS4 = 2;//on off switch for trigger operation
Timeout(rsdeS4, () => { deS4 = 1; });//timer reset function
//int bm = 0;
if (TM250s.Count > 0)
bm = rand.Next(0, TM250s.Count);
GamePlay.gpPostMissionLoad(MPtm + TM250s[bm].TN);
Timeout(4.0, () => { STxA(-1, TM250s[bm].TM); });
Timeout(6.0, () => { STxA(1, TM250s[bm].TMR); STxA(2, TM250s[bm].TMB); });
break;
case 2:
//Timeout(6.0, () => { STxA(-1, "Objective trigger already in action"); });// rem out once checks complete
Timeout(6.0, () => { STxA(1, TM250s[bm].TMR); STxA(2, TM250s[bm].TMB); });// repeat mission objective tx
break;
}
pDE4 = false;
}

/*===================uk objective mis======================*/

if (pUK4 == true)
{
switch (ukS4)
{
case 1:
ukS4 = 2;//on off switch for trigger operation
Timeout(rsukS4, () => { ukS4 = 1; });// timer reset function
//int rm = 0;
if (TM150s.Count > 0)
rm = rand.Next(0, TM150s.Count);
GamePlay.gpPostMissionLoad(MPtm + TM150s[rm].TN);
Timeout(4.0, () => { STxA(-1, TM150s[rm].TM); });
Timeout(6.0, () => { STxA(1, TM150s[rm].TMR); STxA(2, TM150s[rm].TMB); });
break;
case 2:
//Timeout(6.0, () => { STxA(-1, "Objective trigger already in action"); });// rem out once checks complete
Timeout(6.0, () => { STxA(1, TM150s[rm].TMR); STxA(2, TM150s[rm].TMB); });// repeat mission objective tx
break;
}
pUK4 = false;
}
}

#endregion

/*=========================================*/




Ok now the second part to trigger operation, the loading of missions. These as you may of guessed are randomly selected as per sub missions, the lists are different for the triggered armies, ie Blue planes spawn for red triggering the radar ranges and visa versa. What spawns though is selected randomly exactly the same way as the core engine. The difference however,is there is a reset function timer. This is so that the trigger zones don’t load a whole pile of missions with every player crossing the line.

I should explain the reset funtion a little for those new to C#, here I am using a “Switch” which is a case selection tool, ie in the case of a number being 2 “case 2” is selected and all code within it is run. I simply have a number switching between 1 and 2, when it is 1 the loading sequence is active and when it is 2 the loader is inactive. Actived I set the case select number to 2 then a timer runs which sets a timer in motion for the random generated period to reset the case to 1 again. Hence switch operation.
The timer reset period though is randomised between 100 and 900 seconds where each zone can be in operation at different times depending how deep the player enters the radar region. For testing I have messages telling players that the zone is already activated, the intension is that players won’t normally get a message.

Smokeynz
02-27-2012, 12:38 AM
Ok now I am going to jump to objective missions which is blocked region after the AI despawner.

Here I have modified an Onplace enter piece of code, original from the collection at 1C, but when the player enters a plane an objective is created, I trigger the TrigMis(), again this triggers the list of objectives in the objective mission list and randomly selects an objective. The selection times out after 30 minutes (period for testing) before objective reset and another objective mission could be loaded. Every player who enters an aircraft wll get a message of the current objective, the opposing team gets a warning a player enters and gets the message to defend that objective, or they could go after their objective. Again as other random lists of missions sets you can have nulls, (there is plenty to play with anyway), currently even though I have a number of objective missions in the lists for each side there is only one objective mission, I’ve yet to create some more, maybe some users can create some! The time reset is still under consideration, deciding on reset on completion or time reset, or both. At the moment objectives do reset on completion and time resets, but not in the case of non completion.




/*=========================================*/

#region OnPlaceEnter Welcome Tx, objective creation

public override void OnPlaceEnter(Player player, AiActor actor, int placeIndex)
{
base.OnPlaceEnter(player, actor, placeIndex);
AiAircraft aircraft = actor as AiAircraft;
if (aircraft != null)
switch (aircraft.Army())
{
case 1:
if (aircraft.Type() == AircraftType.Bomber)
{
{ GamePlay.gpHUDLogCenter(new Player[] { player }, "Bombers, Check your maps for targets!"); }
}
else { GamePlay.gpHUDLogCenter(new Player[] { player }, "Fighters, Escort Bombers or Patrol the lines!"); }
pUK4 = true;// set main mission objective group activation true/false
TrigMis();// apply the truth
break;
case 2:
if (aircraft.Type() == AircraftType.Bomber)
{
{ GamePlay.gpHUDLogCenter(new Player[] { player }, "Bombers, uberprüfen Sie Ihre Karten fur Ziele!"); }
}
else { GamePlay.gpHUDLogCenter(new Player[] { player }, "Fighters, Escort Bombers oder Patrol den Zeilen!"); }
pDE4 = true;
TrigMis();
break;
}
}

#endregion

/*=========================================*/

Smokeynz
02-27-2012, 12:41 AM
Despawner, I have taken the original code and added reset timers, many of the current users use hard code, if you leave the aircraft the plane despawns. The issue for me with this is in the case of controlling a flight of planes. Eg taking a flight of light bombers to a target and swap from plane to plane to bomb the area then take them home again. Why not!

Anyway I set a timer running on creation, on place leave and added on task completed. I actually have 3 timer periods, set via the editable inputs near start in constants and parameters section. I also added ground object removal for objects after the first base mission. The default is 45mins so be aware of what is going on.



/*=========================================*/

#region Despawner

/*
despawner timer added to original code, sets overall time period for actors,
Advantage is, if delayed, say trying to land behind masses of planes, actors despawn at timer period set anyway.
allows for players to fly groups of planes,
Player can re eneter flights or groups, as bombers take them to target, use each at target then fly group home
despawn only triggers once player leaves whole group
*/
public override void OnActorCreated(int missionNumber, string shortName, AiActor actor)
{
base.OnActorCreated(missionNumber, shortName, actor);

// note despawntime (set as varible at start of cs)
if (actor is AiGroundActor && missionNumber > 0)//main mission is left alone
//if ((actor as AiGroundActor).Type() != maddox.game.world.AiGroundActorType.AAGun)//all ai ground except aa guns
Timeout(despawntime3, () =>
{
if (actor != null)
{ (actor as AiGroundActor).Destroy(); }
});

if (actor is AiAircraft)
Timeout(despawntime, () => { damageAiControlledPlane(actor); });
}

public override void OnPlaceLeave(Player player, AiActor actor, int placeIndex)
{
base.OnPlaceLeave(player, actor, placeIndex);
Timeout(despawntime, () => { damageAiControlledPlane(actor); });
}

// Path completion despawner, when flight path ends despawner action triggered
// note:final despawner detects players in group

public override void OnActorTaskCompleted(int missionNumber, string shortName, AiActor actor)
{
base.OnActorTaskCompleted(missionNumber, shortName, actor);
if (actor is AiGroundActor)
if (actor != null)
Timeout(despawntime2, () => { (actor as AiGroundActor).Destroy(); });

Timeout(despawntime2, () => { damageAiControlledPlane(actor); });
}

//check if a player is in any of the "places"
private bool isAiControlledPlane(AiAircraft aircraft)
{
if (aircraft == null) return false;
for (int i = 0; i < aircraft.Places(); i++)
if (aircraft.Player(i) != null)
return false;
return true;
}

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

//actual Aiaircraft despawner, note it polls player detection
private void damageAiControlledPlane(AiActor actorMain)
{
//Timeout(1, () => { STxA(-1, "Despawn time completes for Actor, Despawning"); });//indicates action about to happen, rem out after mission checks complete
foreach (AiActor actor in actorMain.Group().GetItems())
{
if (actor == null || !(actor is AiAircraft))
return;
AiAircraft aircraft = (actor as AiAircraft);
if (!isAiControlledPlane(aircraft))
return;
if (aircraft == null)
return;
(actor as AiAircraft).Destroy();
}
}

#endregion

/*=========================================*/

Smokeynz
02-27-2012, 12:45 AM
Complete code



/**ScrimBase.cs**/
//
//By Smokeynz
//collated collection of parts from 1c, many thanks to credits to all envolved, especially Kodiak,
//Parts modified plus own iterations and random thoughts
//lastmod 25/2/2012
//Dedicated server compatible

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

public class Mission : AMission
{
/*=========================================*/

#region Mission paths

string MP = "missions/Multi/Dogfight/ScrimBase/";//simplifying Txt MissionPath
string MPsub = "missions/Multi/Dogfight/ScrimBase/sub/";//simplifying Txt MissionPath sub missions
string MPtm = "missions/Multi/Dogfight/ScrimBase/tm/";//simplifying Txt MissionPath triggered missions

#endregion

/*=========================================*/
/*=========================================*/

#region Inputs for Missions, Triggered Missions & Messages

//NOTE: place sub missions into "sub" folder
//NOTE: sub mis 0~99 series nums
//If you add sub missions to input region, add new lines to list below(index numbering)
//Mis Input layout: new SubM(Mission, Message all, Message Red, Message Blue)

List<SubM> SubMs = new List<SubM>
{
new SubM ("Sub00.mis", "Hurricanes on Patrol", "Hun Patrol !!", "Auctung Hurricanes !"),
new SubM ("Sub01.mis", "Messers on Patrol", "JG on Patrol", "RAF Search !!"),
new SubM ("Sub02.mis", "Bomber Command", "Bomber Escort required", "Auctung Englander Bombers"),
new SubM ("Sub03.mis", "KG Forming up", "Scramble Bombers inbound", "Angels 8"),
new SubM ("Subnull.mis", "random null Sub4", null, null),
new SubM ("Subnull.mis", "random null Sub5", null, null),
new SubM ("Subnull.mis", "random null Sub6", null, null),
new SubM ("Subnull.mis", "random null Sub7", null, null),
new SubM ("Subnull.mis", "random null Sub8", null, null),
new SubM ("Subnull.mis", "random null Sub9", null, null),
new SubM ("Subnull.mis", "random null Sub10", null, null)
};

/*=========================================*/

// Triggered Mission inputs and setup
//NOTE: place triggered missions into "tm" folder

/*=========================================*/

//Triggered Mission lists for UK
//NOTE: UK = 100~149 series num
//If you add missions to input region, add new lines to list below(index numbering)

List<TM100> TM100s = new List<TM100>
{
new TM100 ("t100.mis", "Triggered mission 100", "Intercept the blue hound", "Intercepters are coming Blue"),
new TM100 ("t101.mis", "Triggered mission 101", "Intercept the blue hound", "Intercepters are coming Blue"),
new TM100 ("t102.mis", "Triggered mission 102", "Intercept the blue hound", "Intercepters are coming Blue"),
new TM100 ("t103.mis", "Triggered mission 103", "Intercept the blue hound", "Intercepters are coming Blue"),// add comma for each new line
new TM100 ("t104.mis", "Triggered mission 104", "Intercept the blue hound", "Intercepters are coming Blue"),
new TM100 ("tnull.mis", "random null TM5", null, null),
new TM100 ("tnull.mis", "random null TM6", null, null),
new TM100 ("tnull.mis", "random null TM7", null, null),
new TM100 ("tnull.mis", "random null TM8", null, null),
new TM100 ("tnull.mis", "random null TM9", null, null),
new TM100 ("tnull.mis", "random null TM10", null, null)
};

/*=========================================*/

//Objective Triggered Mission lists for UK
//NOTE: UK = 150~199 series num
//If you add missions to input region, add new lines to list below(index numbering)

List<TM150> TM150s = new List<TM150>
{
new TM150 ("t150.mis", "Objective mission 150 KILL BILL", "Bill has fled the building fire, get him!", "Save Bill!"),
new TM150 ("t151.mis", "Objective mission 151 KILL BILL", "Bill has fled the building fire, get him!", "Save Bill!"),
new TM150 ("t152.mis", "Objective mission 152 KILL BILL", "Bill has fled the building fire, get him!", "Save Bill!"),
new TM150 ("t153.mis", "Objective mission 153 KILL BILL", "Bill has fled the building fire, get him!", "Save Bill!"),// add comma for each new line
new TM150 ("t154.mis", "Objective mission 154 KILL BILL", "Bill has fled the building fire, get him!", "Save Bill!")
};

/*=========================================*/

// Triggered Mission lists for DE
//NOTE: DE = 200~249 series num
//If you add missions to input region, add new lines to list below(index numbering)

List<TM200> TM200s = new List<TM200>
{
new TM200 ("t200.mis", "Triggered mission 200", "Interceptors coming RED", "Intercept the Red devil"),
new TM200 ("t201.mis", "Triggered mission 201", "Interceptors coming RED", "Intercept the Red devil"),
new TM200 ("t202.mis", "Triggered mission 202", "Interceptors coming RED", "Intercept the Red devil"),
new TM200 ("t203.mis", "Triggered mission 203", "Interceptors coming RED", "Intercept the Red devil"),// add comma for each new line
new TM200 ("t204.mis", "Triggered mission 204", "Interceptors coming RED", "Intercept the Red devil"),
new TM200 ("tnull.mis", "random null TM5", null, null),
new TM200 ("tnull.mis", "random null TM6", null, null),
new TM200 ("tnull.mis", "random null TM7", null, null),
new TM200 ("tnull.mis", "random null TM8", null, null),
new TM200 ("tnull.mis", "random null TM9", null, null),
new TM200 ("tnull.mis", "random null TM10", null, null)

};

/*=========================================*/

// Triggered Objective missions lists for DE uk
//NOTE: DE = 250~299 series num
//If you add missions to input region, add new lines to list below(index numbering)

List<TM250> TM250s = new List<TM250>
{
new TM250 ("t250.mis", "Objective mission 250 KILL BOB", "Save BOB!", "BOB has fled the building fire, get him!"),
new TM250 ("t251.mis", "Objective mission 251 KILL BOB", "Save BOB!", "BOB has fled the building fire, get him!"),
new TM250 ("t252.mis", "Objective mission 252 KILL BOB", "Save BOB!", "BOB has fled the building fire, get him!"),
new TM250 ("t253.mis", "Objective mission 253 KILL BOB", "Save BOB!", "BOB has fled the building fire, get him!"),// add comma for each new line
new TM250 ("t254.mis", "Objective mission 254 KILL BOB", "Save BOB!", "BOB has fled the building fire, get him!")
};

#endregion

/*=========================================*/
/*=========================================*/

#region Repeats and delays & Random selection varibles

//tick time can alter length due to processing damands, however assume 30/sec
//1 sec = 30 ticks
//1 min = 1800 ticks
//1hour = 108000 ticks

//note, r value must be bigger than d value

private int rt0 = 9000;//5 min repeat
private int dt0 = 1800;//1 min delay

private int rt1 = 9000;//5 min repeat
private int dt1 = 3600;//2 min delay

private int rt2 = 18000;//10 min repeat
private int dt2 = 5400;//3 min delay

private int rt3 = 27000;//15 min repeat
private int dt3 = 7200;//4 min delay

//timer reset(start value, gets randomised 100~900 in map selection,)
//auto reset switch delay stops multible trigger loading, this version resets by random time period(100~900 secs)
//for fixed value unrem the pvt double below, rem out random version in map selection
//private double rsukS1 = 300;

Random rsukS1 = new Random();
private int ukS1 = 1;
Random rsukS2 = new Random();
private int ukS2 = 1;
Random rsukS3 = new Random();
private int ukS3 = 1;

private double rsukS4 = 3600;
private int ukS4 = 1;

Random rsdeS1 = new Random();
private int deS1 = 1;
Random rsdeS2 = new Random();
private int deS2 = 1;
Random rsdeS3 = new Random();
private int deS3 = 1;

private double rsdeS4 = 3600;
private int deS4 = 1;

Random rand = new Random();//random mis selection

#endregion

/*=========================================*/
/*=========================================*/

#region Mission classes

internal class SubM //SubMission
{
public string MN { get; set; }//MissionName
public string MM { get; set; }//MissionMessage
public string MMR { get; set; }//MissionMessageRed
public string MMB { get; set; }//MissionMessageBlue

public SubM(string mn, string mm, string mmr, string mmb)
{
this.MN = mn;
this.MM = mm;
this.MMR = mmr;
this.MMB = mmb;
}
}

internal class TM100 //Triggered missions
{
public string TN { get; set; }//MissionName
public string TM { get; set; }//MissionMessage
public string TMR { get; set; }//MissionMessageRed
public string TMB { get; set; }//MissionMessageBlue

public TM100(string tn, string tm, string tmr, string tmb)
{
this.TN = tn;
this.TM = tm;
this.TMR = tmr;
this.TMB = tmb;
}
}

internal class TM150 //Triggered missions
{
public string TN { get; set; }//MissionName
public string TM { get; set; }//MissionMessage
public string TMR { get; set; }//MissionMessageRed
public string TMB { get; set; }//MissionMessageBlue

public TM150(string tn, string tm, string tmr, string tmb)
{
this.TN = tn;
this.TM = tm;
this.TMR = tmr;
this.TMB = tmb;
}
}

internal class TM200 //Triggered missions
{
public string TN { get; set; }//MissionName
public string TM { get; set; }//MissionMessage
public string TMR { get; set; }//MissionMessageRed
public string TMB { get; set; }//MissionMessageBlue

public TM200(string tn, string tm, string tmr, string tmb)
{
this.TN = tn;
this.TM = tm;
this.TMR = tmr;
this.TMB = tmb;
}
}

internal class TM250 //Triggered missions
{
public string TN { get; set; }//MissionName
public string TM { get; set; }//MissionMessage
public string TMR { get; set; }//MissionMessageRed
public string TMB { get; set; }//MissionMessageBlue

public TM250(string tn, string tm, string tmr, string tmb)
{
this.TN = tn;
this.TM = tm;
this.TMR = tmr;
this.TMB = tmb;
}
}

#endregion

/*=========================================*/
/*=========================================*/

#region const's and params

//Tx's To
private const int All = -1;
private const int Allies = 1;
private const int Axis = 2;

//Despawner time varible
//To times 5min=300 10min=600 15min=900 30min=1800 45min=2700 60min=3600
private double despawntime = 2700;//ai aircraft despawn
private double despawntime2 = 300;//secondary ai aircraft despawn
private double despawntime3 = 2700;//ground ai despawn

//Goes with time indicator, set period for cycle repeat period: value = seconds
//1mins=60sec=1800ticks
private double ts = 60;

//trigger boolean start point condition
bool pDE1 = false;
bool pDE2 = false;
bool pDE3 = false;
bool pDE4 = false;
bool pUK1 = false;
bool pUK2 = false;
bool pUK3 = false;
bool pUK4 = false;
private int rm;
private int bm;
/*
[Trigger]
uk1 TPassThrough 5 2 12025 10315 15000
uk2 TPassThrough 5 2 12025 10315 10000
uk3 TPassThrough 5 2 12025 10315 5000
de1 TPassThrough 5 1 30440 31260 15000
de2 TPassThrough 5 1 30440 31260 10000
de3 TPassThrough 5 1 30440 31260 5000
*/

Point2d DEr1 = new Point2d(30440, 31260);// radar trigger(Red active)
Point2d UKr1 = new Point2d(12025, 10315);// radar trigger(Blue active)

//listen to events from all missions.
public override void OnBattleStarted()
{
base.OnBattleStarted();
MissionNumberListener = -1;
}

#endregion

/*=========================================*/
/*=========================================*/

#region Main mission operation

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

/*=========================================*/
/* Server timer for mis time testing */
/* rem out when not in use */
/*=========================================*/

// Time. current() in seconds from Battle Start
if (Time.current() >= ts)
{
GamePlay.gpHUDLogCenter("Elapsed Time:Min:" + ts / 60 + " Secs:" + ts + " Ticks:" + ts * 30);
ts = ts + ts;
}

/*=========================================*/

//tick counter mission random loader

if (Time.tickCounter() % rt0 == dt0)
{
int i = 0;
if (SubMs.Count > 0)
i = rand.Next(0, SubMs.Count);
GamePlay.gpPostMissionLoad(MPsub + SubMs[i].MN);
STxA(-1, SubMs[i].MM);
Timeout(2.0, () => { STxA(1, SubMs[i].MMR); STxA(2, SubMs[i].MMB); });
}
if (Time.tickCounter() % rt1 == dt1)
{
int i = 0;
if (SubMs.Count > 0)
i = rand.Next(0, SubMs.Count);
GamePlay.gpPostMissionLoad(MPsub + SubMs[i].MN);
STxA(-1, SubMs[i].MM);
Timeout(2.0, () => { STxA(1, SubMs[i].MMR); STxA(2, SubMs[i].MMB); });
}
if (Time.tickCounter() % rt2 == dt2)
{
int i = 0;
if (SubMs.Count > 0)
i = rand.Next(0, SubMs.Count);
GamePlay.gpPostMissionLoad(MPsub + SubMs[i].MN);
STxA(-1, SubMs[i].MM);
Timeout(2.0, () => { STxA(1, SubMs[i].MMR); STxA(2, SubMs[i].MMB); });
}
if (Time.tickCounter() % rt3 == dt3)
{
int i = 0;
if (SubMs.Count > 0)
i = rand.Next(0, SubMs.Count);
GamePlay.gpPostMissionLoad(MPsub + SubMs[i].MN);
STxA(-1, SubMs[i].MM);
Timeout(2.0, () => { STxA(1, SubMs[i].MMR); STxA(2, SubMs[i].MMB); });
}
}

#endregion

/*=========================================*/
/*=========================================*/

#region Mission triggers

internal double getDistanceFromTo(Point2d startPoint, Point2d endPoint)
{
return startPoint.distance(ref endPoint);
}

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

List<Player> plebs = new List<Player>();
if (GamePlay.gpPlayer() != null)
plebs.Add(GamePlay.gpPlayer());
if (GamePlay.gpRemotePlayers() != null)
plebs.AddRange(GamePlay.gpRemotePlayers());

plebs.ForEach(item =>
{
if (item.Place() != null)
{
Point2d ActorPos = new Point2d(item.Place().Pos().x, item.Place().Pos().y);
//radar circles
if (item.Army() == 1)
{
if (getDistanceFromTo(ActorPos, DEr1) > 15000.1)
{
pDE1 = false;
pDE2 = false;
pDE3 = false;
}

if (getDistanceFromTo(ActorPos, DEr1) < 15000)
if (getDistanceFromTo(ActorPos, DEr1) > 10000.1)
{
pDE1 = true;
pDE2 = false;
pDE3 = false;
}

if (getDistanceFromTo(ActorPos, DEr1) < 10000)
if (getDistanceFromTo(ActorPos, DEr1) > 5000.1)
{
pDE1 = false;
pDE2 = true;
pDE3 = false;
}
if (getDistanceFromTo(ActorPos, DEr1) < 5000)
{
pDE1 = false;
pDE2 = false;
pDE3 = true;
}
}

if (item.Army() == 2)
{
if (getDistanceFromTo(ActorPos, UKr1) > 15000.1)
{
pUK1 = false;
pUK2 = false;
pUK3 = false;
}
if (getDistanceFromTo(ActorPos, UKr1) < 15000)
if (getDistanceFromTo(ActorPos, UKr1) > 10000.1)
{
pUK1 = true;
pUK2 = false;
pUK3 = false;

}
if (getDistanceFromTo(ActorPos, UKr1) < 10000)
if (getDistanceFromTo(ActorPos, UKr1) > 5000.1)
{
pUK1 = false;
pUK2 = true;
pUK3 = false;
}
if (getDistanceFromTo(ActorPos, UKr1) < 5000)
{
pUK1 = false;
pUK2 = false;
pUK3 = true;
}
}
}
});

if ((pDE1 | pDE2 | pDE3 | pUK1 | pUK2 | pUK3) == true)
TrigMis();


// ai as triggers (AI can be treated separately here)
if (shortName.Equals("de1") && active)
{
STxA(-1, shortName + " ai trigger check(red in blue)");// rem out once checks complete
}
if (shortName.Equals("de2") && active)
{
STxA(-1, shortName + " ai trigger check(red in blue)");// rem out once checks complete
}
if (shortName.Equals("de3") && active)
{
STxA(-1, shortName + " ai trigger check(red in blue)");// rem out once checks complete
}
//if (shortName.Equals("de4") && active)
//{
//STxA(-1, shortName + " ai trigger check(blue bomber)");// rem out once checks complete
//}
if (shortName.Equals("uk1") && active)
{
STxA(-1, shortName + " ai trigger check(blue in red)");// rem out once checks complete
}
if (shortName.Equals("uk2") && active)
{
STxA(-1, shortName + " ai trigger check(blue in red)");// rem out once checks complete
}
if (shortName.Equals("uk3") && active)
{
STxA(-1, shortName + " ai trigger check(blue in red)");// rem out once checks complete
}
//if (shortName.Equals("uk4") && active)
//{
//STxA(-1, shortName + " ai trigger check(red bomber)");// rem out once checks complete
//}

if (shortName.Equals("killkubel") && active)
{
Timeout(8.0, () => { STxA(-1, shortName + " YOU KILLED BILL"); });
ukS4 = 1;
}
if (shortName.Equals("safekubel") && active)
{
Timeout(8.0, () => { STxA(-1, shortName + "BILL IS SAFE"); });
}

if (shortName.Equals("killbob") && active)
{
Timeout(8.0, () => { STxA(-1, shortName + " YOU KILLED BOB"); });
deS4 = 1;
}
if (shortName.Equals("safebob") && active)
{
Timeout(8.0, () => { STxA(-1, shortName + "BOB IS SAFE"); });

}
}


private void TrigMis()
{
/*===================de triggered mis=====================*/
if (pDE1 == true)
{
switch (deS1)
{
case 1:
deS1 = 2;//on off switch for trigger operation
Timeout(rsdeS1.Next(100, 901), () => { deS1 = 1; });//random timer reset function
int i = 0;
if (TM200s.Count > 0)
i = rand.Next(0, TM200s.Count);
GamePlay.gpPostMissionLoad(MPtm + TM200s[i].TN);
Timeout(4.0, () => { STxA(-1, TM200s[i].TM); });
Timeout(6.0, () => { STxA(1, TM200s[i].TMR); STxA(2, TM200s[i].TMB); });
break;
case 2:
Timeout(6.0, () => { STxA(-1, "de1 trigger already in action"); });// rem out once checks complete
break;
}
pDE1 = false;
}

if (pDE2 == true)
{
switch (deS2)
{
case 1:
deS2 = 2;//on off switch for trigger operation
Timeout(rsdeS2.Next(100, 901), () => { deS2 = 1; });//random timer reset function
int i = 0;
if (TM200s.Count > 0)
i = rand.Next(0, TM200s.Count);
GamePlay.gpPostMissionLoad(MPtm + TM200s[i].TN);
Timeout(4.0, () => { STxA(-1, TM200s[i].TM); });
Timeout(6.0, () => { STxA(1, TM200s[i].TMR); STxA(2, TM200s[i].TMB); });
break;
case 2:
Timeout(6.0, () => { STxA(-1, "de2 trigger already in action"); });// rem out once checks complete
break;
}
pDE2 = false;
}

if (pDE3 == true)
{
switch (deS3)
{
case 1:
deS3 = 2;//on off switch for trigger operation
Timeout(rsdeS3.Next(100, 901), () => { deS3 = 1; });//random timer reset function
int i = 0;
if (TM200s.Count > 0)
i = rand.Next(0, TM200s.Count);
GamePlay.gpPostMissionLoad(MPtm + TM200s[i].TN);
Timeout(4.0, () => { STxA(-1, TM200s[i].TM); });
Timeout(6.0, () => { STxA(1, TM200s[i].TMR); STxA(2, TM200s[i].TMB); });
break;
case 2:
Timeout(6.0, () => { STxA(-1, "de3 trigger already in action"); });// rem out once checks complete
break;
}
pDE3 = false;
}

/*===================uk triggered mis======================*/

if (pUK1 == true)
{
switch (ukS1)
{
case 1:
ukS1 = 2;//on off switch for trigger operation
Timeout(rsukS1.Next(100, 901), () => { ukS1 = 1; });//random timer reset function
int i = 0;
if (TM100s.Count > 0)
i = rand.Next(0, TM100s.Count);
GamePlay.gpPostMissionLoad(MPtm + TM100s[i].TN);
Timeout(4.0, () => { STxA(-1, TM100s[i].TM); });
Timeout(6.0, () => { STxA(1, TM100s[i].TMR); STxA(2, TM100s[i].TMB); });
break;
case 2:
Timeout(6.0, () => { STxA(-1, "uk1 trigger already in action"); });// rem out once checks complete
break;
}
pUK1 = false;
}

if (pUK2 == true)
{
switch (ukS2)
{
case 1:
ukS2 = 2;//on off switch for trigger operation
Timeout(rsukS2.Next(100, 901), () => { ukS2 = 1; });//random timer reset function
int i = 0;
if (TM100s.Count > 0)
i = rand.Next(0, TM100s.Count);
GamePlay.gpPostMissionLoad(MPtm + TM100s[i].TN);
Timeout(4.0, () => { STxA(-1, TM100s[i].TM); });
Timeout(6.0, () => { STxA(1, TM100s[i].TMR); STxA(2, TM100s[i].TMB); });
break;
case 2:
Timeout(6.0, () => { STxA(-1, "uk2 trigger already in action"); });// rem out once checks complete
break;
}
pUK2 = false;
}

if (pUK3 == true)
{
switch (ukS3)
{
case 1:
ukS3 = 2;//on off switch for trigger operation
Timeout(rsukS3.Next(100, 901), () => { ukS3 = 1; });//random timer reset function
int i = 0;
if (TM100s.Count > 0)
i = rand.Next(0, TM100s.Count);
GamePlay.gpPostMissionLoad(MPtm + TM100s[i].TN);
Timeout(4.0, () => { STxA(-1, TM100s[i].TM); });
Timeout(6.0, () => { STxA(1, TM100s[i].TMR); STxA(2, TM100s[i].TMB); });
break;
case 2:
Timeout(6.0, () => { STxA(-1, "uk3 trigger already in action"); });// rem out once checks complete
break;
}
pUK3 = false;
}

/*===================de objective mis======================*/

if (pDE4 == true)
{
switch (deS4)
{
case 1:
deS4 = 2;//on off switch for trigger operation
Timeout(rsdeS4, () => { deS4 = 1; });//timer reset function
//int bm = 0;
if (TM250s.Count > 0)
bm = rand.Next(0, TM250s.Count);
GamePlay.gpPostMissionLoad(MPtm + TM250s[bm].TN);
Timeout(4.0, () => { STxA(-1, TM250s[bm].TM); });
Timeout(6.0, () => { STxA(1, TM250s[bm].TMR); STxA(2, TM250s[bm].TMB); });
break;
case 2:
//Timeout(6.0, () => { STxA(-1, "Objective trigger already in action"); });// rem out once checks complete
Timeout(6.0, () => { STxA(1, TM250s[bm].TMR); STxA(2, TM250s[bm].TMB); });// repeat mission objective tx
break;
}
pDE4 = false;
}

/*===================uk objective mis======================*/

if (pUK4 == true)
{
switch (ukS4)
{
case 1:
ukS4 = 2;//on off switch for trigger operation
Timeout(rsukS4, () => { ukS4 = 1; });// timer reset function
//int rm = 0;
if (TM150s.Count > 0)
rm = rand.Next(0, TM150s.Count);
GamePlay.gpPostMissionLoad(MPtm + TM150s[rm].TN);
Timeout(4.0, () => { STxA(-1, TM150s[rm].TM); });
Timeout(6.0, () => { STxA(1, TM150s[rm].TMR); STxA(2, TM150s[rm].TMB); });
break;
case 2:
//Timeout(6.0, () => { STxA(-1, "Objective trigger already in action"); });// rem out once checks complete
Timeout(6.0, () => { STxA(1, TM150s[rm].TMR); STxA(2, TM150s[rm].TMB); });// repeat mission objective tx
break;
}
pUK4 = false;
}
}

#endregion

/*=========================================*/
/*=========================================*/

#region Despawner

/*
despawner timer added to original code, sets overall time period for actors,
Advantage is, if delayed, say trying to land behind masses of planes, actors despawn at timer period set anyway.
allows for players to fly groups of planes,
Player can re eneter flights or groups, as bombers take them to target, use each at target then fly group home
despawn only triggers once player leaves whole group
*/
public override void OnActorCreated(int missionNumber, string shortName, AiActor actor)
{
base.OnActorCreated(missionNumber, shortName, actor);

// note despawntime (set as varible at start of cs)
if (actor is AiGroundActor && missionNumber > 0)//main mission is left alone
//if ((actor as AiGroundActor).Type() != maddox.game.world.AiGroundActorType.AAGun)//all ai ground except aa guns
Timeout(despawntime3, () =>
{
if (actor != null)
{ (actor as AiGroundActor).Destroy(); }
});

if (actor is AiAircraft)
Timeout(despawntime, () => { damageAiControlledPlane(actor); });
}

public override void OnPlaceLeave(Player player, AiActor actor, int placeIndex)
{
base.OnPlaceLeave(player, actor, placeIndex);
Timeout(despawntime, () => { damageAiControlledPlane(actor); });
}

// Path completion despawner, when flight path ends despawner action triggered
// note:final despawner detects players in group

public override void OnActorTaskCompleted(int missionNumber, string shortName, AiActor actor)
{
base.OnActorTaskCompleted(missionNumber, shortName, actor);
if (actor is AiGroundActor)
if (actor != null)
Timeout(despawntime2, () => { (actor as AiGroundActor).Destroy(); });

Timeout(despawntime2, () => { damageAiControlledPlane(actor); });
}

//check if a player is in any of the "places"
private bool isAiControlledPlane(AiAircraft aircraft)
{
if (aircraft == null) return false;
for (int i = 0; i < aircraft.Places(); i++)
if (aircraft.Player(i) != null)
return false;
return true;
}

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

//actual Aiaircraft despawner, note it polls player detection
private void damageAiControlledPlane(AiActor actorMain)
{
//Timeout(1, () => { STxA(-1, "Despawn time completes for Actor, Despawning"); });//indicates action about to happen, rem out after mission checks complete
foreach (AiActor actor in actorMain.Group().GetItems())
{
if (actor == null || !(actor is AiAircraft))
return;
AiAircraft aircraft = (actor as AiAircraft);
if (!isAiControlledPlane(aircraft))
return;
if (aircraft == null)
return;
(actor as AiAircraft).Destroy();
}
}

#endregion

/*=========================================*/
/*=========================================*/

#region OnPlaceEnter Welcome Tx, objective creation

public override void OnPlaceEnter(Player player, AiActor actor, int placeIndex)
{
base.OnPlaceEnter(player, actor, placeIndex);
AiAircraft aircraft = actor as AiAircraft;
if (aircraft != null)
switch (aircraft.Army())
{
case 1:
if (aircraft.Type() == AircraftType.Bomber)
{
{ GamePlay.gpHUDLogCenter(new Player[] { player }, "Bombers, Check your maps for targets!"); }
}
else { GamePlay.gpHUDLogCenter(new Player[] { player }, "Fighters, Escort Bombers or Patrol the lines!"); }
pUK4 = true;// set main mission objective group activation true/false
TrigMis();// apply the truth
break;
case 2:
if (aircraft.Type() == AircraftType.Bomber)
{
{ GamePlay.gpHUDLogCenter(new Player[] { player }, "Bombers, uberprüfen Sie Ihre Karten fur Ziele!"); }
}
else { GamePlay.gpHUDLogCenter(new Player[] { player }, "Fighters, Escort Bombers oder Patrol den Zeilen!"); }
pDE4 = true;
TrigMis();
break;
}
}

#endregion

/*=========================================*/
/*=========================================*/

#region STxA Screen Text to Armies

private void STxA(int army, string Tx, 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.gpHUDLogCenter(null, Tx);
else if (Consignees.Exists(item => item.Army() == army))
GamePlay.gpHUDLogCenter(Consignees.FindAll(item => item.Army() == army).ToArray(), Tx);
}

#endregion

/*=========================================*/
}
//note the last bracket wraps Amission

Smokeynz
02-27-2012, 12:48 AM
Known bugs, On task completed generates one bug I have found. If a path of a flight crosses the last path point of its own path or another operating mission path, even the line from the second to last point to the last point, then that flight that passes over the previous path will trigger the on task completed and despawn. Oh that’s a pain! This bug will trigger for anyone using on task completed so just be aware if planes despawn in places you are not expecting. A method to bypass the bug is to place the last waypoint section to side of flights. Also don’t land planes on takeoff points, the spawning plane with despawn due to being at the operating task completed path.


Finally here is the complete code and mission setup
Please test, I am looking for bugs and quirks and general xxxx.

*****Updates*****
last version 2/3/2012

Smokeynz
02-27-2012, 12:51 AM
reserved 10

41Sqn_Banks
02-27-2012, 06:40 AM
Good stuff :)

Just as a sidenote: Try to use understandable names for variables and method. No one understands what "STaX" or "rsukS1" is supposed to mean without carefully looking at the source code. There is (almost) no restriction in the length of variable and method names so there is no need to shorten them.

Ataros
02-27-2012, 07:10 AM
Many thanks for posting. Very useful!

Please add links to script collection for new users here http://forum.1cpublishing.eu/showthread.php?t=26523&page=2