[Tutorial] The MD - First Contact

The place to discuss scripting and game modifications for X³: Terran Conflict and X³: Albion Prelude.

Moderators: Scripting / Modding Moderators, Moderators for English X Forum

User avatar
Gazz
Posts: 13244
Joined: Fri, 13. Jan 06, 16:39
x4

[Tutorial] The MD - First Contact

Post by Gazz »

For a first time user this can be quite confusing.
Some very basic information on how the MD "ticks" is simply not there.

There are a few tutorials on how to do particular things.
This here is rather on why it is necessary to do them. Sort of an introduction to the "real" tutorials.

Script Editor (SE) experience is useful only insofar, that it helps understanding what the objects do, once the MD tells them to do something.

This is by no means a complete list but only the issues I... encountered so far.
  1. Where are the files?
    Use Doubleshadow's Mod Manager to unpack director\*.* , starting with 01.CAT (provided you want the graphics) or 03.CAT.
  2. Get an XML editor
    You need one. (Yes, you do!)
    Visual Web Developer 2008 is free and it works.
  3. director.htm
    Very important reference file.
    Requires ActiveX so don't try opening it with Firefox.
  4. MD file naming convention
    director\*.xml
    That's it.

    If you mess up the XML structure, the whole file is ignored.
  5. When are MD files read / activated / patched?
    To "activate" your script once the game is *modified*:
    Start new game ...or...
    Load game

    Once "in" the game, all cues stay loaded and are buffered in the savegame.
    Deleting the *.xml file has no effect.

    When loading a game, the buffered code of all cues in the savegame (including those "waiting to fire"!)
    is overwritten by yourscript.xml, provided that the cue names match.

    Therefore there is no need to "patch" the MD code itself and no need for a version check.
    All that could require fixing is the state of the cues and which are currently "hot".
    This isn't more or less work than with the SE... just different.
  6. When do Cues unload? How to get rid of them?
    The only way to completely remove cues from your savegame is

    Code: Select all

      <destroy_cue cue="xgz_mdl_setup"/>
    for all cue names you used.
    That's the uninstall script or when you royally screwed up.

    No idea if that automatically destroys all subcues with it. I would assume so, but...
  7. Variables
    All variables are assigned on the fly without requiring declaration.
    They are not and cannot be limited to a data type.
  8. Variable Scope
    All variables are global and immediately accessible in all cues of all MD scripts.
    Expect typical name concflict issues.

    Only when a variable is used as "mycue.variable" (or in it's home cue, "this.variable"), it is created "semi-local".
    For all practical purposes you just used a longer variable name in doing so, so the name is unique again.

    Every local variable can be accessed in all cues of all MD scripts if you reference it as "mycue.variable" so it's not really "local".

    Not tested:
    Local variable might be affected by the destroy_cue action while global ones are not tied to a specific cue.
  9. The Local / Global variable ambiguity
    While using the same term, MD and SE use completely incompatible systems.
  10. Objects
    Just like in the SE:
    unless you find/create an object, you have no object.
    You have {player.ship} as a constant.
    Supposedly object="" also defaults to {player.ship} but being verbose is far easier to read and debug.
  11. Timing
    Unless otherwise directed, a cue's actions execute instantly when the conditions are met.
    If there is a delay then it's too small for me to measure.
    So obviously, the conditon is checked every frame or nearly so.

    But there ain't no such thing as a free lunch.
    Have 200 unique cues simultaneously waiting for a condition like the distance between 2 objects... and watch your FPS drop.

    The MD cannot magically check many things instantly without using CPU time to do that.
    So just like in the SE - use sensible delays to limit how often things are checked.

    Some conditons (ship destroyed) are "real" events, that are apparently driven by real hardcoded events/signals like in the SE.
    Those are rather harmless in that regard.
  12. When do Cues fire and what do they fire?
    A cue will always wait patiently until all specified conditions are met.
    Then it executes the <action> and all included subcues.

    Code: Select all

        <cue name="cue_test">
          <condition> <object_exists object="gzstation"/> </condition>
          <action>  <incoming_message popup="1" text="Test for condition: station EXIST"/>  </action>
          <cues>
            <cue name="subcue_test">
              <action>  <incoming_message popup="1" text="Unconditional subcue of    station EXIST"/> </action>
            </cue>
          </cues>
        </cue>
    Both messages fire at the same time.
  13. When do Cues refire?
    They don't.
    Once a (non instanced) cue has run, it's done forever.

    You want to keep doing something, you include a failsafe to reset and enable the cue to run again - based on it's conditions.

    Code: Select all

        <cue name="xgz_mdl_setup">
          <condition> <object_exists negate="1" object="gzstation"/> </condition>
          <action>
            <do_all>
              <reset_cue cue="xgz_mdl_setup_reset"/>
              <find_sector name="this.mdl_sector" x="0" y="0" nearest="1"/>
              <create_station name="gzstation" safety="0" race="neutral" typename="SS_DOCK_A_TRADE">
                <position x="00km" y="00km" z="00km"/>
                <sector sector="this.mdl_sector"/>
                <equipment loadout="none"/>
              </create_station>
              <incoming_message popup="1" text="Object not found.  New {object.name@gzstation} created."/>
            </do_all>
          </action>
    
          <cues>
            <cue name="xgz_mdl_setup_reset">
              <condition>
                <check_all>
                  <object_exists negate="1" object="gzstation" />
                </check_all>
              </condition>
              <action>
                <do_all>
                  <incoming_message popup="1" text="Setup complete, object  'gzstation'  not found.  Reseting setup script."/>
                  <reset_cue cue="xgz_mdl_setup"/>
                </do_all>
              </action>
            </cue>
          </cues>
        </cue>
    When the "setup" runs, the subcue starts waiting for it's sole condition - which is that the required station is no longer there.
    If that ever happens, it kicks the main cue back into action.
    Note: The reset automatically applies to all (?) subcues, such as the failsafe cue itself.

    (yes, there are other ways to have reoccurring missions and the likes but that's definitely not "basics")
  14. Operators
    There are (+-*/).
    Nothing binary, no SQR, no nothing. Suck it up.

    Check_Value can supposedly (not tested) compare a string to exact="xyz".
    That's the complete list of string operations.
  15. Game Data
    Very little is accessible.
    Any remotely intelligent weapon/loadout selection is impossible because the MD cannot read laser damage,
    required ammo, missile damage / flags, or even the range of any weapon at all.
    Whenever the MD selects an enemy ship and a weapon loadout it's deciding based on assumptions, not data.

    You can hardcode such data points in your MD script but then your MD script will only work properly in the mod it was written for.
    Since the MD cannot access SE global variables such as 'XTMOD.ACTIVE', it will have a hard time figuring out if a different mod is active.
    Creative workarounds will be required.
Last edited by Gazz on Mon, 16. Aug 10, 12:56, edited 7 times in total.
My complete script download page. . . . . . I AM THE LAW!
There is no sense crying over every mistake. You just keep on trying till you run out of cake.
User avatar
Gazz
Posts: 13244
Joined: Fri, 13. Jan 06, 16:39
x4

Post by Gazz »

** reserved **
User avatar
eldyranx3
Posts: 2178
Joined: Sat, 14. Jan 06, 21:29
xr

Post by eldyranx3 »

Exactly what I needed! Pouring through the 2.0 Plot XML had me scratching my head for a while. Thank You Sir, you are a GoD among Men.
User avatar
ScRaT_GER
Posts: 1962
Joined: Tue, 8. Jan 08, 18:19
x3tc

Post by ScRaT_GER »

Nice overview Gazz!
I have no idea how the game's FPS would be affected by 200 unique cues simultaneously waiting for something like the distance between 2 objects becoming smaller than x m.
For this purpose you can set the "delay" attribute for each cue, which specifies the time interval for checking the cues conditions.
So if you write

Code: Select all

<cue name="X" delay="1s">...</cue>
the cues conditions will be checked in 1s intervals.

However this does not work with event conditions like "object_destroyed", because these only fire momentarily, so only by accident the cues conditions would be checked at the same time the event takes place.

Greetings,
ScRaT
User avatar
Gazz
Posts: 13244
Joined: Fri, 13. Jan 06, 16:39
x4

Post by Gazz »

Yah, so the tooltip says but I intentionally glossed over all details like that.
Performance tuning is a long way off from the first script. =P


I'm more interested in generic info on how the system works here.
How does the leg bone connect to the hip bone?

For instance, how do I "do stuff" with every single member (ship) of a group?
The very basic tools already come in a group flavour but what if I need something else?
In the SE I'd loop through the ship array and get all objects single file.
Cake.
How does that work in the MD?
My complete script download page. . . . . . I AM THE LAW!
There is no sense crying over every mistake. You just keep on trying till you run out of cake.
User avatar
ScRaT_GER
Posts: 1962
Joined: Tue, 8. Jan 08, 18:19
x3tc

Post by ScRaT_GER »

In the SE I'd loop through the ship array and get all objects single file. How does that work in the MD?
Exactly the same way - only less intuitive. :P

Given "this.stations" is a group of stations, you can write something like this:

Code: Select all

<action>
  <do_all exact="{group.object.count@this.stations}" counter="mycount">
    <set_value name="this.tmpStation" exact="{group.object.{counter@mycount}@this.stations}"/>
 </do_all>
</action>
That should work, but I didn't test it.
Btw: The count begins at 1. So the first object of a group can be refenced by {group.object.1@mygroup} instead of {group.object.0@mygroup}.

Greetings,
ScRaT
User avatar
Aro
Posts: 2770
Joined: Tue, 15. Jul 03, 00:35
x4

Post by Aro »

I have no idea how the game's FPS would be affected by 200 unique cues simultaneously waiting for something like the distance between 2 objects becoming smaller than x m.
FPS will plummet like a rock. A simple create station and a ship script running unchecked will start to slow a computer down after a few seconds. Was interesting to watch though.

Variable scope can also be a little weird at times.

Large cues holding many sub-cues can be an issue as well, some of the cues may not fire. Seems that ES uses a 'cue.Step' to control missions. And breaking them up with a main controller to run them.
Shush
Posts: 244
Joined: Sat, 6. Dec 03, 16:21
x4

Post by Shush »

Great MD topic, I'll add my own experiences...

You could also do the following with ScRaT_GER's example:

Code: Select all

 
<cue name="Blah.setup">
  <action>
    this.stations = whatever
  </action>

  <cues>           
    <cue name="Blah.instance.handler" instantiate="static">
      <condition>
        <check_value value="{group.object.count@parent.stations}" min="1"/>
      </condition>
          
      <action>
        <do_all>
          <set_object name="this.tmpStation" value="{group.object.1@parent.stations}"/>
          <remove_object_from_group object="{group.object.1@parent.stations}" group="parent.stations"/>

          <do something with "this.tmpStation"/>

        </do_all>
      </action>
    </cue>
  </cues>

</cue>
What this does is fork of a complete copy, (instance), of every cue and subcue under the instantiate="static" command. Each instance exists seperately until it and all of its sub cues finish or are cancelled, the instance is then destroyed. The way I interpret the instantiate="static" command, is that the original cue is static and that the copy generated by the condition is the instance.

The main cue Blah.setup is run once, the sub cue Blah.instance.handler is copied and run, (instanced), everytime the group.object.count is bigger than or equal to 1. In other words the "Blah.instance.handler" check_value condition constantly loops at some pre-determined internal mission director frequency, (probably within the main game loop and thus probably limited to actual frame rendering frequency). This is where the delay="" command is extremely useful for performance, it would limit the polling and hence potential instance generation to "n" per second, where delay="ns".

Ok, so what stops the instancing from running out of control? Well if you're not careful, nothing, that's why the remove_object_from_group command in this example saves the day. It reduces the size of the station queue/list/array by one for each instance that is generated.

You will also notice that I referenced this.tmpStation as parent.tmpStation in the sub cue, I could have called the group variable Blah.stations which would be a global variable name not only accessible in all cues of this MD file but all cues of all MD files, this has obvious advantages for accessing global data between MD files, but drawbacks for localised data. Two very important reasons to use this., parent. and parent.parent., (as you traverse down the sub queue tree, you prepend one parent. for each cue) etc, are;

1) MD does a search for variable names throughout your entire cue/sub cue tree structure trying to find the actual variable, I'm guessing with complicated cue trees this could be fairly performance intensive, using this., parent., etc allows MD to know exactly which cue your variable is located within, without having to search for it.

2) Using instances will often require you to reference the instance's copy of a variable, you must use this. and parent. etc in this particular example, any other way of referencing your variable names will cause all instances to reference a global copy of that variable.

