Scripting

eddieb

Vice Admiral
Hi:

Since you guys have done quite a bit of scripting, thought I'd ask for suggestions. I've done a bit of brainstorming on adding some scripting to flight commander, so here are some notes. I'd like to hear your thoughts more on the semantics, not so much on the syntax. Whether there are any critical conditions or actions missing. Just wanted to see if you can come up with any interesting mission concepts which would not be expressible in this scripting language. Ignoring cutscenes, for sake of discussion. Note I'm trying to get away without runtime variables (with the exception of timers), arithmetic operators, and loops.

Scripting

Conditions
has n objects been destroyed at nav(int n)
has n objects been inspected at nav(int n)
has specific ship been destroyed(int shipid)
has specific ship been inspected(int shipid)
has wave started(int waveid)
or
and
not
has n components destroyed(int shipid, int n)
has n turrets destroyed(int shipid, int n)
hasdocked(int shipid)
nav entered(int nav)
timer expired(string timerid)
missiontimegreaterthan(int time)
wave done(int nav, int wavenumber)
nav done(int nav) // all waves done
mission_successful
mission_failed

Actions
Change alignment(int shipid, int alignment)
Set Mission failure()
Set Mission won()
startnextwave()
play comm video(string wavname, string videoname, string name, int alignemnt)
set will autopilot(shipid)
dock to( meship, docktarget)
create timer( timerid)
aiflee( shipid)
aisettarget(meshipid, targetid)
destroy( shipid)
eject( shipid)
displaytext(string text)
jumpout(shipid)
setobjectivestatus(String objective, int status)

actual syntax:

<script>
<if>
<or>
<haswavestarted wave="0" />
<navdone nav="0" />
</or>
</if>
<actions>
<changealignment ship="moray1" alignment="enemy" />
</actions>
<script>
 
Ok, I only have time for a few short comments right now. First things first - how ambitious are you when it comes to mission complexity? Are you planning to allow the possibility of missions as complex as what you see in WCP/SO/UE/Standoff? Or are you content with WC1/2's level of complexity?

If the answer is the latter, please ignore the rest of this post - the language you've come up with is more than good enough for that, and indeed any of the changes I suggest would be counter-productive, because you wouldn't gain any of the benefits. However, if you do want more complexity...

You said you want comments about the semantics rather than the syntax, but I'm gonna have to give you comments about the syntax anyway :p. Trying to get away without variables, arithmetic operators and loops is, IMO, a really, really, really bad thing to do. I've had the opportunity to use a few different scripting languages, and in my experience, the more a language resembles a true programming language, the easier it is to use and learn, and the more expandable it is - the syntax you've chosen means that you are the only one who can come up with conditions. On the other hand, in Standoff, thanks to the use of variables, we've been able to come up with conditions and structures that nobody at Origin ever thought of.
 
Quarto said:
Ok, I only have time for a few short comments right now. First things first - how ambitious are you when it comes to mission complexity? Are you planning to allow the possibility of missions as complex as what you see in WCP/SO/UE/Standoff? Or are you content with WC1/2's level of complexity?

If the answer is the latter, please ignore the rest of this post - the language you've come up with is more than good enough for that, and indeed any of the changes I suggest would be counter-productive, because you wouldn't gain any of the benefits. However, if you do want more complexity...

You said you want comments about the semantics rather than the syntax, but I'm gonna have to give you comments about the syntax anyway :p. Trying to get away without variables, arithmetic operators and loops is, IMO, a really, really, really bad thing to do. I've had the opportunity to use a few different scripting languages, and in my experience, the more a language resembles a true programming language, the easier it is to use and learn, and the more expandable it is - the syntax you've chosen means that you are the only one who can come up with conditions. On the other hand, in Standoff, thanks to the use of variables, we've been able to come up with conditions and structures that nobody at Origin ever thought of.

