PDA

View Full Version : Some Scripting Help...



No.401_Wolverine
Apr-25-2013, 09:45
I've been testing and trying some things to try and build better missions and possible create a new campaign type that could be a lot of fun (a sort of dynamic, 'RPG' style co-op campaign that happens in real time), but I need a few very simple scripts to work before I can push forward.

What I need to do is this:

I need to be able to do the blanket messages that go by at the top of the screen when I load new missions on the fly and when certain targets are destroyed. I can get bits of the code working (kodiak posted a few things that I've been able to kind of sort out), but here's my problems:

1. I can get messages to pop up with a delay, but only when the delay is keyed from the mission start time, or start tick. If I try to have them key off of when the 'sub' mission is loaded over the first one, the message displays okay, but it never goes away. It just sits there big and yellow and never fades.

2. I need a little bit of help in how to make a trigger for objects on the map, such that if an object is destroyed, a mission message displays. These triggers will need to work for objects that aren't on the map at the outset of the mission, but are placed there when a mission is loaded on top. (a 'sub' mission gets loaded over the map with a truck that, if destroyed, gives the message that it has been destroyed).

3. I also need a method of de-spawning an object after a variable method of time. For example, a truck convoy is spawned as part of a mission and proceeds along waypoints until it reaches a location, whereupon it de-spawns (and sends a message). This can be by a variable of time, or a trigger at the end location I suppose.

The purpose for these scripts is as follows:

I will be locally hosting a map that the campaign participants will be joining. As the map plays out, I will be introducing missions on the fly to act and react to the participants' actions (sending out fighter sweeps, bomber raids on their assets, sending convoys between points, etc.) and basically acting as a battle guide, running the whole enemy AI force on the fly by loaded missions from a large set of 'possible' actions. Each mission requires messages to indicate to the players what is happening (I don't have CH Radar stuff). I could give the messages audibly over teamspeak I suppose, but keeping track of multiple missions at once might get tricky with timing. The participants will also be able to ask for various actions like sending out convoys, bomber raids on enemy targets, etc. that have been pre-designed. The idea is to create a dynamic tactical campaign environment for smaller groups that can be run frequently and persistantly (hence the destruction messages, so I can track what to change on the map for the next campaign night).

I will certainly supply anyone who helps with copies of the final campaign mission files if they would like them.

I've looked around, but haven't really found much help. It could be that I've just been looking in the wrong places. If there are threads or sites out there with the info I need, please direct me and I'll head over there!

Cheers,


Wolverine

Salmo
Apr-25-2013, 09:57
1. I can get messages to pop up with a delay, but only when the delay is keyed from the mission start time, or start tick. If I try to have them key off of when the 'sub' mission is loaded over the first one, the message displays okay, but it never goes away. It just sits there big and yellow and never fades.
Try using the overload methods of the gpHUDLogCenter command. In the example below, the message is displayed for 10 seconds.



GamePlay.gpPostMissionLoad(MissionName);
GamePlay.gpHUDLogCenter(ToAll(), "The sub-mission {0} just loaded.", new object[] { MissionName }, 10);

public Player[] ToAll()
#region returns an array containing all the game players
{
try
{
List<Player> players = new List<Player>();
foreach (Player player in GamePlay.gpRemotePlayers()) { players.Add(player); }
return players.ToArray();
}
catch (Exception e)
{
Console.WriteLine("Error: [ToAll] " + e.Message);
return null;
}
}
#endregion



3. I also need a method of de-spawning an object after a variable method of time. For example, a truck convoy is spawned as part of a mission and proceeds along waypoints until it reaches a location, whereupon it de-spawns (and sends a message). This can be by a variable of time, or a trigger at the end location I suppose.
Not tested, but something like this ...


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

AiGroundActor ga = actor as AiGroundActor;
if (ga != null)
{
if (ga.Type() == AiGroundActorType.Truck)
{
ga.Destroy();
GamePlay.gpHUDLogCenter(ToAll(), "{0} has just been removed.", new object[] { actor.Name() }, 10);
}
}
}

No.401_Wolverine
Apr-25-2013, 12:54
Thanks a bunch, Salmo. Couple of questions:

With that message script, I just dump it into the beginning of the script of the mission that I'm intending to load, yes?

With the removal script, I also need to delcare the missionNumber, shortName, and actor. So, usage would go:


OnActorTaskCompleted(1, [shortName], truck_01);

and that would give the message "truck_01 has just been removed."

What is the shortName string for? Also, would that script interrupt another script designed to indicate that the truck was destroyed? It seems like a script designed to say "truck_01 destroyed" would kick off after this one via the ga.Destroy();

Salmo
Apr-25-2013, 22:26
Hmm. Looks like I should run some scripting tutorials to clarify how the scripting structure works.