So what was the point of all this? It's obviously more complicated than ScRaT_GER's example but it lends itself to some extremely powerful multiple thread/fork/task techniques that are very difficult or impossible to perform using loops or sequential code. For example in my upcoming set of LIFE scripts and MD missions I use this technique frequently to allow me to add dynamic missions to the universe based on the universe's current state rather than pre-determined click on this, stuff spawns, do something, type missions. With instances I can be reactive to pre-existing ships/stations being attacked, create an actor, offer a conversation/mission and handle universe events without having to fake anything.

P.S. Anyone who wants to experiment with instancing may find it hairy to wrap your head around the fact that there can be multiple copies of your cues all firing simultaneously, here's a few lines of helper code that can make it much easier to track the instances themselves.

P.P.S. This code is for instructional purposes, but it should run with trivial modifications.

Code: Select all

<!-- Use this within your instance, prints instance ID and total instance count -->
<incoming_message author="Blah.instance.handler {instance@Blah.instance.handler}" text="Total Instance Count: {instances.count@{static@Blah.instance.handler}}"/>

<!-- Global instance count, put this in a setup cue -->
<set_value name="Blah.instancesCount" exact="0"/>

<!-- Use this before the instanced cues -->
<cue name="Blah.instance.watcher" delay="1s">
  <condition>
    <check_value negate="1" value="{instances.count@{Blah.instance.handler}}" exact="{value@Blah.instancesCount}"/>
  </condition>

  <action>
    <do_all>
      <!-- If instances count = 0 then do something -->
      <do_if value="{instances.count@{LIFE.GNS.StationHandler}}" exact="0">
        <incoming_message author="Blah.instance.watcher" text="Instance Count: 0"/>
      </do_if>
              
      <!-- If instances count has decreased, then do something -->
      <do_if value="{instances.count@{Blah.instance.handler}}" max="{value@Blah.instancesCount}-1">
        <incoming_message author="Blah.instance.watcher" text="Instance Count: decreased"/>
      </do_if>
      
      <!-- If instances count has increased, then do something -->
      <do_if value="{instances.count@{Blah.instance.handler}}" min="{value@Blah.instancesCount}+1">
        <incoming_message author="Blah.instance.watcher" text="Instance Count: increased"/>
      </do_if>

      <!-- Save instances count -->
      <set_value name="Blah.instancesCount" exact="{instances.count@{Blah.instance.handler}}"/>
    </do_all>
  </action>