Yes, I'd very much like to do prophecy/UE/Standoff style missions now. I could already do simple wc1/2 missions without the scripting language. I think you're right in that variables make it much more powerful and handy, and I'll come up with a way to add them. Now as far as build-in engine functions/actions, is this list pretty sufficient to do a Standoff style complex mission. Assuming I add in arithmetic operations and variables.
 
eddieb said:
Yes, I'd very much like to do prophecy/UE/Standoff style missions now. I could already do simple wc1/2 missions without the scripting language. I think you're right in that variables make it much more powerful and handy, and I'll come up with a way to add them. Now as far as build-in engine functions/actions, is this list pretty sufficient to do a Standoff style complex mission. Assuming I add in arithmetic operations and variables.
Ok. Well, my answer would be no - neither the list of the built-in engine functions/actions, nor in general the scripting system you've presented would be sufficient to do a Standoff-style misssion, even with the addition of arithmetic operations and variables.

To understand why this is, you have to firstly consider that, in terms of built-in functions/actions, WCP has over 250 to offer - and even so, we ended up adding another 20-30 functions that WCP didn't have (...and continue to add more). So in order to have all the necessary functions, you'd really have to look at the list of WCP (and our) functions, and include all of them (in fact, even then it would probably be good to add another two or three dozen functions on top :)).

The second thing, as I already mentioned, is the structure. Right now, as I understand it, a mission in your system would essentially consist of a bunch of IF structures. So, it would be like a big FAQ, with a bunch of questions and an answer to each one of them. Our missions are totally different. They're... uh, I'm not actually a programmer, so I may be using the term incorrectly, but I think they're what would be classified as object-oriented code. Every ship, navpoint or other object in the mission has its own set of functions ("function" in this case meaning a set of commands grouped between a "begin" and an "end"). Most objects only have one function (the "main" function), but some have multiples (for example, a "death" function, triggered when a a ship dies). And of course you can declare functions unattached to any particular object, and then call them from within other functions. All of these functions can utilise two and a half types of variables - global variables (which are declared *outside* of the mission, and can be passed on from one mission to another), and local variables (which can be subdivided into two types - you can declare a local variable at the start, and use it for the entire mission, or you can declare a local variable inside a particular function, in which case the rest of the mission won't know about its existence), and the degree of control that this system offers is simply amazing.

The WCP system, in my opinion, is excellent. It's not quite ideal (personally, I think there should be a central main function for the whole mission, which would form the backbone, so to speak - although the current solution, where the player's main function offers pretty much the same functionality), but it's pretty close. Again, the degree of control - it's great. You can have two dozen different things going on at once, and it will all be strictly under your control. You can create a complex network of events that will trigger each other, end each other, et cetera. And, very importantly, you have great clarity - if I want a particular ship to do something, I know that I need to put the code inside its main function. Then, when I edit the mission two weeks later, it's dead-easy to find that main function, because it's named - which your IF structures are not - and because there's only one main function for that one ship - while you might have two dozen IF structures dealing with just one ship.

Don't get me wrong - the structure you've designed is excellent for simple missions (that's why I asked about the kind of missions that you'd like to create). Basically, the way I see it, the two systems have the following advantages and disadvantages.

WCP:
+ Huge flexibility - you can do the same thing in many different ways.
+ Huge power - you can do things that the game's programmers never thought of.
+ Easy to use for anyone with *any* experience in an ordinary programming language.
+ Potentially clean-structured - an incompetent programmer will still clutter up the code to make it unreadable, but anybody else will have no trouble structuring the code in such a way as to make debugging joyfully easy.
- Difficult for anyone who's never had contact with programming.
- Excessively wordy, for simple missions (...but "cut & paste" save the day ;) ).