1. With that message script, I just dump it into the beginning of the script of the mission that I'm intending to load, yes?
You'd think so wouldn't you? But sadly no, there are some scripting intricacies that I've discovered over time. Some script command will work if you place them in the sub-mission & some do not. It seems any command that writes to the game console or the game screen (gpHUDLogCenter) does not work if placed in a sub-mission. This means you'll have to put it into your main mission script file immediately after the command to load the sub-mission (gpPostMissionLoad) as I've indicated above.

2. With the removal script, I also need to delcare the missionNumber, shortName, and actor. So, usage would go:
No need to declare anything. Just copy-paste the entire OnActorTaskCompleted routine into your script file. The OnActorTaskCompleted event code is called automatically every time an actor (any moving object) completes it's task. In the case of a truck convoy, the task is completed when it reaches the end of it's waypoints. When the task is complete, the OnActorTaskCompleted routine is called by the game & the missionNumber, shortName & actor are passed by the game to the routine (so you can use these for whatever purpose you want). Hence, I test whether the actor that has just completed the task is a truck.

No.401_Wolverine
Apr-26-2013, 09:56
Hmm. Looks like I should run some scripting tutorials to clarify how the scripting structure works.

1. With that message script, I just dump it into the beginning of the script of the mission that I'm intending to load, yes?
You'd think so wouldn't you? But sadly no, there are some scripting intricacies that I've discovered over time. Some script command will work if you place them in the sub-mission & some do not. It seems any command that writes to the game console or the game screen (gpHUDLogCenter) does not work if placed in a sub-mission. This means you'll have to put it into your main mission script file immediately after the command to load the sub-mission (gpPostMissionLoad) as I've indicated above.


Hmm, this might not work then. My idea is to load the main mission (we'll call this Campaign Map.mis) and then on the fly, hit ESC, then Load Mission, and load another mission (we'll call this Fighter Sweep.mis). So, for example, we've loaded Campaign Map.mis and the game has started. I don't know what the RED team is going to do until they decide to fly some bombers towards a target. Hearing this, I think "aha, that fighter sweep should intercept them" and I hit ESC, Load Mission, and then browse to and find my Fighter Sweep.mis and load it, thus creating that sweep. When that happens, I want a message to pop up saying something to the effect of "Radar contacts reported in the area of E 5 on patrol." or something like that.

This would be the same if I decided to load a train moving between two points, or a truck convoy, or whatever. So I can't script the sub-mission loads in the main script file because I don't know when or if I'll be loading them.

I'm basically trying to create a one man army, using AI troops, that can oppose a thinking enemy (RED side) which can react in real time to events that unfold. I guess the best comparison is to Dungeons and Dragons, where I would be the Dungeon Master running the bad guys.

And thanks for explaining the OnActorTaskCompleted thing. That makes sense. So I can set up different actions to take place based on what type of actor it is (truck, train, etc.) by making different 'if' statements.

No.401_Wolverine
Apr-28-2013, 20:37
Tried dropping the OnActorTaskCompleted script in and it didn't work :/ Truck just sat there at the end of its waypoints. I'll keep testing it.

No.401_Wolverine
Apr-29-2013, 00:18
Okay, so I've SORT of got a script working that sits inside the sub missions that will display a message when the mission loads (while hosting the server locally - non-dedicated). It's a combination of some stuff I found from Kodiak and Gromic on the 1C forums, plus I've edited one small thing with OnMissionLoaded code. But there's a problem. Here's the code first:


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

public class Mission : AMission
{

private const int All = -1;
private const int Allies = 1;
private const int Axis = 2;

#region MessageSystem

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

//Singleplayer or Dedi Server
if (GamePlay.gpPlayer() != null)
{
if (GamePlay.gpPlayer().Army() == army || army == -1)
Players.Add(GamePlay.gpPlayer());
} // Multiplayer
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 sendChatMessageTo(int army, string msg, object[] parms)
{
List<Player> Players = new List<Player>();

//Singleplayer or Dedi Server
if (GamePlay.gpPlayer() != null)
{
if (GamePlay.gpPlayer().Army() == army || army == -1)
Players.Add(GamePlay.gpPlayer());
} // Multiplayer
if (GamePlay.gpRemotePlayers() != null || GamePlay.gpRemotePlayers().Length > 0)
{
foreach (Player p in GamePlay.gpRemotePlayers())
{
if (p.Army() == army || army == -1)
Players.Add(p);
}
}
if (Players != null && Players.Count > 0)
GamePlay.gpLogServer(Players.ToArray(), msg, parms);
}


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

//Singleplayer or Dedi Server
if (GamePlay.gpPlayer() != null)
{
if ((GamePlay.gpPlayer().Army() == army || army == -1) && GamePlay.gpPlayer().LanguageName().Equals(playerla nguage))
Players.Add(GamePlay.gpPlayer());
} // Multiplayer
if (GamePlay.gpRemotePlayers() != null || GamePlay.gpRemotePlayers().Length > 0)
{
foreach (Player p in GamePlay.gpRemotePlayers())
{
if ((p.Army() == army || army == -1) && p.LanguageName().Equals(playerlanguage))
Players.Add(p);
}
}
if (Players != null && Players.Count > 0)
GamePlay.gpHUDLogCenter(Players.ToArray(), msg, parms);
}


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