</cue>
User avatar
Gazz
Posts: 13244
Joined: Fri, 13. Jan 06, 16:39
x4

Post by Gazz »

Shush wrote:Ok, so what stops the instancing from running out of control? Well if you're not careful, nothing, that's why the remove_object_from_group command in this example saves the day. It reduces the size of the station queue/list/array by one for each instance that is generated.
Wouldn't this run your instanced instructions once for every group member and then never again?
When the group count reaches 0, that's it forever.


So

Code: Select all

<cue name="Blah.instance.handler" instantiate="static"> 
leads to this cue being run multiple times even though Blah.Setup would be a completed cue once it has reached the point of Blah.instance.handler exactly once?

Anyone who wants to experiment with instancing may find it hairy to wrap your head around the fact that there can be multiple copies of your cues all firing simultaneously, here's a few lines of helper code that can make it much easier to track the instances themselves.
That in itself is pretty normal. SE Signal and Jobs scripts do that all the time. Or one turret script that runs on several tasks of the same object, using a weapon changer script that "serves" all of them at once.

The difficulty with MD instances is... I dunno. If I knew I could probably understand it. Which I haven't, yet.
I'm not even interested in specific examples but rather why the cues do what they do.
The jump between single and multiple instance is triggered by what, when, and how often?
My complete script download page. . . . . . I AM THE LAW!
There is no sense crying over every mistake. You just keep on trying till you run out of cake.
Shush
Posts: 244
Joined: Sat, 6. Dec 03, 16:21
x4

Post by Shush »

Wouldn't this run your instanced instructions once for every group member and then never again?
When the group count reaches 0, that's it forever.
The first condition within the instanced cue is what initiates the copy/instancing process, so in the example I gave whenever the object count is >= 1 a new instance is spawned. As far as the top level parent cue, i.e. the static cue is concerned, the condition is never met and it sits there idle waiting forever unless you happen to cancel or complete it, which you can do elsewhere in a static cancel/reset cue or even within the instance that was just spawned.
leads to this cue being run multiple times even though Blah.Setup would be a completed cue once it has reached the point of Blah.instance.handler exactly once?
Blah.Setup is never completed, that's the tricky bit, the instance is spawned and Blah.Setup sits there passively idling waiting to spawn new instances. If I was to guess as to how it is implemented internally; Blah.Setup gets to the instantiate="static" line, checks the condition, if the check is satisified it copies every cue and sub cue under this condition and then begins processing the new instance. The condition that spawned the instance is not marked as completed so that when processing gets back to Blah.Setup it happily performs the condition again and again ad infinitum until you stop it with a cancel/reset as mentioned above.
That in itself is pretty normal. SE Signal and Jobs scripts do that all the time. Or one turret script that runs on several tasks of the same object, using a weapon changer script that "serves" all of them at once.
Yup I started with MSCI and progressed to MD, they are both powerful in their own rights, but the real golden opportunity the devs missed was providing a data passing mechanism between the two. Though you can get around this by having MD create objects in the universe, running a script to tell MSCI about them and then using either homebase or user wing arrays to pass back objects fron the MSCI to the MD. I do this a lot in my stuff and though it's a serious kludge, it works really well with only 1 or 2 caveats.
The difficulty with MD instances is... I dunno. If I knew I could probably understand it. Which I haven't, yet. I'm not even interested in specific examples but rather why the cues do what they do. The jump between single and multiple instance is triggered by what, when, and how often?
I've tried to explain it above, it will click, the eureka moment I had was when I wrote a very simple instancing example and used incoming message to watch what was happening; in exactly the same way I use XTail with 8-10 log files running simultaneously on a second monitor to debug what the game really does in the MSCI, it's the only way to fully appreciate all the idosyncrasies and gotchya's.
User avatar
Gazz
Posts: 13244
Joined: Fri, 13. Jan 06, 16:39
x4

Post by Gazz »

That already makes more sense.
The tricky bit with stuff like that usually isn't getting an answer but finding the right question.
Takes a real noob to do that so manuals/tutorials written by experts suck when they don't explain the "obvious" issues.

It's obvious that there must be instances, both from seeing the game work and the "instantiate" property of the cue tag.
Only without a clear understanding of what exact bits of what cues are executed and when, it's not useable.

The SE is easier in that reagard.
You start an action - something happens.
You have it wait until conditions XYZ are met - then it does something.
The sequence of events is always clear.
My complete script download page. . . . . . I AM THE LAW!
There is no sense crying over every mistake. You just keep on trying till you run out of cake.
User avatar
ScRaT_GER
Posts: 1962
Joined: Tue, 8. Jan 08, 18:19
x3tc

Post by ScRaT_GER »

With instances I can be reactive to pre-existing ships/stations being attacked, create an actor, offer a conversation/mission and handle universe events without having to fake anything.
Wow, that's a really good technique! Until now, I didn't even know how instances really work! And I still don't completely, which is why I have the following questions:

You wrote:
Blah.Setup is never completed, that's the tricky bit, the instance is spawned and Blah.Setup sits there passively idling waiting to spawn new instances. If I was to guess as to how it is implemented internally; Blah.Setup gets to the instantiate="static" line, checks the condition, if the check is satisified it copies every cue and sub cue under this condition and then begins processing the new instance. The condition that spawned the instance is not marked as completed so that when processing gets back to Blah.Setup it happily performs the condition again and again ad infinitum until you stop it with a cancel/reset as mentioned above.
So you don't need "reset_cue cue="Blah.Setup" in your instance cues? What happens if you supply one?
but the real golden opportunity the devs missed was providing a data passing mechanism between the two
Yes, that's a pity.
2) Using instances will often require you to reference the instance's copy of a variable, you must use this. and parent. etc in this particular example, any other way of referencing your variable names will cause all instances to reference a global copy of that variable.
That's interesting, too.
As far as I understood it, when cues are instantiated, cues with the exact same name are created and run. When I now tried to reference a variable not using this or parent, which copy would be rerenced?