Your system:
+ Simple, therefore easy to learn for someone who's never programmed anything.
+ Clean, short code, cutting time for simple missions.
- Inflexible - the Q&A format you seem to be using essentially forces the player into doing everything in the form of IF structures.
- Limited - I can't imagine doing anything in this system that you didn't personally think of putting in there (but this would be solved by adding variables and arithmetics), because the system really doesn't allow it.
- Frustrating for people with programming experience - you've come up with a structure different to ordinary programming languages. I personally find this structure difficult to understand, and I suspect others would, too. After a while, you start building up a set of preconceived notions of what programs and scripts should look like... and you then get confused when you see something new (at least, I do).
- Potentially difficult to debug. If I make a complex mission in this system, it really will have multiple IF structures for many ships and navpoints... and that will be hard to debug (in WCP, we do sometimes have missions where a ship's main function includes a dozen IF structures nested within... but I try to keep this as rare as possible, precisely because it is difficult to read, and usually unnecessary in any case).
- Uses HTML-like tags. This is a personal bias... I think I'm allergic to "<word> </word>" tags, even if they really do sometimes make the code more readable :).


Now, it's time for me to get to sleep, so I'd best get to the point, which is my advice.

What is my advice? Simple - don't reinvent the wheel. Remember - Origin spent eight years making WC games. With just about every game, they returned to the question of scripting structure, and reworked it as necessary. WCP is the ultimate conclusion of this process - it is a summary of eight years' worth of experience. Everything in WCP is done for concrete reasons based on Origin's knowledge of what had worked and not worked in the methodology used in previous games. I suppose, had they made another WC game later, they probably would have improved this structure even further - but I am also certain that the changes would have been evolutionary rather than revolutionary. In short, WCP is a standard to be imitated, and hence my advice - Copy the WCP code, including the structure, syntax, and everything else. In fact, you could go a step further - get in touch with Thomas Bruckner and get the WCPPas source code from him. This source code will allow you to understand WCP code (WCPPas compiles the WCP Pascal code into true WCP code). Then you can alter Flight Commander to add support for such code - and apart from gaining a really great scripting language, you would gain the possibility of importing existing WCP/SO/UE missions.

Of course, compiling into WCP code is a time-consuming and rather optional step - just making Flight Commander read WCP Pascal code would be sufficient (and would not require you to get the WCPPas source code from Thomas); in this way, you would lose the capability to import WCP/SO missions, but you would gain the capability to update mission code on-the-fly without the need to recompile.

I realise that you might think I'm simply trying to persuade you to switch to the particular system that I'm used to. But I think the length of this post goes to show that I've given this a lot of thought ;). My experience with games is not limited to modding WCP - apart from modding other games, I've also had the opportunity to work on a few commercial titles. As such, I've had the fortune (and misfortune) to experience a number of different scripting languages... and believe me, the more languages I encounter, the more respect I have for the language that Origin created for WCP. Compared to other languages, it really is nearly perfect.

(BTW, the WCP scripting system is universal, in the sense that the exact same kind of script is used outside of missions, in the series script, for the mission tree and for the clickable hotspots in the room-style interface)
 
Attached is the list of functions we currently use in Standoff. I say currently, because we're constantly adding more :p

Also, here's a small example of part of the mission scripting in a typical Standoff mission. Very basic stuff. I'd be happy to share some of lastest - and not yet released - mission code, but in private, of course ;) It'd show you a more complete picture of what we do, what we need, and even how you could improve from WCP's scripting structure.

Code:
function S_KilrathiSnakeirNav;
var HasComplained = false;
begin
  inc(m_NumberOfAliveEnemies);
  SF_SetObjectFlag(OF_alignment, ALIGN_Evil_Kilrathi);
  SF_ActivateSelf(AO_appear);
  SF_BindToActionSphere(Nav_Snakeir);
  while(1) do
      Begin
           if ((HasComplained = False) and (SF_GetHitpoints < 90)) then
                Begin
                     if (SYS_Random(2)) then AI_SendComm(COMM_HeavyDamage);
                     HasComplained := true;
                end;
           AI_WaitSeconds(1);
      End;
end;



function m_SpawnEnemiesFromSnakeir(NumberToSpawn, SpawnInterval, ShipType, BasePilot, PilotRandomVariation, ShipTargetID);
var i, j,
    RandomPilot;