//Singleplayer or Dedi Server
if (GamePlay.gpPlayer() != null)
{
if ((GamePlay.gpPlayer().Army() == army || army == -1) && GamePlay.gpPlayer().LanguageName().Equals(playerla nguage))
Players.Add(GamePlay.gpPlayer());
} // Multiplayer
if (GamePlay.gpRemotePlayers() != null || GamePlay.gpRemotePlayers().Length > 0)
{
foreach (Player p in GamePlay.gpRemotePlayers())
{
if ((p.Army() == army || army == -1) && p.LanguageName().Equals(playerlanguage))
Players.Add(p);
}
}
if (Players != null && Players.Count > 0)
GamePlay.gpLogServer(Players.ToArray(), msg, parms);
}


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

//Singleplayer or Dedi Server
if (GamePlay.gpPlayer() != null)
{
if (GamePlay.gpPlayer().Army() == army || army == -1)
Players.Add(GamePlay.gpPlayer());
} // Multiplayer
if (GamePlay.gpRemotePlayers() != null || GamePlay.gpRemotePlayers().Length > 0)
{
foreach (Player p in GamePlay.gpRemotePlayers())
{
if (p.Army() == army || army == -1)
Players.Add(p);
}
}

foreach (string st in exepttoplayerlanguages)
{
Players.RemoveAll(item => item.LanguageName().Equals(st));
}

if (Players != null && Players.Count > 0)
GamePlay.gpLogServer(Players.ToArray(), msg, parms);
}


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

foreach (string st in exepttoplayerlanguages)
{
//Singleplayer or Dedi Server
if (GamePlay.gpPlayer() != null)
{
if (GamePlay.gpPlayer().Army() == army || army == -1)
Players.Add(GamePlay.gpPlayer());
} // Multiplayer
if (GamePlay.gpRemotePlayers() != null || GamePlay.gpRemotePlayers().Length > 0)
{
foreach (Player p in GamePlay.gpRemotePlayers())
{
if (p.Army() == army || army == -1)
Players.Add(p);
}
}
}
foreach (string st in exepttoplayerlanguages)
{
Players.RemoveAll(item => item.LanguageName().Equals(st));
}

if (Players != null && Players.Count > 0)
GamePlay.gpHUDLogCenter(Players.ToArray(), msg, parms);
}


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


private void sendScreenMessageTo(Player player, string msg, object[] parms)
{
if (player != null)
GamePlay.gpHUDLogCenter(new Player[] { player }, msg, parms);
}


private void sendChatMessageTo(Player player, string playerlanguage, string msg, object[] parms)
{
if (player != null)
if (player.LanguageName().Equals(playerlanguage))
GamePlay.gpLogServer(new Player[] { player }, msg, parms);
}


private void sendScreenMessageTo(Player player, string playerlanguage, string msg, object[] parms)
{
if (player != null)
if (player.LanguageName().Equals(playerlanguage))
GamePlay.gpHUDLogCenter(new Player[] { player }, msg, parms);
}


private void sendChatMessageTo(Player player, string[] exepttoplayerlanguages, string msg, object[] parms)
{

List<Player> Players = new List<Player>();

if (player != null)
Players.Add(player);

foreach (string st in exepttoplayerlanguages)
{
if(Players != null)
Players.RemoveAll(item => item.LanguageName().Equals(st));
}

if (Players != null && Players.Count > 0)
GamePlay.gpLogServer(Players.ToArray(), msg, parms);
}


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

if (player != null)
Players.Add(player);

foreach (string st in exepttoplayerlanguages)
{
if (Players != null)
Players.RemoveAll(item => item.LanguageName().Equals(st));
}

if (Players != null && Players.Count > 0)
GamePlay.gpHUDLogCenter(Players.ToArray(), msg, parms);
}

#endregion

// End of Kodiak's message system
// ***
// ***
// ***
// Start of Code from Gromic

public override void OnMissionLoaded(int missionNumber)
{
base.OnMissionLoaded(missionNumber);

Timeout(10, () =>
{SendScreenMessageTo(Axis, "We've begun our bomber attack heading south!");
SendScreenMessageTo(Allies, "Caribou, this is Control. Raid detected 20+ in sector E 2 heading 180.");}
);
}