I'm asking that, because atm I wouldn't know how to destroy all instances of a cue or some objects created by instances of a cue (e.g. for uninstalling). Of course I could use a instance-local uninstall-cue, but I guess it might be nicer to seperate the "working" cues from the ones to uninstall.

Can instances of a cue be referenced by one of the following commands?
director.htm wrote:{instance@cue}: {instance@this} - Instance id of the specified cue
{static@cue}: {static@this} - Instance id of the static cue of a cue instance
{instances.count@cue}: {instances.count@this} - Number of instances of this static cue
{instances.num@cue}: {instances.1@this} - Instance id of an instance of this static cue
So could you write something like "<destroy_cue cue="{instance.1@myStaticCue}/>" ?

Greetings,
ScRaT
Shush
Posts: 244
Joined: Sat, 6. Dec 03, 16:21
x4

Post by Shush »

So you don't need "reset_cue cue="Blah.Setup" in your instance cues? What happens if you supply one?
Nope you definitely don't need to reset your top level cue, and for simple instance handlers you wouldn't. The top level cue could sit there happily checking the initial instance condition and spawning instances forever. The obvious caveat is that you need a mechanism to make sure that the spawning doesn't get out of control, that usually means that each instance somehow changes the properties of the initial condition.

The instances themselves once fully complete or cancelled, destroy themselves out of existance; so they spawn as self contained entities that can replicate MD behavioural rules, that react and process to spontaneous events that occur throughout the universe through various MD condition checks.

For more complex instance handlers I've found I invariably do end up reset'ing the top level cue, cancelling individual instances and even cancelling all instances. There are multiple ways of doing these exceptions, but the cleanest way I have found so far is by having an instance watcher cue, similar to the code I showed in a reply to gazz's post. By using global variables and knowing how and when I want to control the instances I can feed information into the instance watcher which sits just above the instance handler and have it somewhat elegantly control everything.

I've added some examples code from one of my current MD projects.

Code: Select all

<!-- Check if instances count has changed -->
        <cue name="LIFE.GNS.Restart" delay="1s">
          <condition>
            <check_value negate="1" value="{instances.count@{LIFE.GNS.StationHandler}}" exact="{value@LIFE.GNS.instancesCount}"/>
          </condition>

          <action>
            <do_all>
              <!-- If instances count = 0 then restart LIFE.GNS -->
              <do_if value="{instances.count@{LIFE.GNS.StationHandler}}" exact="0">
                <!--incoming_message author="LIFE.GNS.Restart" text="Instance Count: {instances.count@{LIFE.GNS.StationHandler}}"/-->
                <reset_cue cue="LIFE.GNS"/>
              </do_if>

              <!-- If instances count has decreased, then cancel all instances -->
              <do_if value="{instances.count@{LIFE.GNS.StationHandler}}" max="{value@LIFE.GNS.instancesCount}-1">
                <set_value name="LIFE.GNS.cancelInstances" exact="1"/>
              </do_if>

              <!-- Save instances count -->
              <set_value name="LIFE.GNS.instancesCount" exact="{instances.count@{LIFE.GNS.StationHandler}}"/>
              <reset_cue cue="LIFE.GNS.Restart"/>
            </do_all>
          </action>
        </cue>
LIFE.GNS is the top most level cue and performs all the setup, it sits above LIFE.GNS.Restart. LIFE.GNS.Restart is the instance watcher that sits just above the instance handler, LIFE.GNS.StationHandler. The instances themselves are spawned by LIFE.GNS.StationHandler which as part of their conditions, checks the global variable LIFE.GNS.cancelInstances, when the global goes true they all systematically cancel themselves and then the top most level cue is reset, (for my code I absoloutely must have 0 instances running before I completely reset LIFE.GNS). This allows me to on the fly to react to events in the universe as I mentioned earlier. This bit of code belongs to an MD file that allows me to spawn station actors on any number of stations in a sector belonging to any number of races, if the number of stations, races or player's notoriety to these stations/races changes, then everything adapts within a few seconds with appropriate actors being destroyed and new actors generated.
That's interesting, too.
As far as I understood it, when cues are instantiated, cues with the exact same name are created and run. When I now tried to reference a variable not using this or parent, which copy would be rerenced?
I re-read what I wrote and it is poorly written, as you said when an instance is spawned, everything about the instanced cues and their sub cues is copied; i.e. the cues, the conditions, the actions, the variables etc. But depending on the scope of a variable, i.e. whether it is local or global, MD will search for the variable in different ways. I pulled this out of the XMDGuide on page 10 -->

Code: Select all

If it doesn't find a cue of the correct name then it will start looking down all cue structures but may not always find the correct instance (in fact for instantiated cues it will always find the master instance). You can shortcut this 
process and prevent the latter part of it by using "this" to refer to the current cue instance rather than the cue name, and "parent" to refer to the parent cue instance."
The important bit there is "(in fact for instantiated cues it will always find the master instance)"...Now to be honest I haven't fully tested the MD to see if it behaves exactly the way this line of text reads to me, but the way this. and parent. work is exactly as expected, so my assumption is that a global variable will always be referenced from within the static master cue by the copied instanced cues rather than from within the instances themselves.
I'm asking that, because atm I wouldn't know how to destroy all instances of a cue or some objects created by instances of a cue (e.g. for uninstalling). Of course I could use a instance-local uninstall-cue, but I guess it might be nicer to seperate the "working" cues from the ones to uninstall.
If you can perform your cleanup in the instance itself I will go out on a limb and say that generally it is usually easier to do it locally within the instance itself. But as I showed above sometimes you need a more macroscopic control, using my watcher cue I am able to control when instances destroy themselves. If you were able to seperate worker instances from cleanup instances, then I am guessing you could use either method to control how the cleanup instances did their thing. Because my MD projects seem to be all based on the dynamic state of the universe without me creating new objects, this isn't much of an issue for me as I have very little to clean up; but when I do have to clean things up, like actors, offers, conversations etc, I can invariably handle it all locally within the worker instance itself.

{instance@cue}: returns a unique identifier for an instance, very useful for debugging and differentiating an instance from other instances. You can only use it within an instanced cue and the value it returns is very similar to other ID's, like actor ID's, object ID's etc.
{static@cue} returns a unique identifier for the master copy of the instanced cues, so for example if you need to know the total number of spawned instances within an instance, you would query the instance count of the master copy, not of one of the individual instances; like so:

Code: Select all

{instances.count@{static@LIFE.attacked.handler}}
where LIFE.attacked.handler is the name of the instanced cue.
So could you write something like "<destroy_cue cue="{instance.1@myStaticCue}/>" ?[/code]

I haven't tried this but it absoloutely should work, in fact I think I'll work it into some of my code and see how I go. You would most likely be performing it in a non instanced cue and when you do that if you reference an instanced cue by name then you be default reference the static copy, i.e. the master copy. What would be interesting is if you could destroy an instance from another instance, which would have to look something like this -->

Code: Select all