begin
     for i:=0 to NumberToSpawn do
          begin
               if (m_SnakeirDied = false) then
                   Begin
                        m_GetPos(Snakeir);
                        RandomPilot := (BasePilot + SYS_Random(PilotRandomVariation));
                        NAV_CreateShip(ShipType, RandomPilot, ShipTargetID, @S_KilrathiSnakeirNav, @S_DeathKilrathi, x_pos, y_pos, z_pos-270);
                        j := 0;
                        while ((j < SpawnInterval) and (NAV_WithinSphere(Player)) and (m_SnakeirDied = false)) do
                            Begin
                                 AI_WaitSeconds(1);
                                 inc(j);
                            End;
                   End;
          end;
end;



function M_NavSnakeir;
var
  setup = true,
  x;
begin
  NAV_HideSelf;
  while(1) do begin
    if (NAV_WithinSphere(Player)) then begin
      NAV_ActivateSelf;
      if (setup) then begin
          m_Event := EVENT_None;
          SF_ActivateObject(Snakeir, 0);
          m_SpawnEnemies(6, ST_SHIPID_Jalkehi, PILOT_Kilrathi_Gen_1, 2, 0, NavSnakeir_X +50, NavRalatha_Y - 100, NavRalatha_Z + 900);
          setup:=false;
          AI_WaitSeconds(10);
          m_SpawnEnemiesFromSnakeir(6, 10, ST_SHIPID_Sartha, PILOT_Kilrathi_Gen_1, 2, 0);
          m_SpawnEnemiesFromSnakeir(3, 10, ST_SHIPID_Krant PILOT_Kilrathi_Gen_1, 2, 0);
          m_SpawnEnemiesFromSnakeir(6, 10, ST_SHIPID_Gratha PILOT_Kilrathi_Gen_1, 2, 0);
          m_SpawnEnemiesFromSnakeir(6, 20, ST_SHIPID_Krant PILOT_Kilrathi_Gen_1, 2, 0);
          m_SpawnEnemiesFromSnakeir(6, 20, ST_SHIPID_Drakhri PILOT_Kilrathi_Gen_1, 2, 0);
          m_SpawnEnemiesFromSnakeir(2, 15, ST_SHIPID_Gratha PILOT_Kilrathi_Gen_1, 2, 0);
          m_SpawnEnemiesFromSnakeir(2, 15, ST_SHIPID_Jalkehi PILOT_Kilrathi_Gen_1, 2, 0);
          m_SpawnEnemiesFromSnakeir(2, 15, ST_SHIPID_Krant PILOT_Kilrathi_Gen_1, 2, 0);
          m_SpawnEnemiesFromSnakeir(2, 15, ST_SHIPID_Jalkehi PILOT_Kilrathi_Gen_1, 2, 0);
          m_SpawnEnemiesFromSnakeir(2, 15, ST_SHIPID_Drakhri PILOT_Kilrathi_Gen_1, 2, 0);
      end;
      while(NAV_WithinSphere(Player)) do
          begin
               AI_WaitSeconds(1);
          end;
      AI_WaitSeconds(1);
    end;
    NAV_DeactivateSelf;
    AI_WaitSeconds(1);
  end;
end;



function M_Snakeir;
begin
  SF_SetObjectFlag(OF_Alignment, ALIGN_Evil_Kilrathi);
  AI_WaitUntilActive;
  SF_BindToActionSphere(Nav_Snakeir);
  AI_SetThrottle(100);
  AI_GotoObjRange(Ralatha, 200);
  AI_SetThrottle(0);
  while(1) do begin
      AI_WaitSeconds(1);
  end;
end;
 

Attachments

  • WCP.PAS.txt
    12.5 KB · Views: 52
So after Quarto's very first reply, I did much searching and experimenting on existing scripting languages. When I replied saying I'd come up with a way of adding variables, I meant to say that I was completely throwing away the notion of conditions and actions only, and the notion of writing scripts in XML.