private void SendScreenMessageTo(int army, string message)
{
if (army == All)
{
GamePlay.gpHUDLogCenter(message);
}
else
{
//Singleplayer (for Testing)
if (GamePlay.gpRemotePlayers() == null ||
GamePlay.gpRemotePlayers().Length <= 0)
{
if (GamePlay.gpPlayer() != null &&
GamePlay.gpPlayer().Army() == army)
{
GamePlay.gpHUDLogCenter(message);
}
}
else // Multiplayer
{
var playersInArmy = new List<Player>();

foreach (var player in GamePlay.gpRemotePlayers())
{
if (player.Army() == army)
{
playersInArmy.Add(player);
}
}

GamePlay.gpHUDLogCenter(playersInArmy.ToArray(), message);
}
}
}

private void SendChatMessageTo(int army, string message)
{
if (army == All)
{
GamePlay.gpLogServer(null, message, null);
}
else
{
//Singleplayer (for Testing)
if (GamePlay.gpRemotePlayers() == null ||
GamePlay.gpRemotePlayers().Length <= 0)
{
if (GamePlay.gpPlayer() != null &&
GamePlay.gpPlayer().Army() == army)
{
GamePlay.gpLogServer(null, message, null);
}

}
else // Multiplayer
{
var playersInArmy = new List<Player>();

foreach (var player in GamePlay.gpRemotePlayers())
{
if (player.Army() == army)
{
playersInArmy.Add(player);
}
}

GamePlay.gpLogServer(playersInArmy.ToArray(), message, null);
}
}
}

// End of code from Gromic
// ***
// ***
// ***

}

Now the problem is that, it works fine. I can load my main map and spawn in and sit there for abit, or take off or whatever, then hit ESC, Load Mission, navigate and find the mission I want, double-click it and then go back into the game. After the short delay (the Timeout bit) the message pops up nicely. It will also work with multiple sub-missions ( I just put this same code into all the sub-missions, and change the messages to suit ). That's not the problem. The problem is that if I have to respawn in the map, the last message displayed will pop up again after the Timout once I've spawned almost as if my spawning into the map is calling the OnMissionLoaded thing from the last loaded submission. Is there a way to stop that from happening?

Salmo
Apr-30-2013, 03:46
Spawn the sub-mission after a pre-set period of time after starting your main mission?

Main mission script:


using System;
using maddox.game;
using System.Diagnostics;

public class Mission : AMission
{
public override void OnBattleStarted()
{
base.OnBattleStarted();

Timeout(60.0, () => // wait 1 minute
{
GamePlay.gpPostMissionLoad(MyMission1.mis); // load mission into the battle (spawn a sub-mission)
GamePlay.gpHUDLogCenter("MyMission1 just loaded"); // tell the players what happened
});

Timeout(120.0, () => // wait 2 minutes
{
GamePlay.gpPostMissionLoad(MyMission2.mis); // load mission into the battle (spawn a sub-mission)
GamePlay.gpHUDLogCenter("MyMission2 just loaded"); // tell the players what happened
});
}
}

No.401_Wolverine
Apr-30-2013, 09:36
That would work, but it wouldn't be what I need.

The missions that get loaded aren't 'pre set'. I might not even load a certain mission depending on what I and/or the players do.

For example:

If the players decide that they are going to attack a certain airbase, I would load a .mis that includes just a fighter sweep in that area.

I may decide that the BLUE army is going to send an armor column towards a town that the RED army is holding. If that attack is not successful, I may launch a second wave. If the attack IS successful, I wouldn't launch another wave at it (the town is already BLUE).

So, calling the .mis files from scripts like that won't work. I don't think an in game 'menu' via the radio commands would work either since I'm going to have a lot of .mis files to choose from and browsing them that way would be too slow.

|450|Devil
May-02-2013, 08:06
If I may say - I think you guys are awesome with your ability to come to grips with the scripting in CLOD without the developer providing any manuals or guides etc.

Power to the inquisitive people.

ps. I've just opened my second bottle of very good NZ white wine.

No.401_Wolverine
May-02-2013, 17:41
If I may say - I think you guys are awesome with your ability to come to grips with the scripting in CLOD without the developer providing any manuals or guides etc.

Power to the inquisitive people.

ps. I've just opened my second bottle of very good NZ white wine.

I come no where close to Salmo's level, that's for sure, but thanks!

And good on you for the wine. I just brought home 24 bottles of Chimay Blue trappistes ale (and a few bottles of Kentish Spitfire Ale - the bottle of britain!). I'm set for the weekend.