<destroy_cue cue="{instance.1@{static@myInstancedCue}}/>
User avatar
ScRaT_GER
Posts: 1962
Joined: Tue, 8. Jan 08, 18:19
x3tc

Post by ScRaT_GER »

Thanks for the info! This is going to be really valuable for later projects I'm planning.
This bit of code belongs to an MD file that allows me to spawn station actors on any number of stations in a sector belonging to any number of races, if the number of stations, races or player's notoriety to these stations/races changes, then everything adapts within a few seconds with appropriate actors being destroyed and new actors generated.
Yes, that's exactly what I needed! It's practically impossible to spawn multiple actors using one cue and handling their conversations.
Because of that, I had to limit myself to one single station - until now... :)
If you can perform your cleanup in the instance itself I will go out on a limb and say that generally it is usually easier to do it locally within the instance itself.
Okay, together with your watcher cue, the cue control should work fine. Thanks for the insights.

BTW: I noticed that you name your cues with dots everywhere, like "LIFE.GNS.StationHandler". Couldn't that be problem for the MD when it tries to find names. For example LIFE.GNS.StationHandler could either reference the "cue-local" variable "StationHandler" in the cue "LIFE.GNS" or it could reference the cue-local variable "GNS.StationHandler" in the cue "LIFE". Or am I missing something here?

Greetings,
ScRaT

PS: I'm really looking forward to your LIFE project - looks very promising!
Shush
Posts: 244
Joined: Sat, 6. Dec 03, 16:21
x4

Post by Shush »

BTW: I noticed that you name your cues with dots everywhere, like "LIFE.GNS.StationHandler". Couldn't that be problem for the MD when it tries to find names. For example LIFE.GNS.StationHandler could either reference the "cue-local" variable "StationHandler" in the cue "LIFE.GNS" or it could reference the cue-local variable "GNS.StationHandler" in the cue "LIFE". Or am I missing something here?
It shouldn't be a problem as all my cue names are unique, here is my MD file that handles the news for LIFE across multiple stations in a sector, you'll see what I mean about the naming and you may get some use out of the rest of the code :)

Code: Select all

<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml-stylesheet href="director.xsl" type="text/xsl" ?>

<director name="test" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="director.xsd">

  
  <documentation>
    <author name="Ari Tsironis" alias="Shush" contact="Shush at forum.egosoft.com"/>
    <content reference="LIFE.GNS" name="LIFE.GNS" description="Galactic News System launcher for LIFE"/>
    <version number="0.0" date="today" status="testing"/>
  </documentation>
  
  
  <cues>
    <!-- Galactic News System launcher, check MCSI<->MD flag for 666, (START GNS) -->
    <cue name="LIFE.GNS">
      <condition>
        <check_all>
          <check_age value="{player.age}" min="5s"/>
          <check_value value="{player.notoriety.other2}" exact="666"/>
        </check_all>
      </condition>

      <action>
        <do_all>
          <!--incoming_message author="LIFE.GNS" text="Initialising:"/-->

          <!-- Global instance count -->
          <set_value name="LIFE.GNS.instancesCount" exact="0"/>

          <!-- Global cancel instances flag -->
          <set_value name="LIFE.GNS.cancelInstances" exact="0"/>

          <!-- Save player's notoriety with all races -->
          <set_value name="LIFE.GNS.argonNotoriety"   exact="{player.notoriety.argon}"/>
          <set_value name="LIFE.GNS.boronNotoriety"   exact="{player.notoriety.boron}"/>
          <set_value name="LIFE.GNS.splitNotoriety"   exact="{player.notoriety.split}"/>
          <set_value name="LIFE.GNS.paranidNotoriety" exact="{player.notoriety.paranid}"/>
          <set_value name="LIFE.GNS.teladiNotoriety"  exact="{player.notoriety.teladi}"/>
          <set_value name="LIFE.GNS.pirateNotoriety"  exact="{player.notoriety.pirate}"/>
          <set_value name="LIFE.GNS.gonerNotoriety"   exact="{player.notoriety.goner}"/>
          <set_value name="LIFE.GNS.other1Notoriety"  exact="{player.notoriety.other1}"/>
          <set_value name="LIFE.GNS.atfNotoriety"     exact="{player.notoriety.atf}"/>
          <set_value name="LIFE.GNS.terranNotoriety"  exact="{player.notoriety.terran}"/>
          <set_value name="LIFE.GNS.yakiNotoriety"    exact="{player.notoriety.yaki}"/>

          <!-- Build a list of candidate stations -->
          <find_station group="this.stations" multiple="1" nearest="1" max="10" findobject="{player.ship}" dockingallowed="1" typename="SS_DOCK_TR_MILITARY_3|SS_DOCK_TR_MILITARY_2|SS_DOCK_TR_MILITARY_1|SS_DOCK_T_TRADE|