Instead, I've already incorporated a fully featured scripting language,Lua. Lua has been used in commercial games, including World of Warcraft, Neverwinter Nights, and more. Lua has a really small user overhead, no need to include a huge python distribution. It's really easy for me to interface with. Mission designers can also include scripts as plain text, or can chose to compile down scripts to hide them from players.

And the syntax is really easy to read, it looks a lot like wcppas. Here's the first ever Flight Commander script, which actually works, it plays a video and prints the time to the console.

function playvideofunction()
Comm_playVideo("maestro_yes.wav", "kilrathi.avi", "fromlua", 0);
print(Mission_getElapsedTime() );
end

Pete, thanks much, and I'd love to hear your thoughts on extensions, and of course do PM me the code if you're able. I think a reasonable goal for me would be to try to implement the most complex Standoff mission (not cutscene :) ) in Flight Commander .

Scripts would only be required for complex missions, simple wc1 style missions are still going to be possible without scripting.

Anyway, I'm adding hooks to run scripts for each nav point, plus on ship creation and death. The engine by default will still take care of all the tedious counting of friendly and enemies killed and waves, but scripts will be able to override this easily, or obtains and set the counts themselves.

So thanks all for the peer review, I'm very glad I didn't settle on creating a weird new language from scratch. It was much easier to avoid reinventing the wheel, and now I've got a complete scripting language.
 
Mhmm, yep. Lua is a very good option, we used it in one of the games I was involved with (an RPG for the PC/XBox... you won't find it in stores, because the company died before it was done :p ). Let me know when you have the basics implemented. I'd be happy to give it a go - I doubt I'd have time to do too much, but I could probably do a basic mission as a technology presentation - and in doing so, I'd be able to offer more feedback on how you've implemented the language.

By the way, make sure to provide a way to force a script reload during the game - if Flight Commander supports multi-tasking, then it should also support refreshing the scrits without quitting. This kind of thing can save a lot of time (although obviously, inside a mission, a script-refresh would almost inevitably require a mission-restart).

Other things you should keep in mind - as you can see from the wcp.pas file that Pierre attached, WCP commands follow a fairly strict naming scheme. For example, all the commands that are used for AI control begin with AI_ (AI_WaitSeconds, AI_FaceObject, etc). Similarly, all the commands used in the gameflow begin with GF_ (GF_QuickLoad, GF_RunStellarMap, etc). This is a very good convention to follow, for obvious reasons :).
 
Quarto said:
Our missions are totally different. They're... uh, I'm not actually a programmer, so I may be using the term incorrectly, but I think they're what would be classified as object-oriented code. Every ship, navpoint or other object in the mission has its own set of functions ("function" in this case meaning a set of commands grouped between a "begin" and an "end").
You almost got it right. All the ships, capships, navpoints (etc.) in game objects are really C++ objects in the game engine, although the mission script itself is not object-oriented. The engine casts an object [as in object-oriented porgramming] for each ship, nav, etc. thing you declare or spawn in the mission code. So each main/death functions we code are part of the object. (In fact, for the programmers out there, it's simply an unsigned int property of the object containing the CRC of the scripted main function in the mission code). Aaaahhhh, the programmer in me can't stop admiring the beautifull simplicity of the Vision engine. Pure objects who run some AI code, instead of a mission code whith ships "living" in.

Quarto said:
It's not quite ideal (personally, I think there should be a central main function for the whole mission, which would form the backbone, so to speak - although the current solution, where the player's main function offers pretty much the same functionality), but it's pretty close.
That would imply linear scripting, wich goes against the object concept of Vision.

Quarto said:
In fact, you could go a step further - get in touch with Thomas Bruckner and get the WCPPas source code from him. This source code will allow you to understand WCP code (WCPPas compiles the WCP Pascal code into true WCP code). Then you can alter Flight Commander to add support for such code - and apart from gaining a really great scripting language, you would gain the possibility of importing existing WCP/SO/UE missions.
That would indeed be a huge advantage for Flight Commander.
 
Back
Top