SS_DOCK_S_TRADE|SS_DOCK_P_TRADE|SS_DOCK_A_TRADE|SS_DOCK_B_TRADE|SS_DOCK_GONER|SS_DOCK_GONER_NEW|SS_DOCK_PIRATES"><jumps exact="0"/></find_station>

          <!-- Nothing in our list, so find any station we can dock at -->
          <do_if value="{group.object.count@this.stations}" exact="0">
            <find_station group="this.stations" nearest="1" max="1" findobject="{player.ship}" dockingallowed="1" enemy="0"><jumps exact="0"/></find_station>
          </do_if>

          <!-- Global no stations flag -->
          <set_value name="LIFE.GNS.noStations" exact="0"/>
          <do_if value="{group.object.count@this.stations}" exact="0"><set_value name="LIFE.GNS.noStations" exact="1"/></do_if>

          <!--play_subtitles duration="5000" text="GNS: {object.name@{group.object.1@this.stations}} {group.object.count@this.stations}"/-->
        </do_all>  
      </action>


      <cues>
        <!-- No dockable stations, so restart LIFE.GNS when player leaves the sector -->
        <cue name="LIFE.GNS.NoStations">
          <condition>
            <check_all>            
              <object_changed_sector/>
              <check_value value="{value@LIFE.GNS.noStations}" exact="1"/>
            </check_all>
          </condition>
          
          <action>
            <do_all>
              <!--incoming_message author="LIFE.GNS.NoStations" text="Instance Count: {instances.count@{LIFE.GNS.StationHandler}}"/-->
              <reset_cue cue="LIFE.GNS"/>
            </do_all>
          </action>
        </cue>


        <!-- Restart LIFE.GNS when player's notoriety changes with any race -->
        <cue name="LIFE.GNS.PlayerNotoriety" delay="1s">
          <condition>
            <check_any>
              <check_value negate="1" value="{value@LIFE.GNS.argonNotoriety}"   exact="{player.notoriety.argon}"/>
              <check_value negate="1" value="{value@LIFE.GNS.boronNotoriety}"   exact="{player.notoriety.boron}"/>
              <check_value negate="1" value="{value@LIFE.GNS.splitNotoriety}"   exact="{player.notoriety.split}"/>
              <check_value negate="1" value="{value@LIFE.GNS.paranidNotoriety}" exact="{player.notoriety.paranid}"/>
              <check_value negate="1" value="{value@LIFE.GNS.teladiNotoriety}"  exact="{player.notoriety.teladi}"/>
              <check_value negate="1" value="{value@LIFE.GNS.pirateNotoriety}"  exact="{player.notoriety.pirate}"/>
              <check_value negate="1" value="{value@LIFE.GNS.gonerNotoriety}"   exact="{player.notoriety.goner}"/>
              <check_value negate="1" value="{value@LIFE.GNS.other1Notoriety}"  exact="{player.notoriety.other1}"/>
              <check_value negate="1" value="{value@LIFE.GNS.atfNotoriety}"     exact="{player.notoriety.atf}"/>     
              <check_value negate="1" value="{value@LIFE.GNS.terranNotoriety}"  exact="{player.notoriety.terran}"/>
              <check_value negate="1" value="{value@LIFE.GNS.yakiNotoriety}"    exact="{player.notoriety.yaki}"/>   
            </check_any>
          </condition>
          
          <action>
            <do_all>
              <!--incoming_message author="LIFE.GNS.PlayerNotoriety" text="Instance Count: {instances.count@{LIFE.GNS.StationHandler}}"/-->
              
              <!-- If there are no instances of LIFE.GNS.StationHandler then restart LIFE.GNS -->
              <do_if value="{instances.count@{LIFE.GNS.StationHandler}}" exact="0"><reset_cue cue="LIFE.GNS"/></do_if>

              <!-- If there are instances of LIFE.GNS.StationHandler then use the global LIFE.GNS.cancelInstances to restart LIFE.GNS -->
              <do_if value="{instances.count@{LIFE.GNS.StationHandler}}" min="1"><set_value name="LIFE.GNS.cancelInstances" exact="1"/></do_if>
            </do_all>
          </action>
        </cue>


        <!-- Check if instances count has changed -->
        <cue name="LIFE.GNS.Restart" delay="1s">
          <condition>
            <check_value negate="1" value="{instances.count@{LIFE.GNS.StationHandler}}" exact="{value@LIFE.GNS.instancesCount}"/>
          </condition>

          <action>
            <do_all>
              <!-- If instances count = 0 then restart LIFE.GNS -->
              <do_if value="{instances.count@{LIFE.GNS.StationHandler}}" exact="0">
                <!--incoming_message author="LIFE.GNS.Restart" text="Instance Count: {instances.count@{LIFE.GNS.StationHandler}}"/-->
                <reset_cue cue="LIFE.GNS"/>
              </do_if>

              <!-- If instances count has decreased, then cancel all instances -->
              <do_if value="{instances.count@{LIFE.GNS.StationHandler}}" max="{value@LIFE.GNS.instancesCount}-1">
                <set_value name="LIFE.GNS.cancelInstances" exact="1"/>
              </do_if>

              <!-- Save instances count -->
              <set_value name="LIFE.GNS.instancesCount" exact="{instances.count@{LIFE.GNS.StationHandler}}"/>
              <reset_cue cue="LIFE.GNS.Restart"/>
            </do_all>
          </action>
        </cue>


        <!-- Found a station -->
        <cue name="LIFE.GNS.StationHandler" instantiate="static" delay="1s">
          <condition>
            <check_value value="{group.object.count@parent.stations}" min="1"/>  
          </condition>

          <action>
            <do_all>
              <!-- Save station object and remove it from group so that we don't have runaway instance creation -->
              <set_object name="this.station" value="{group.object.1@parent.stations}"/>
              <remove_object_from_group object="{group.object.1@parent.stations}" group="parent.stations"/>

              <!-- Save station owner and players notoriety with station owner -->
              <set_value name="this.owner" exact="{object.race@this.station}"/>
              <set_value name="this.notoriety" exact="{player.notoriety.{object.race@this.station}}"/>

              <!--set_value name="this.instanceID" exact="{instance@LIFE.GNS.StationHandler}"/>
              <set_value name="this.instancesCount" exact="{instances.count@{static@LIFE.GNS.StationHandler}}"/>
              <incoming_message author="{object.name@this.station}" text="StationHandler: {lookup.race.name@{value@this.owner}} {value@this.notoriety} {value@this.instanceID} {value@this.instancesCount}"/-->
              <!--play_subtitles duration="5000" text="StationHandler: {object.name@this.station} {lookup.race.name@{value@this.owner}} {value@this.notoriety} {value@this.instanceID} {value@this.instancesCount}"/-->

              <!-- Create GNS Representative -->
              <do_if value="{value@this.owner}" exact="{lookup.race@argon}">
                <create_actor name="this.actor" location="crew" race="{value@this.owner}" character=".I.B.C. Representative" object="this.station" voiceflags="32"/>
                <create_offer actor="this.actor" conversation="LIFE.GNS.conversation.argon" cue="LIFE.GNS.StationHandler" discipline="XXXT"/>
              </do_if>
              <do_if value="{value@this.owner}" exact="{lookup.race@boron}">
                <create_actor name="this.actor" location="crew" race="{value@this.owner}" character=".R.C.N. Representative" object="this.station" voiceflags="32"/>
                <create_offer actor="this.actor" conversation="LIFE.GNS.conversation.boron" cue="LIFE.GNS.StationHandler" discipline="XXXT"/>
              </do_if>
              <do_if value="{value@this.owner}" exact="{lookup.race@split}">
                <create_actor name="this.actor" location="crew" race="{value@this.owner}" character=".S.F.N. Representative" object="this.station" voiceflags="32"/>
                <create_offer actor="this.actor" conversation="LIFE.GNS.conversation.split" cue="LIFE.GNS.StationHandler" discipline="XXXT"/>
              </do_if>
              <do_if value="{value@this.owner}" exact="{lookup.race@paranid}">
                <create_actor name="this.actor" location="crew" race="{value@this.owner}" character=".I.P.N. Representative" object="this.station" voiceflags="32"/>
                <create_offer actor="this.actor" conversation="LIFE.GNS.conversation.paranid" cue="LIFE.GNS.StationHandler" discipline="XXXT"/>
              </do_if>
              <do_if value="{value@this.owner}" exact="{lookup.race@teladi}">
                <create_actor name="this.actor" location="crew" race="{value@this.owner}" character=".C.B.N.N. Representative" object="this.station" voiceflags="32"/>
                <create_offer actor="this.actor" conversation="LIFE.GNS.conversation.teladi" cue="LIFE.GNS.StationHandler" discipline="XXXT"/>
              </do_if>
              <do_if value="{value@this.owner}" exact="{lookup.race@pirate}">
                <create_actor name="this.actor" location="crew" race="{value@this.owner}" character=".M.I.N. Representative" object="this.station" voiceflags="32"/>
                <create_offer actor="this.actor" conversation="LIFE.GNS.conversation.pirate" cue="LIFE.GNS.StationHandler" discipline="XXXT"/>
              </do_if>
              <do_if value="{value@this.owner}" exact="{lookup.race@terran}">
                <create_actor name="this.actor" location="crew" race="{value@this.owner}" character=".S.S.N. Representative" object="this.station" voiceflags="32"/>
                <create_offer actor="this.actor" conversation="LIFE.GNS.conversation.terran" cue="LIFE.GNS.StationHandler" discipline="XXXT"/>
              </do_if>
              <do_if value="{value@this.owner}" exact="{lookup.race@yaki}">
                <create_actor name="this.actor" location="crew" race="{value@this.owner}" character=".Y.C.N. Representative" object="this.station" voiceflags="32"/>
                <create_offer actor="this.actor" conversation="LIFE.GNS.conversation.yaki" cue="LIFE.GNS.StationHandler" discipline="XXXT"/>
              </do_if>
            </do_all>
          </action>

          
          <cues>
            <!-- Main Event handler -->
            <cue name="LIFE.GNS.EventHandler">
              <cues>
                <!-- Check player changed sector, station destroyed, MCSI<->MD flag 667 (STOP GNS), global cancel instances flag -->
                <cue name="LIFE.GNS.CancelInstance">
                  <condition>
                    <check_any>
                      <object_changed_sector/>
                      <object_destroyed object="parent.parent.station"/>
                      <object_destroyed_by_player object="parent.parent.station"/>
                      <check_value value="{player.notoriety.other2}" exact="667"/>
                      <check_value value="{value@LIFE.GNS.cancelInstances}" exact="1"/>
                    </check_any>
                  </condition>

                  <action>
                    <do_all>
                      <!--set_value name="this.instanceID" exact="{instance@LIFE.GNS.StationHandler}"/>
                      <set_value name="this.instancesCount" exact="{instances.count@{static@LIFE.GNS.StationHandler}}"/>
                      <incoming_message author="{actor.name@parent.parent.actor}" text="CancelInstance: {value@this.instanceID} {value@this.instancesCount}"/-->
                      <!--play_subtitles duration="5000" text="CancelInstance: {actor.name@parent.parent.actor} {value@this.instanceID} {value@this.instancesCount}"/-->

                      <!-- If GNS Representative exists, remove him and his conversation -->
                      <do_if value="{actor.exists@parent.parent.actor}" exact="1">
                        <do_if value="{value@parent.parent.owner}" exact="{lookup.race@argon}"><remove_offer actor="parent.parent.actor" conversation="LIFE.GNS.conversation.argon" discipline="XXXT"/></do_if>
                        <do_if value="{value@parent.parent.owner}" exact="{lookup.race@boron}"><remove_offer actor="parent.parent.actor" conversation="LIFE.GNS.conversation.boron" discipline="XXXT"/></do_if>
                        <do_if value="{value@parent.parent.owner}" exact="{lookup.race@split}"><remove_offer actor="parent.parent.actor" conversation="LIFE.GNS.conversation.split" discipline="XXXT"/></do_if>
                        <do_if value="{value@parent.parent.owner}" exact="{lookup.race@paranid}"><remove_offer actor="parent.parent.actor" conversation="LIFE.GNS.conversation.paranid" discipline="XXXT"/></do_if>
                        <do_if value="{value@parent.parent.owner}" exact="{lookup.race@teladi}"><remove_offer actor="parent.parent.actor" conversation="LIFE.GNS.conversation.teladi" discipline="XXXT"/></do_if>
                        <do_if value="{value@parent.parent.owner}" exact="{lookup.race@pirate}"><remove_offer actor="parent.parent.actor" conversation="LIFE.GNS.conversation.pirate" discipline="XXXT"/></do_if>
                        <do_if value="{value@parent.parent.owner}" exact="{lookup.race@terran}"><remove_offer actor="parent.parent.actor" conversation="LIFE.GNS.conversation.terran" discipline="XXXT"/></do_if>
                        <do_if value="{value@parent.parent.owner}" exact="{lookup.race@yaki}"><remove_offer actor="parent.parent.actor" conversation="LIFE.GNS.conversation.yaki" discipline="XXXT"/></do_if>
                        <clear_actor_location actor="parent.parent.actor"/>
                        <destroy_actor actor="parent.parent.actor"/>
                      </do_if>

                      <!-- Cancel instance -->
                      <cancel_cue cue="LIFE.GNS.StationHandler"/>
                    </do_all>
                  </action>
                </cue>

                
                <!-- Process conversation -->
                <cue name="LIFE.GNS.Conversation">
                  <condition>
                    <check_any>
                      <check_all>
                        <check_value value="{value@parent.parent.owner}" exact="{lookup.race@argon}"/>
                        <conversation_completed conversation="LIFE.GNS.conversation.argon" actor="parent.parent.actor"/>
                      </check_all>
                      <check_all>
                        <check_value value="{value@parent.parent.owner}" exact="{lookup.race@boron}"/>
                        <conversation_completed conversation="LIFE.GNS.conversation.boron" actor="parent.parent.actor"/>
                      </check_all>
                      <check_all>
                        <check_value value="{value@parent.parent.owner}" exact="{lookup.race@split}"/>
                        <conversation_completed conversation="LIFE.GNS.conversation.split" actor="parent.parent.actor"/>
                      </check_all>
                      <check_all>
                        <check_value value="{value@parent.parent.owner}" exact="{lookup.race@paranid}"/>
                        <conversation_completed conversation="LIFE.GNS.conversation.paranid" actor="parent.parent.actor"/>
                      </check_all>
                      <check_all>
                        <check_value value="{value@parent.parent.owner}" exact="{lookup.race@teladi}"/>
                        <conversation_completed conversation="LIFE.GNS.conversation.teladi" actor="parent.parent.actor"/>
                      </check_all>
                      <check_all>
                        <check_value value="{value@parent.parent.owner}" exact="{lookup.race@pirate}"/>
                        <conversation_completed conversation="LIFE.GNS.conversation.pirate" actor="parent.parent.actor"/>
                      </check_all>
                      <check_all>
                        <check_value value="{value@parent.parent.owner}" exact="{lookup.race@terran}"/>
                        <conversation_completed conversation="LIFE.GNS.conversation.terran" actor="parent.parent.actor"/>
                      </check_all>
                      <check_all>
                        <check_value value="{value@parent.parent.owner}" exact="{lookup.race@yaki}"/>
                        <conversation_completed conversation="LIFE.GNS.conversation.yaki" actor="parent.parent.actor"/>
                      </check_all>
                    </check_any>  
                  </condition>

                  <!--action>
                    <do_all>
                      <set_value name="this.instanceID" exact="{instance@LIFE.GNS.StationHandler}"/>
                      <set_value name="this.instancesCount" exact="{instances.count@{static@LIFE.GNS.StationHandler}}"/>
                      <incoming_message author="{actor.name@parent.parent.actor}" text="Conversation: {lookup.race.name@{value@parent.parent.owner}} {value@this.instanceID} {value@this.instancesCount}"/>
                      <play_subtitles duration="5000" text="Conversation: {actor.name@parent.parent.actor} {lookup.race.name@{value@parent.parent.owner}} {value@this.instanceID} {value@this.instancesCount}"/>
                    </do_all>
                  </action-->

                  <cues>
                    <!-- Wait for dialog menu to close, run news script, restart event handler -->
                    <cue name="LIFE.GNS.NewsScript">
                      <condition>
                        <check_value value="{player.menu.dialog}" exact="0"/>
                      </condition>

                      <action>
                        <!-- GNS needs to know station owner's race -->
                        <do_all>
                          <run_script script="plugin.LIFE.generate.news" task="1">
                            <scriptargs>
                              <scriptvalue datatype="integer" datavalue="{value@parent.parent.parent.owner}"/>
                            </scriptargs>
                          </run_script>

                          <reset_cue cue="LIFE.GNS.EventHandler"/>
                        </do_all>
                      </action>
                    </cue>
                  </cues>

                </cue>
              </cues>

            </cue>
          </cues>

        </cue>
      </cues>
      
    </cue>
  </cues>

</director>
User avatar
ScRaT_GER
Posts: 1962
Joined: Tue, 8. Jan 08, 18:19
x3tc

Post by ScRaT_GER »

Impressive.
you'll see what I mean about the naming
Yes, they are unique. Still, I think for my personal use, I'll stick to a spacing underscore _ because it clearly seperates cue name from local variables.

I have one question to the code. Atm there are quite a lot duplications, because of all the do_if statements to distinguish between the races. E.g. here:

Code: Select all

<do_if value="{value@this.owner}" exact="{lookup.race@argon}"> 
  <create_actor name="this.actor" location="crew" race="{value@this.owner}" character=".I.B.C. Representative" object="this.station" voiceflags="32"/> 
  <create_offer actor="this.actor" conversation="LIFE.GNS.conversation.argon" cue="LIFE.GNS.StationHandler" discipline="XXXT"/> 
</do_if>
Should the following code do the same, but for all races:

Code: Select all

<create_actor name="this.actor" location="crew" race="{value@this.owner}" character=".I.B.C. Representative" object="this.station" voiceflags="32"/> 
<create_offer actor="this.actor" conversation="LIFE.GNS.conversation.{object.race.name@this.owner}" cue="LIFE.GNS.StationHandler" discipline="XXXT"/> 
If this worked, it would cut down your code to about a half, I guess. :)

Greetings,
ScRaT
Shush
Posts: 244
Joined: Sat, 6. Dec 03, 16:21
x4

Post by Shush »

Should the following code do the same, but for all races:

Code: Select all

<create_actor name="this.actor" location="crew" race="{value@this.owner}" character=".I.B.C. Representative" object="this.station" voiceflags="32"/> 
<create_offer actor="this.actor" conversation="LIFE.GNS.conversation.{object.race.name@this.owner}" cue="LIFE.GNS.StationHandler" discipline="XXXT"/> 



If this worked, it would cut down your code to about a half, I guess.
Wouldn't it need to be more like?

Code: Select all

conversation="LIFE.GNS.conversation.{object.race.name@{value@this.owner}}"
Anyway the point is moot, neither method works :( I used to have a much simpler method that used 1 conversation and 1 character="blah" text string but I wanted race variety in the conversation and the actor's name.

One of the things I find difficult to work with in director is the lack of expression handling. What you suggested should be completely feasible, but it isn't; it isn't handled in a generic way at all, but rather through extra commands like set_object and set_actor, that allow you to change command attributes from expressions on a case by case basis.

Also the string/text handling within MD is fundamentally missing compared to other intrinsic types, the reasoning being I would guess is that it's all supposed to be handled by text files for locale :/

So the effect of all is this is that in the end you get a few set_ commands that work with expressions and a whole bunch of other command attributes that you can do nothing with but fill with constants inside do_if or do_choose blocks like I have had to do.
User avatar
ScRaT_GER
Posts: 1962
Joined: Tue, 8. Jan 08, 18:19
x3tc

Post by ScRaT_GER »

Wouldn't it need to be more like?
Oh, right.
Anyway the point is moot, neither method works
:( Well, it would have been too easy.
Maybe something like this works:

Code: Select all

<set_value name="this.conversation" value="LIFE.GNS.conversation.{object.race.name@{value@this.owner}}"/>

... And then ...
conversation="LIFE.GNS.conversation.{value@this.conversation}"
But I guess this doesn't work either.

Btw: I noticed that you can use {instance@this} to get the instance id of the current instantiation. So instead of writing

Code: Select all

<set_value name="this.instanceID" exact="{instance@LIFE.GNS.StationHandler}"/>
you could also write:

Code: Select all

<set_value name="this.instanceID" exact="{instance@this}"/>
To me, this seems a bit "safer". Before I somehow doubted that using the cue name of the cue would refer to the correct instantiation (which it does, but I wanted to point out another way of doing it).
Startpost wrote: Supposedly object="" also defaults to {player.ship} but being verbose is far easier to read and debug.
I thought that, too, but when I used the following code it wouldn't fire when specifying the reference object explicitly:

Code: Select all

<condition>
  <!--object_changed_sector object="{player.ship}"/ DOESN'T WORK-->
  <object_changed_sector/> <!--WORKS-->
</condition>
Did I do something wrong?

Greetings,
ScRaT
Shush
Posts: 244
Joined: Sat, 6. Dec 03, 16:21
x4

Post by Shush »

Btw: I noticed that you can use {instance@this} to get the instance id of the current instantiation. So instead of writing

Code: Select all

<set_value name="this.instanceID" exact="{instance@LIFE.GNS.StationHandler}"/>
you could also write:

Code: Select all

<set_value name="this.instanceID" exact="{instance@this}"/>
To me, this seems a bit "safer". Before I somehow doubted that using the cue name of the cue would refer to the correct instantiation (which it does, but I wanted to point out another way of doing it).
Yup absoloutely it is safer, good pickup.
I thought that, too, but when I used the following code it wouldn't fire when specifying the reference object explicitly:

Code: Select all

<condition> 
  <!--object_changed_sector object="{player.ship}"/ DOESN'T WORK--> 
  <object_changed_sector/> <!--WORKS--> 
</condition>
Did I do something wrong?
Nope you did nothing wrong, {player.ship} just doesn't work for that event, there may also be others it doesn't work for but that is the only one I have come across. Obviously as long as we have a way of referencing {player.ship} through leaving object unassigned, then it doesn't really matter apart from readability and potential confusion :/
User avatar
Gazz
Posts: 13244
Joined: Fri, 13. Jan 06, 16:39
x4

Post by Gazz »

Code: Select all

<!--object_changed_sector object="{player.ship}"/ DOESN'T WORK--> 
I bet that this is related to how the object changes sector.

Try having the Playership change sector while under autopilot control.
Do not disable the AP until the ship completes the "flying away rom gate" routine in the new sector.

For AI ships, that is the point when SIGNAL_CHANGE_SECTOR fires.
My complete script download page. . . . . . I AM THE LAW!
There is no sense crying over every mistake. You just keep on trying till you run out of cake.

Return to “X³: Terran Conflict / Albion Prelude - Scripts and Modding”