[TOOL][WIP] Script Editor X2S + DIFF Creator

The place to discuss scripting and game modifications for X4: Foundations.

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

EterniaLogic
Posts: 26
Joined: Mon, 20. May 13, 17:49
x4

[TOOL][WIP] Script Editor X2S + DIFF Creator

Post by EterniaLogic »

I am working on a Script converter because Xml scripting was just too hardcore for me. This script converter (kind of) uses the base syntax of javascript, but is modified to enable the code to work with X4's XML files.

Currently only converts AIScripts from XML to script.


Github: (full open-source)
https://github.com/EterniaLogic/X4-XML2Script-Editor

Java Jar Downoad: (Version: 0.4)
https://drive.google.com/open?id=1RswAV ... nlNSzGpKIB (works with aiscripts, to use, just rightclick->JDK 1.8 )
  • Upon opening, just go File->Open ... '?????/aiscripts/???.xml'
    Upon saving, a secondary file will be created called 'savelocation/???.xml.script' that can also be opened and saved into xml with.
TODO:
  • auto-creation of XML DIFFs for mods. (Tools -> DIFF Creator Gui)
  • validate with the .xsd file
  • support MD XML scripting (converts part of it)
  • support libraries to make adding gamedata easier. (will not be script-like, not sure on the format it would take yet)
  • support .xsd files to edit/add and reference XML types easily. (will not be script-like, not sure on the format it would take yet)
  • intelligent dropdowns referencing with the .xsd file.
  • fully debug different varieties of aiscript and md scripts.
DONE:
  • GUI with Syntax Highlighting
  • Working with aiscript files
  • AI Script -> XML (99% debugged)
  • XML -> AI Script (99% debugged)

Input: (Converted from original XML)

Code: Select all

aiscript boarding.pod.return{ // {name="boarding.pod.return", xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance", xsi:noNamespaceSchemaLocation="aiscripts.xsd"}
    param target();
    param initialwaittime();
    param debugoutputchance("0"); // {default}

    #interrupt_handler_ref(TargetInvalidHandler);

    function attention(visible){
        if(not $target.exists){
            debug_text("'Error: target is not valid [Owen]'"); // {text}
            return();
        }
        if($target != player.occupiedship){
            debug_text("'Error: non-player occupied ship case not currently supported [Owen]'"); // {text}
            return();
        }
        if($target.dockslot){
            debug_text("$debugoutputchance", "'Target is docked. Immediatly return.'"); // {chance, text}
        }
        wait("$initialwaittime"); // {exact}
        
        // TODO @Owen detatch_object action - warp is 1 frame delayed. restore=""
        if(not this.ship.parent.isclass.zone){
            debug_text("$debugoutputchance", "'Disengaging from ' + this.ship.parent + ' ' + this.ship.parent.knownname"); // {chance, text}
            
            // TODO @Owen improve movement so disabling collisions is not needed
            disable_collisions_between("this.ship", "this.ship.parent"); // {object, target}
            warp("this.ship", "this.zone"){ // {object, zone}
                position("this.ship"); // {object}
            }
            wait("1ms"); // {exact}
            debug_text("$debugoutputchance", "'Now a child of ' + this.ship.parent + ' ' + this.ship.parent.knownname"); // {chance, text}
        }
        
        /* Initial movement to clear boarded object
            a
            b
            c
            d
            e
            f
            g
         */
        move_to("$target", "true", "true", "this.ship", "false"){ // {destination, finishonapproach, forcesteering, object, uselocalhighways}
            interrupt_after_time("3s"); // {time}
        }
        set_avoid_collisions("false", "this.ship", "false"); // {bigobjects, object, smallobjects}
        find_dockingbay("$dock", "$target"){ // {name, object}
            match_dock("this.ship.docksize"); // {size}
        }
        if($dock){
            get_docking_approach_pos("$dock", "$approachpos", "$approachrot", "this.ship"); // {dock, position, rotation, ship}
            move_to("false", "$dock.component", "true", "flightbehaviour.boardingpod", "this.ship", "false"){ // {avoid, destination, finishonapproach, flightbehaviour, object, uselocalhighways}
                position("$approachpos"); // {value}
                rotation("$approachrot"); // {value}
                interrupt_after_time("30s"); // {time}
            }
            debug_text("'move_docking'"); // {text}
            move_docking("$dock", "flightbehaviour.dock_highspeed", "this.ship"); // {dock, flightbehaviour, object}
            debug_text("'docked'"); // {text}
        }else{
            
            // TODO @Owen
        }
        
        // Fire a signal the MD Boarding script is waiting for
        signal_objects("this.ship", "'boarding.pod'", "'returned'"); // {object, param, param2}
        wait("1ms"); // {exact}
        destroy_object("false", "this.ship"); // {explosion, object}
    }

    function attention(unknown){
        
        //  cleanup 
        destroy_object("false", "this.ship"); // {explosion, object}
    }


}
            
Output XML: (doubly converted text!)

Code: Select all

<?xml version="1.0" encoding="utf-8"?>
<aiscript name="boarding.pod.return" xsi:noNamespaceSchemaLocation="aiscripts.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <params>
        <param name="target" />
        <param name="initialwaittime" />
        <param name="debugoutputchance" default="0" />
    </params>
    <interrupts>
        <handler ref="TargetInvalidHandler" />
    </interrupts>
    <attention min="visible">
        <actions>
            <do_if value="not $target.exists">
                <debug_text text="'Error: target is not valid [Owen]'" />
                <return />
            </do_if>
            <do_if value="$target != player.occupiedship">
                <debug_text text="'Error: non-player occupied ship case not currently supported [Owen]'" />
                <return />
            </do_if>
            <do_if value="$target.dockslot">
                <debug_text chance="$debugoutputchance" text="'Target is docked. Immediatly return.'" />
            </do_if>
            <wait exact="$initialwaittime" />
            <!-- TODO @Owen detatch_object action - warp is 1 frame delayed. restore=\"\"-->
            <do_if value="not this.ship.parent.isclass.zone">
                <debug_text chance="$debugoutputchance" text="'Disengaging from ' + this.ship.parent + ' ' + this.ship.parent.knownname" />
                <!-- TODO @Owen improve movement so disabling collisions is not needed-->
                <disable_collisions_between object="this.ship" target="this.ship.parent" />
                <warp object="this.ship" zone="this.zone">
                    <position object="this.ship" />
                </warp>
                <wait exact="1ms" />
                <debug_text chance="$debugoutputchance" text="'Now a child of ' + this.ship.parent + ' ' + this.ship.parent.knownname" />
            </do_if>
            <!-- /* Initial movement to clear boarded object
a
b
c
d
e
f
g-->
            <move_to object="this.ship" uselocalhighways="false" destination="$target" finishonapproach="true" forcesteering="true">
                <interrupt_after_time time="3s" />
            </move_to>
            <set_avoid_collisions object="this.ship" bigobjects="false" smallobjects="false" />
            <find_dockingbay name="$dock" object="$target">
                <match_dock size="this.ship.docksize" />
            </find_dockingbay>
            <do_if value="$dock">
                <get_docking_approach_pos dock="$dock" rotation="$approachrot" ship="this.ship" position="$approachpos" />
                <move_to object="this.ship" flightbehaviour="flightbehaviour.boardingpod" uselocalhighways="false" destination="$dock.component" finishonapproach="true" avoid="false">
                    <position value="$approachpos" />
                    <rotation value="$approachrot" />
                    <interrupt_after_time time="30s" />
                </move_to>
                <debug_text text="'move_docking'" />
                <move_docking object="this.ship" dock="$dock" flightbehaviour="flightbehaviour.dock_highspeed" />
                <debug_text text="'docked'" />
            </do_if>
            <do_else>
                <!-- TODO @Owen-->
            </do_else>
            <!-- Fire a signal the MD Boarding script is waiting for-->
            <signal_objects object="this.ship" param="'boarding.pod'" param2="'returned'" />
            <wait exact="1ms" />
            <destroy_object object="this.ship" explosion="false" />
        </actions>
    </attention>
    <attention min="unknown">
        <actions>
            <!-- cleanup-->
            <destroy_object object="this.ship" explosion="false" />
        </actions>
    </attention>
</aiscript> 
GUI:
Image


DIFF Creator Gui: (CONCEPT!!!)
Image
Last edited by EterniaLogic on Tue, 11. Dec 18, 04:28, edited 33 times in total.
Because I can, I'm here to disturb your mind.
SirNukes
Posts: 549
Joined: Sat, 31. Mar 07, 23:44
x4

Re: [TOOL][WIP] X4-Xml2Script Editor (XML -> Script -> XML)

Post by SirNukes »

Oh neat, someone is doing it. I toyed around with going to Python last spring: viewtopic.php?f=146&t=398691&p=4709325. The problem I ran into was the conflict between having to deal so much with the xml to make a tool to avoid the xml. It will be interesting to see how your tool turns out.
Teleth
Posts: 292
Joined: Sat, 31. Mar 12, 06:39

Re: [TOOL][WIP] X4-Xml2Script Editor (XML -> Script -> XML)

Post by Teleth »

Nice work, scripting in XML is a complete and utter nightmare, this may go a long way to being approachable and a great deal less time consuming.
EterniaLogic
Posts: 26
Joined: Mon, 20. May 13, 17:49
x4

Re: [TOOL][WIP] X4-Xml2Script Editor (XML -> Script -> XML)

Post by EterniaLogic »

Well, everybody wants it, so I am putting it up on github soon.

Only problem that I've had so far is that java likes to alphabetically order parameters.
Because I can, I'm here to disturb your mind.
ceecee
Posts: 13
Joined: Tue, 9. Sep 14, 06:13
x4

Re: [TOOL][WIP] X4-Xml2Script Editor (XML -> Script -> XML)

Post by ceecee »

That's great stuff man. Editing XML is easy enough when its just a list of attributes describing an object, but doing actual logic scripting with it is just straight into the ha ha kill me zone.
MutantDwarf
Posts: 716
Joined: Tue, 20. Jun 06, 02:29
x4

Re: [TOOL][WIP] X4-Xml2Script Editor (XML -> Script -> XML)

Post by MutantDwarf »

The immediate problem I see with this is how you're going to be able to tell what parameter is what. If a function in the XML source can only take a single parameter, then fine - that's easy and works. But if a function can take multiple parameters, and several of them are optional, then in the XML source they can be in any order, while in the javascript-like code they have to appear in a fixed order. Means some things that you can do in the XML you can't translate to one-to-one in the javascript and then back to the XML source.

I'd recommend changing how you're doing that significantly. Either make it so the parameters can be called out by name like in the XML source or make it so it doesn't just turn it into basic text but instead a sort of metadata-enabled editor.
Langy the Mutant Dwarf
EterniaLogic
Posts: 26
Joined: Mon, 20. May 13, 17:49
x4

Re: [TOOL][WIP] X4-Xml2Script Editor (XML -> Script -> XML)

Post by EterniaLogic »

They go into comments. Only way I saw that it would be reverse compatible.

Github up ~woo.

I originally had it like:

Code: Select all

function(name=value);
but that got clunky really fast.

Image
Because I can, I'm here to disturb your mind.
peanoot
Posts: 4
Joined: Mon, 3. Dec 18, 06:42
xr

Re: [TOOL][WIP] X4-Xml2Script Editor (XML -> Script -> XML)

Post by peanoot »

This looks great! It's gonna make dealing with XML scripts so much less of a pain.
Assailer
Posts: 478
Joined: Sun, 25. May 14, 17:45
x4

Re: [TOOL][WIP] X4-Xml2Script Editor (XML -> Script -> XML)

Post by Assailer »

Nice! +10!
Alewx
Posts: 303
Joined: Sun, 23. Mar 03, 11:09
x4

Re: [TOOL][WIP] X4-Xml2Script Editor (XML -> Script -> XML)

Post by Alewx »

OMG this is really soooooo needed. never liked the MD system and wanted back the script editor back in X3.
+11 for this.
I7 9900k 4GHz
RTX 3080TI
32GB Corsair
Win10 64bit

But now on a freaking fast SSD aber immernoch ziemlich lahm beim laden :(
User avatar
Meiyo
Posts: 42
Joined: Wed, 20. Nov 13, 23:47
x4

Re: [TOOL][WIP] X4-Xml2Script Editor (XML -> Script -> XML)

Post by Meiyo »

very good thing!
XTC0R
Posts: 401
Joined: Sat, 1. Dec 18, 19:58
x4

Re: [TOOL][WIP] X4-Xml2Script Editor (XML -> Script -> XML)

Post by XTC0R »

this will be very helpful! Great work!
EterniaLogic
Posts: 26
Joined: Mon, 20. May 13, 17:49
x4

Re: [TOOL][WIP] X4-Xml2Script Editor (XML -> Script -> XML)

Post by EterniaLogic »

1. Convert XML to a nodal system. (done via java.xml)

Code: Select all

aiscript
	parameters
		parameter a = ?
	interrupts
	....
	...
2. convert XML java nodes to script (done! 500 lines of code over a day)

3. Convert that Script to nodes (done)

4. Convert Nodal Script to XML ... done
Last edited by EterniaLogic on Tue, 11. Dec 18, 03:35, edited 1 time in total.
Because I can, I'm here to disturb your mind.
User avatar
enenra
Posts: 7150
Joined: Fri, 8. Apr 05, 19:09
x4

Re: [TOOL][WIP] X4-Xml2Script Editor (XML -> Script -> XML)

Post by enenra »

Cool stuff! Didn't think it could be done but it looks like it can, awesome!

TBH I'm still a bit skeptical regarding code structure. You've clearly proven that a conversion works, but the MD-like syntax with the subcues etc. also means that you have to structure code in a certain way that may be very un-intuitive in a "normal" syntax. I guess that's still better than working with standard XML code but I feel like it may lead to people trying to structure it in a way it's not supposed to be structured.
EterniaLogic
Posts: 26
Joined: Mon, 20. May 13, 17:49
x4

Re: [TOOL][WIP] X4-Xml2Script Editor (XML -> Script -> XML)

Post by EterniaLogic »

It can only be done for MDs and aiscripts. The libraries/ xml files are probably not adaptable as they are just data scripts.

The code structure is a bit funny, but it lines up pretty well with javascript for <actions>.

These are all previews of aiscripts, MDs use cues, but those cues all seem to act just like <actions> sooo...
Because I can, I'm here to disturb your mind.
EterniaLogic
Posts: 26
Joined: Mon, 20. May 13, 17:49
x4

Re: [TOOL][WIP] X4-Xml2Script Editor (XML -> Script -> XML)

Post by EterniaLogic »

Almost there!

Converted Script to XML. (Last step is to validate XML in the code, true last step is to take diffs of the xml output)

Several debugging issues dealing with parameters and interrupts that need to be fixed, but I'm kind of impressed that this was doable in only 3 days. It aint perfect, but it is on the way there.

Original XML:

Code: Select all

aiscript order.move.recon{ // {name="order.move.recon", xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance", xsi:noNamespaceSchemaLocation="aiscripts.xsd"}
    order("internal", "{1041, 292}", "Recon", "{1041, 291}"){ // {category, description, id, name}
        param object targetspace("Space. Space to reconnoiter", "null", "true", "{1041, 10117}"){ // {comment, default, required, text}
            input_param class = "[class.sector, class.gate, class.highwayentrygate]";
        }
        param length radius("true", "Radius. only used by blindtourist.", "null", "{1041, 10093}"); // {advanced, comment, default, text}
        param time timeout("true", "Duration", "0s", "0s", "{1041, 10034}"){ // {advanced, comment, default, infinitevalue, text}
            input_param min = "0s";
            input_param max = "24h";
            input_param step = "10min";
        }
        param object hq("true", "Headquarters. Station or ship this ship will report back to after doing reconnaissance. Only used by normal recon or police.", "null", "{1041, 10045}"){ // {advanced, comment, default, text}
            input_param class = "[class.station, class.ship]";
            input_param canbecommanderof = "this.ship";
        }
        param internal targetclasses("Target object classes", "[class.ship, class.station]"); // {comment, default}
        param internal targetpurposes("Target object purposes", "[]"); // {comment, default}
        param internal police("Police investigation. Look for disguised criminals or smuggled goods? Otherwise, default behavior is to identify potential threats to our faction in $targetspace.", "false", "{1041, 10084}"); // {comment, default, text}
        param internal exploreupdate("Update known space. Refresh information on known stations in $targetspace.", "false", "{1041, 10137}"); // {comment, default, text}
        param internal resourcescout("Scout for resources. Look for valuable asteroids, collectables, or lockboxes in $targetspace.", "false", "{1041, 10109}"); // {comment, default, text}
        param internal blindtourist("Explore blind with no cheating", "false", "{1041, 10039}"); // {comment, default, text}
        param internal cannotdock("Unable to dock. used for error handling in case of inability to dock", "false", "{1041, 10133}"); // {comment, default, text}
        param bool debugchance("true", "Print debug output", "0", "{1041, 10086}"){ // {advanced, comment, default, text}
            input_param truevalue = "100";
        }
        skill("40"); // {min}
    }


    #interrupt_handler_ref(SectorChangeHandler);
    #interrupt_handler_ref(TargetInvalidHandler);
    #interrupt_handler_ref(AttackHandler);
    #interrupt_handler_ref(MissileLockHandler);
    #interrupt_handler_ref(ScannedHandler);
    #interrupt_handler_ref(InspectedHandler);
    #interrupt_handler_ref(FoundAbandonedHandler);
    #interrupt_handler_ref(ResupplyHandler);
    #interrupt_handler_ref(JobRemoveRequestHandler);
    #interrupt_handler_if(check_any(event_object_attacked(group="$localtargetgroup") && event_object_signalled(check="false", object="this.sector", param="'police'")) && 
                            set_value(exact="if (event.name == 'event_object_attacked') then event.param else event.param2", name="$attacker") && 
                            $police && 
                            this.sector.exists && 
                            $attacker.isoperational && 
                            $attacker.zone.policefaction && 
                            not this.hasrelation.enemy.{$attacker.zone.policefaction} && 
                            $attacker.owner != this.owner && 
                            $attacker.owner != event.param3.owner){
        if(event.name == 'event_object_attacked'){
            debug_text("$debugchance", "'%s %s attacking attacker %s %s of scanned ship in sector %s. interdicted ship cleared.'.[this.ship.knownname, this.ship, $attacker.knownname, $attacker, this.sector.knownname]"); // {chance, text}
        }else{
            debug_text("$debugchance", "'%s %s called to attack %s %s in sector %s. interdicted ship cleared.'.[this.ship.knownname, this.ship, $attacker.knownname, $attacker, this.sector.knownname]"); // {chance, text}
        }
        if(@$localtarget){
            if($localtarget == player.occupiedship and $attacker != player.occupiedship){
                debug_text("$debugchance", "'called to attack. interdicted player ship cleared.'"); // {chance, text}
                
                //  NB: has to stay a conversation. cannot call player.interaction from an interrupt, and this is fairly important. 
                start_conversation("this", "(ship scan result - nothing found).", "Speak_Scan", "11204", "80", "unqueued"); // {actor, comment, conversation, convparam, priority, type}
            }
            signal_objects("$localtarget", "'proceed'", "this.defensible"); // {object, param, param2}
            
            //  cleaned up in order.wait.signal by $localtarget's pilot 
            this.$police_cleared.{$localtarget} = player.age;
        }
        if($attacker.isplayerowned){
            add_relation_boost("1", "5min", "this", "$attacker", "relationchangereason.illegalcargo", "-0.1"); // {decay, delay, object, otherobject, reason, value}
        }else{
            add_relation_boost("1", "5min", "$attacker", "this", "-0.1"); // {decay, delay, object, otherobject, value}
        }
        create_order("'Attack'", "true", "this.ship"){ // {id, immediate, object}
            param primarytarget("$attacker"); // {value}
            param allowothertargets("true"); // {value}
            param debugchance("$debugchance"); // {value}
        }

    };

    function init(){
        if(not $targetspace){
            if(this.sector){
                $targetspace = this.sector;
            }else{
                
                //  assumes that if this.sector is null, we are in a superhighway 
                if(this.zone.issuperhighway){
                    $targetspace = this.zone.destination.sector;
                }else{
                    debug_text("error", "'%s %s %s is neither in a sector nor in a superhighway. zone: %s %s. aborting.'.[this.assignedcontrolled.idcode, this.assignedcontrolled.knownname, this.assignedcontrolled, this.zone.knownname, this.zone]"); // {filter, text}
                    return();
                }
            }
            if($targetspace){
                edit_order_param("will cause script to restart", "this.assignedcontrolled.order", "'targetspace'", "$targetspace"); // {comment, order, param, value}
            }
        }
        if($targetspace.isclass.gate){
            if(not $targetspace.isactive){
                debug_text("'selected gate is inactive. no space to reconnoiter. aborting.'"); // {text}
                return();
            }else{
                debug_text("$debugchance", "'switching targetspace from %s %s %s to %s %s %s.'.[$targetspace.class, $targetspace.knownname, $targetspace, $targetspace.destination.sector.class, $targetspace.destination.sector.knownname, $targetspace.destination.sector]"); // {chance, text}
                $targetspace = $targetspace.destination.sector;
            }
        }else if($targetspace.isclass.highwayentrygate){
            if($targetspace.highway.destination.sector){
                debug_text("$debugchance", "'switching targetspace from %s %s %s to %s %s %s.'.[$targetspace.class, $targetspace.knownname, $targetspace, $targetspace.highway.destination.sector.class, $targetspace.highway.destination.sector.knownname, $targetspace.highway.destination.sector]"); // {chance, text}
                $targetspace = $targetspace.highway.destination.sector;
            }else{
                debug_text("error", "'Highway entry gate: %s has no destination or destination is not in a sector. Destination: %s %s'.[$targetspace, @$targetspace.highway.destination.knownname, @$targetspace.highway.destination]"); // {filter, text}
                return();
            }
        }        
        //  also handles case where targetspace is null 
        else if(not @$targetspace.isclass.sector){
            debug_text("$debugchance", "'Warning: targetspace %1 %2 (%3) is neither a sector, a gate, nor a highway entry gate. Attempting to recover.'.[@$targetspace.class, @$targetspace.knownname, $targetspace]"); // {chance, text}
            if($police){
                if(this.controlled.jobmainzone){
                    if(this.controlled.jobmainzone.policefaction == this.trueowner){
                        $targetspace = this.controlled.jobmainzone.sector;
                    }else{
                        debug_text("error", "'police ship %s %s has jobmainzone set to an area where they are not police. Check job definition: %s. Attempting to recover. jobmainzone: %s %s in sector %s %s with local police %s'.[this.controlled.knownname, this.controlled, this.controlled.job, this.controlled.jobmainzone.knownname, this.controlled.jobmainzone, this.controlled.jobmainzone.sector.knownname, this.controlled.jobmainzone.sector, this.controlled.jobmainzone.policefaction]"); // {filter, text}
                    }
                }else if(@this.sector.policefaction == this.trueowner){
                    $targetspace = this.sector;
                }                else{
                    
                    //  in this case, do not set $targetspace here. will need to set it after the script starts where we could have blocking actions. 
                    debug_text("$debugchance", "'police ship %s %s cannot set a target space at init. initializing after script starts'.[this.controlled.knownname, this.controlled]"); // {chance, text}
                }
            }else if(@$targetspace.sector){
                $targetspace = $targetspace.sector;
            }            else if(this.sector){
                $targetspace = this.sector;
            }            else if(this.zone.isclass.highway){
                $targetspace = this.zone.destination.sector;
            }            else{
                debug_text("error", "'targetspace %1 %2 (%3) is neither a sector nor in a sector. Attempt to recover failed. Stopping reconnaissance. (This can go very badly if this is my default order. Is it? %4)'.[@$targetspace.class, @$targetspace.knownname, $targetspace, this.ship.order == this.ship.defaultorder]"); // {filter, text}
                return();
            }
            debug_text("$debugchance", "'Recovered. targetspace is now: %1 %2 (%3)'.[$targetspace.class, $targetspace.knownname, $targetspace]"); // {chance, text}
        }        create_group("$localtargetgroup"); // {groupname}
        if($police and not this.$police_cleared?){
            this.$police_cleared = table[];
        }
        if($blindtourist and not this.isplayerowned){
            debug_text("$debugchance", "'blind tourist called on someone who does not belong to the player faction. this is not supported. reverting to normal recon.'"); // {chance, text}
            $blindtourist = false;
            edit_order_param("this.assignedcontrolled.order", "'blindtourist'", "false"); // {order, param, value}
        }
        if($blindtourist){
            $anchorpos = position.[0, 0, 0];
            if(this.hascontext.{$targetspace}){
                $refpos = this.assignedcontrolled.position;
                $refzone = this.zone;
            }else if(this.zone.isclass.highway and (this.zone.destination.sector == $targetspace)){
                $refpos = position.[0, 0, 0];
                $refzone = this.zone.destination;
            }            if($refpos? and $refzone?){
                create_position("$anchorpos", "$refzone", "$targetspace", "$refpos"); // {name, object, space, value}
            }
            delete $refzone;
            delete $refpos;
        }
    }
    function attention(unknown){
        set_command_action("commandaction.calculating"); // {commandaction}
        label("start"); // {name}
        if($police){
            if(not $targetspace){
                find_cluster_in_range("$clusterstable", "22", "true", "this.controlled"); // {distances, maxdistance, multiple, object}
                
                //  sort them 
                $sortedclusters = $clusterstable.keys.sorted;
                do_all("$i", "$sortedclusters.count"){ // {counter, exact}
                    find_sector("true", "$clustersectors", "$sortedclusters.{$i}"); // {multiple, name, space}
                    do_all("$j", "$clustersectors.count"){ // {counter, exact}
                        if($clustersectors.{$j}.policefaction == this.trueowner){
                            $targetspace = $clustersectors.{$j};
                            break();
                        }
                    }
                    if($targetspace){
                        debug_text("$debugchance", "'targetspace successfully set.'"); // {chance, text}
                        break();
                    }
                }
                delete $clustersectors;
                delete $sortedclusters;
            }
            debug_text("$debugchance", "'starting in sector %s. local police: %s. our faction: %s. target space: %s %s'.[this.sector.knownname, @this.sector.policefaction, this.trueowner, @$targetspace.class, @$targetspace.knownname]"); // {chance, text}
        }
        
        //  checks: will we need any special equipment? if we don't, should we go get it? could we just assume that we have it? if we have it now, will we always have it? 
        if(not $targetspace and this.ship.isjobship){
            if(this.zone.isclass.highway){
                debug_text("$debugchance", "'%1 %2 is in a highway. waiting to exit.'.[this.ship.idcode, this.ship.knownname]"); // {chance, text}
                wait(){
                }
            }
            $targetspace = this.sector;
        }
        set_order_syncpoint_reached("this.ship.order"); // {order}
        label("prep"); // {name}
        $num_timesreset = @$num_timesreset + 1;
        
        /* <do_if value="$num_timesreset gt 1 and (player.age - $time_lastreset) lt 2s">
                    <debug_text text="'reset after %s seconds.\n in player sector? %s\n police? %s\n resourcescout? %s\n exploreupdate? %s\n blindtourist? %s'.[player.age - $time_lastreset, this.ship.attention ge attention.insector, $police, $resourcescout, $exploreupdate, $blindtourist]"/>
                    <do_if value="not $init_debugchance?">
                      <set_value name="$init_debugchance" exact="$debugchance"/>
                    </do_if>
                    <set_value name="$debugchance" exact="100"/>
                  </do_if>
         */
        $time_lastreset = player.age;
        
        //  NB: this would be more efficient in init, but moved here in case we decide to change $targetspace dynamically via Faction Logic or by other means. 
        if($police){
            set_command("command.investigate"); // {command}
            $scanningrange = [this.ship.maxradarrange, (this.assignedcontrolled.size / 2.0 + 1km)].min;
        }else if($resourcescout){
            set_command("command.searchresources"); // {command}
            $scanningrange = [this.ship.maxradarrange, (this.assignedcontrolled.size / 2.0 + 100m)].min;
        }        else{
            if(this.ship.relationto.{$targetspace.owner} lt 0){
                set_command("command.recon"); // {command}
            }else{
                set_command("command.explore"); // {command}
            }
            $scanningrange = this.ship.maxradarrange;
        }
        if(this.ship.jobexpired or ($timeout and $time_init? and (player.age ge $time_init + $timeout))){
            resume("end"); // {label}
        }else if($timeout and not $time_init?){
            $time_init = player.age;
        }        
        //  reset all recon data. we should only be back here if we're starting fresh. 
        $list_objectsscanned = [];
        $list_threats = [];
        $list_nonthreats = [];
        $table_factionthreatlevels = table[];
        if($exploreupdate){
            create_list("$list_zonestoinvestigate"); // {name}
            find_station("true", "this.owner", "true", "$list_knownstations", "$targetspace"); // {checkoperational, knownto, multiple, name, space}
            
            // <find_station name="$list_knownstations" space="$targetspace" checkoperational="true" canhaveofferlocation="true" knownto="this.owner" multiple="true"/>
            do_all("$i", "$list_knownstations.count"){ // {counter, exact}
                if(not $list_zonestoinvestigate.indexof.{$list_knownstations.{$i}.zone}){
                    append_to_list("$list_knownstations.{$i}.zone", "$list_zonestoinvestigate"); // {exact, name}
                }
            }
            delete $list_knownstations;
        }else if($resourcescout){
            
            //  resourcescout should be called from order.mining.routine after we are already in a resource-rich area, so we shouldn't wander off at this point. 
            create_list("$list_zonestoinvestigate"); // {name}
            append_to_list("this.zone", "$list_zonestoinvestigate"); // {exact, name}
            
            //  $resourcezones populated only in high attention 
            find_zone("true", "$resourcezones", "this.sector"){ // {multiple, name, space}
                match_content("class.asteroid"); // {class}
            }
            shuffle_list("$resourcezones"); // {list}
            do_all("$i", "$resourcezones.count"){ // {counter, exact}
                append_to_list("$resourcezones.{$i}", "$list_zonestoinvestigate"); // {exact, name}
            }
            debug_text("$debugchance", "'resourcescout: found %s zones with resources (only works in high attention). %s zones to explore.'.[$resourcezones.count, $list_zonestoinvestigate.count]"); // {chance, text}
        }        
        //  blind tourist explores each zone as they are found, so no list of zones to explore. 
        else if($blindtourist){
        }        else{
            
            //  make a sorted list of zones in targetspace. 
            find_zone("true", "$list_zonestoinvestigate", "true", "$targetspace"); // {multiple, name, normalzone, space}
        }
        if(@$list_zonestoinvestigate.count){
            debug_text("$debugchance", "'found %1 zones in %2'.[$list_zonestoinvestigate.count, $targetspace.knownname]"); // {chance, text}
            $table_zones = table[];
            do_all("$i", "$list_zonestoinvestigate.count", "true"){ // {counter, exact, reverse}
                $table_zones.{$list_zonestoinvestigate.{$i}} = this.ship.distanceto.{$list_zonestoinvestigate.{$i}};
                if($list_zonestoinvestigate.{$i}.isclass.highway){
                    debug_text("error", "'recon ship has highway in list of zones to investigate.\npolice? %s\nresourcescout? %s\nexploreupdate? %s\nblindtourist? %s'.[$police, $resourcescout, $exploreupdate, $blindtourist]"); // {filter, text}
                    delete $list_zonestoinvestigate.{$i};
                }
            }
            $list_zonestoinvestigate = $table_zones.keys.sorted;
            delete $table_zones;
        }
        debug_text("$debugchance", "'%1 %2 starting reconnaissance mission:\n to %3 %4\n owned by %5.\n relation: %6\n police investigation? %7'.[this.ship.idcode, this.ship.knownname, $targetspace.class, $targetspace.knownname, $targetspace.owner, this.ship.relationto.{$targetspace.owner}, $police]"); // {chance, text}
        label("gothere"); // {name}
        
        //  are we there yet? if not, go there. (note: what is "there"? targetspace? an interim zone?) 
        if(not this.hascontext.{$targetspace}){
            debug_text("$debugchance", "'%1 %2 moving to %3 %4, %5.'.[this.ship.idcode, this.ship.knownname, $targetspace.class, $targetspace.knownname, $targetspace.cluster.knownname]"); // {chance, text}
            run_script('move.generic'){
                param destination("$targetspace"); // {value}
                param endintargetzone("true"); // {value}
                param debugchance("$debugchance"); // {value}
            }
        }
        if(this.sector != $targetspace){
            debug_text("error", "'%1 %2 did not arrive at targetspace.\n targetspace: %3, %4\n at: %5, %6'.[this.ship.idcode, this.ship.knownname, $targetspace.knownname, $targetspace.cluster.knownname, this.sector.knownname, this.cluster.knownname]"); // {filter, text}
            resume("gothere"); // {label}
        }
        if($police and @this.zone.policefaction != this.trueowner){
            debug_text("error", "'police ship %s %s at targetspace but we are not the police faction here. local police: %s, our faction: %s\nat: %s %s owned by %s'.[this.controlled.knownname, this.controlled, @this.zone.policefaction, this.trueowner, this.zone.knownname, this.zone, this.zone.trueowner]"); // {filter, text}
        }
        
        //  determine the zone we want to go to, 
        label("proceedtonextzone"); // {name}
        $destination = null;
        
        //  Idle for a while. Important to prevent infinite loops in cases where there is absolutely nothing to explore. Shouldn't happen in normal game setup except for resource scout in low attention. 
        run_script('move.idle'){
            param Min("1min"); // {value}
            param Max("5min"); // {value}
        }
        if($blindtourist){
            
            //  try to find an undiscovered spot that's fairly close to me and is toward the sector center. 
            find_closest_undiscovered_position("this.assignedcontrolled.combinedskill", "this.ship.maxradarrange * 2", "$pos_target", "$targetspace"){ // {chance, range, result, sector}
                position("$targetspace"); // {object}
                rangecenter("this.assignedcontrolled"); // {object}
            }
            if(not @$pos_target or $pos_target.distanceto.{$anchorpos} gt $radius){
                debug_text("$debugchance", "'no close undiscovered areas found. widening search.'"); // {chance, text}
                
                /*  if there aren't any, find the closest undiscovered spot to me. cap at a sensible distance.
                                      fallback pos changed back to unknown closest to me to mitigate multiple ships going to the same position. 
                 */
                find_closest_undiscovered_position("$radius", "$pos_target", "$targetspace"){ // {range, result, sector}
                    position("this.assignedcontrolled"); // {object}
                    rangecenter("$anchorpos"); // {value}
                }
            }
            if(@$pos_target){
                $destination = $targetspace;
                create_position("$debugchance", "$temp_sectorpos", "this.zone", "$targetspace", "this.position"); // {chance, name, object, space, value}
                debug_text("$debugchance", "'blind tourist is going to %s in %s %s %s.\npresent position: %s\nmy distance to new pos: %s\nanchor distance to new pos: %s\nradius: %s\nsector center distance to new pos: %s'.[$pos_target, $destination.class, $destination.knownname, $destination, $temp_sectorpos, this.ship.distanceto.[$targetspace, $pos_target], $pos_target.distanceto.{$anchorpos}, $radius, $targetspace.distanceto.[$targetspace, $pos_target]]"); // {chance, text}
                delete $temp_sectorpos;
            }else{
                debug_text("$debugchance", "'blind tourist has nothing left to explore. reverting to normal reconnaisance.'"); // {chance, text}
                edit_order_param("this.assignedcontrolled.order", "'blindtourist'", "false"); // {order, param, value}
            }
        }else if(@$list_zonestoinvestigate.{1}){
            if($list_zonestoinvestigate.{1}.exists){
                $destination = $list_zonestoinvestigate.{1};
            }else{
                do_all("$i", "$list_zonestoinvestigate.count", "true"){ // {counter, exact, reverse}
                    if(not $list_zonestoinvestigate.{$i}.exists){
                        delete $list_zonestoinvestigate.{$i};
                    }
                }
                if(@$list_zonestoinvestigate.{1}){
                    $destination = $list_zonestoinvestigate.{1};
                }
            }
        }        
        //  if we're there, move around looking for trouble. 
        label("lookfortrouble"); // {name}
        if(@$destination.exists){
            
            //  pos_target is only used by blindtourist at the moment. 
            if($pos_target?){
                get_safe_pos("this.ship.size / 2", "$safepos", "$destination", "$destination", "$pos_target"); // {radius, result, sector, space, value}
                if(true){
                    
                    //  below optimistically assumes that safepos is fairly close to pos_target, close enough that one of the two distances checked is the hypotenuse. if we are very very close to either the safepos or pos_target, or if safepos is way off, then the distance to safepos calculation will be inaccurate. only used for debug output for now. 
                    if(this.ship.distanceto.[$destination, $safepos] gt this.ship.distanceto.[$destination, $pos_target]){
                        $dist_hyp = this.ship.distanceto.[$destination, $safepos];
                        $dist_nothyp = this.ship.distanceto.[$destination, $pos_target];
                    }else{
                        $dist_hyp = this.ship.distanceto.[$destination, $pos_target];
                        $dist_nothyp = this.ship.distanceto.[$destination, $safepos];
                    }
                    debug_text("'safepos is:\n%s meters away from pos_target\n%s meters away from us\nand %s meters away from sector center.'.[sqrt(($dist_hyp^2)-($dist_nothyp^2)), this.ship.distanceto.[$destination, $safepos], $destination.distanceto.[$destination, $safepos]]"); // {text}
                    delete $dist_nothyp;
                    delete $dist_hyp;
                }
                delete $pos_target;
            }else{
                get_safe_pos("$destination.size / 3", "this.ship.maxradarrange", "this.ship.size / 2", "$safepos", "$destination"); // {max, min, radius, result, zone}
                debug_text("$debugchance", "'next safe pos is %s km from the center of %s %s.\nzone radius: %s km.\nradar range: %s km\npos: %s'.[$destination.distanceto.[$destination, $safepos] / 1000, $destination.class, $destination.knownname, $destination.size / 2 / 1000, this.ship.maxradarrange / 1000, $safepos]"); // {chance, text}
            }
            debug_text("$debugchance", "'going to investigate %1 %2 in %3'.[$destination.class, $destination.knownname, $targetspace.knownname]"); // {chance, text}
        }else{
            debug_text("$debugchance", "'went through all of the zones.'"); // {chance, text}
            resume("report"); // {label}
        }
        
        //  it's possible to wander to a different sector entirely while scanning a ship. in that case, go back to the targetspace. 
        if(not this.hascontext.{$targetspace}){
            debug_text("$debugchance", "'%1 %2 wandered out of target space. Going back.\n am in: %3\n target space: %4'.[this.ship.idcode, this.ship.knownname, this.sector.knownname, $targetspace.knownname]"); // {chance, text}
            resume("gothere"); // {label}
        }
        if($police and $destination.isclass.[class.sector, class.zone] and @$destination.policefaction != this.trueowner){
            debug_text("error", "'police ship %s %s about to patrol area where they are not police. Check jobs setup or faction logic assignment, whichever applies.'.[this.controlled.knownname, this.controlled]"); // {filter, text}
            assert("'police ship %s %s about to patrol area where they are not police. Check jobs setup (location) or faction logic assignment (targetspace), whichever applies.'.[this.controlled.knownname, this.controlled]", "@$destination.policefaction == this.trueowner"); // {text, value}
        }
        if($resourcescout){
            
            //  we skip move.seekenemies at this point since gravidar doesn't find asteroids anyway which are our primary items of interest. 
            find_object("[class.asteroid, class.lockbox, class.collectable]", "true", "$list_potentialtargets", "this.sector"){ // {class, multiple, name, space}
                match_distance("this.assignedcontrolled.maxradarrange * 2", "this.assignedcontrolled"); // {max, object}
            }
            if(not $list_potentialtargets.count){
                if(this.assignedcontrolled.attention ge attention.visible){
                    
                    //  if we don't find anything in high attention, there's nothing to do here. go elsewhere. 
                    return();
                }
                
                /*  in low attention, there's a big chance that nothing will be found since asteroids are culled.
                                with a shift to high attention, asteroids will exist and we want this ship to operate in the general vicinity.
                                resource scout only ever has one zone in its list so, if we want it to shift spaces at this point, resume to label "end" and have order.mining.routine give us a new space. 
                 */
                debug_text("$debugchance", "'resource scout %s %s did not find anything. idling.'.[this.assignedcontrolled.knownname, this.assignedcontrolled]"); // {chance, text}
                run_script('move.idle'){
                    
                    /* <param name="Min" value="17min"/>
                                    <param name="Max" value="43min"/>
                     */
                    param untilattentionchangeto("attention.visible"); // {value}
                    param debugchance("$debugchance"); // {value}
                }
                wait("1s"); // {exact}
                if($destination.exists){
                    resume("lookfortrouble"); // {label}
                }else{
                    resume("proceedtonextzone"); // {label}
                }
            }
            debug_text("$debugchance", "'resource scout %s %s found %s items of interest in sector %s.'.[this.assignedcontrolled.knownname, this.assignedcontrolled, $list_potentialtargets.count, this.sector.knownname]"); // {chance, text}
        }else{
            if(this.ship.distanceto.[$destination, $safepos] gt (this.ship.maxspeed * 60)){
                debug_text("$debugchance", "'destination to next position very far away. moving quickly to a point close to it.\n max speed: %s m/s\n distance: %s m\n time to traverse: %s s'.[this.ship.maxspeed, this.ship.distanceto.[$destination, $safepos], this.ship.distanceto.[$destination, $safepos] / [this.ship.maxspeed, 1m].max]"); // {chance, text}
                create $travel;
            }
            
            //  and go there while scanning for trouble. 
            run_script('move.seekenemies'){
                param destination("$destination"); // {value}
                param pos("$safepos"); // {value}
                
                // <param name="pursuedistance" value="$scanningrange"/>
                param targetclasses("$targetclasses"); // {value}
                param targetpurposes("$targetpurposes"); // {value}
                param recon("true"); // {value}
                param travel("$travel?"); // {value}
                param list_objectstoignore("$list_objectsscanned"); // {value}
                param debugchance("$debugchance"); // {value}
                save_retval("target", "$localtarget"); // {name, variable}
                save_retval("list_targets", "$list_potentialtargets"); // {name, variable}
            }
            delete $travel;
        }
        label("scan"); // {name}
        
.......



Converted 'order.move.recon' code: (Converted from XML->Script->XML)
(Note the errors here, needs debugged out)

Code: Select all

<?xml version="1.0" encoding="utf-8"?>
<aiscript name="order.move.recon" xsi:noNamespaceSchemaLocation="aiscripts.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <order id="Recon" name="{1041,291}" description="{1041,292}" category="internal">
        <params />
        <params>
            <param name="length" type="radius" default="null" advanced="true" comment="Radius. only used by blindtourist." text="{1041, 10093}" />
        </params>
        <params />
        <params>
            <param name="internal" type="targetclasses" default="[class.ship, class.station]" comment="Target object classes" />
            <param name="internal" type="targetpurposes" default="[]" comment="Target object purposes" />
            <param name="internal" type="police" default="false" comment="Police investigation. Look for disguised criminals or smuggled goods? Otherwise, default behavior is to identify potential threats to our faction in $targetspace." text="{1041, 10084}" />
            <param name="internal" type="exploreupdate" default="false" comment="Update known space. Refresh information on known stations in $targetspace." text="{1041, 10137}" />
            <param name="internal" type="resourcescout" default="false" comment="Scout for resources. Look for valuable asteroids, collectables, or lockboxes in $targetspace." text="{1041, 10109}" />
            <param name="internal" type="blindtourist" default="false" comment="Explore blind with no cheating" text="{1041, 10039}" />
            <param name="internal" type="cannotdock" default="false" comment="Unable to dock. used for error handling in case of inability to dock" text="{1041, 10133}" />
        </params>
        <skill min="40" />
    </order>
    <interrupts>
        <handler ref="SectorChangeHandler" />
        <handler ref="TargetInvalidHandler" />
        <handler ref="AttackHandler" />
        <handler ref="MissileLockHandler" />
        <handler ref="ScannedHandler" />
        <handler ref="InspectedHandler" />
        <handler ref="FoundAbandonedHandler" />
        <handler ref="ResupplyHandler" />
        <handler ref="JobRemoveRequestHandler" />
    </interrupts>
    <init>
        <do_if value="not $targetspace">
            <do_if value="this.sector">
                <set_value name="$targetspace" exact="this.sector" />
            </do_if>
            <do_else>
                <!-- assumes that if this.sector is null, we are in a superhighway-->
                <do_if value="this.zone.issuperhighway">
                    <set_value name="$targetspace" exact="this.zone.destination.sector" />
                </do_if>
                <do_else>
                    <debug_text filter="error" text="'%s %s %s is neither in a sector nor in a superhighway. zone: %s %s. aborting.'.[this.assignedcontrolled.idcode,this.assignedcontrolled.knownname,this.assignedcontrolled,this.zone.knownname,this.zone]" />
                    <return />
                </do_else>
            </do_else>
            <do_if value="$targetspace">
                <edit_order_param param="'targetspace'" comment="will cause script to restart" value="$targetspace" order="this.assignedcontrolled.order" />
            </do_if>
        </do_if>
        <do_if value="$targetspace.isclass.gate">
            <do_if value="not $targetspace.isactive">
                <debug_text text="'selected gate is inactive. no space to reconnoiter. aborting.'" />
                <return />
            </do_if>
            <do_else>
                <debug_text chance="$debugchance" text="'switching targetspace from %s %s %s to %s %s %s.'.[$targetspace.class,$targetspace.knownname,$targetspace,$targetspace.destination.sector.class,$targetspace.destination.sector.knownname,$targetspace.destination.sector]" />
                <set_value name="$targetspace" exact="$targetspace.destination.sector" />
            </do_else>
        </do_if>
        <do_elseif value="$targetspace.isclass.highwayentrygate">
            <do_if value="$targetspace.highway.destination.sector">
                <debug_text chance="$debugchance" text="'switching targetspace from %s %s %s to %s %s %s.'.[$targetspace.class,$targetspace.knownname,$targetspace,$targetspace.highway.destination.sector.class,$targetspace.highway.destination.sector.knownname,$targetspace.highway.destination.sector]" />
                <set_value name="$targetspace" exact="$targetspace.highway.destination.sector" />
            </do_if>
            <do_else>
                <debug_text filter="error" text="'Highway entry gate: %s has no destination or destination is not in a sector. Destination: %s %s'.[$targetspace,@$targetspace.highway.destination.knownname,@$targetspace.highway.destination]" />
                <return />
            </do_else>
        </do_elseif>
        <!-- also handles case where targetspace is null-->
        <do_elseif value="not @$targetspace.isclass.sector">
            <debug_text chance="$debugchance" text="'Warning: targetspace %1 %2 (%3) is neither a sector,a gate,nor a highway entry gate. Attempting to recover.'.[@$targetspace.class,@$targetspace.knownname,$targetspace]" />
            <do_if value="$police">
                <do_if value="this.controlled.jobmainzone">
                    <do_if value="this.controlled.jobmainzone.policefaction == this.trueowner">
                        <set_value name="$targetspace" exact="this.controlled.jobmainzone.sector" />
                    </do_if>
                    <do_else>
                        <debug_text filter="error" text="'police ship %s %s has jobmainzone set to an area where they are not police. Check job definition: %s. Attempting to recover. jobmainzone: %s %s in sector %s %s with local police %s'.[this.controlled.knownname,this.controlled,this.controlled.job,this.controlled.jobmainzone.knownname,this.controlled.jobmainzone,this.controlled.jobmainzone.sector.knownname,this.controlled.jobmainzone.sector,this.controlled.jobmainzone.policefaction]" />
                    </do_else>
                </do_if>
                <do_elseif value="@this.sector.policefaction == this.trueowner">
                    <set_value name="$targetspace" exact="this.sector" />
                </do_elseif>
                <do_else>
                    <!-- in this case, do not set $targetspace here. will need to set it after the script starts where we could have blocking actions.-->
                    <debug_text chance="$debugchance" text="'police ship %s %s cannot set a target space at init. initializing after script starts'.[this.controlled.knownname,this.controlled]" />
                </do_else>
            </do_if>
            <do_elseif value="@$targetspace.sector">
                <set_value name="$targetspace" exact="$targetspace.sector" />
            </do_elseif>
            <do_elseif value="this.sector">
                <set_value name="$targetspace" exact="this.sector" />
            </do_elseif>
            <do_elseif value="this.zone.isclass.highway">
                <set_value name="$targetspace" exact="this.zone.destination.sector" />
            </do_elseif>
            <do_else>
                <debug_text filter="error" text="'targetspace %1 %2 (%3) is neither a sector nor in a sector. Attempt to recover failed. Stopping reconnaissance. (This can go very badly if this is my default order. Is it? %4)'.[@$targetspace.class,@$targetspace.knownname,$targetspace,this.ship.order == this.ship.defaultorder]" />
                <return />
            </do_else>
            <debug_text chance="$debugchance" text="'Recovered. targetspace is now: %1 %2 (%3)'.[$targetspace.class,$targetspace.knownname,$targetspace]" />
        </do_elseif>
        <do_if value="$police and not this.$police_cleared?">
            <set_value name="this.$police_cleared" exact="table[]" />
        </do_if>
        <do_if value="$blindtourist and not this.isplayerowned">
            <debug_text chance="$debugchance" text="'blind tourist called on someone who does not belong to the player faction. this is not supported. reverting to normal recon.'" />
            <set_value name="$blindtourist" exact="false" />
            <edit_order_param param="'blindtourist'" value="false" order="this.assignedcontrolled.order" />
        </do_if>
        <do_if value="$blindtourist">
            <set_value name="$anchorpos" exact="position.[0, 0, 0]" />
            <do_if value="this.hascontext.{$targetspace}">
                <set_value name="$refpos" exact="this.assignedcontrolled.position" />
                <set_value name="$refzone" exact="this.zone" />
            </do_if>
            <do_elseif value="this.zone.isclass.highway and (this.zone.destination.sector == $targetspace)">
                <set_value name="$refpos" exact="position.[0, 0, 0]" />
                <set_value name="$refzone" exact="this.zone.destination" />
            </do_elseif>
            <create_position name="$anchorpos" object="$refzone" value="$refpos" space="$targetspace" />
            <remove_value name="refzone" />
            <remove_value name="refpos" />
        </do_if>
    </init>
    <attention min="unknown">
        <actions>
            <set_command_action commandaction="commandaction.calculating" />
            <label name="start" />
            <do_if value="$police">
                <do_if value="not $targetspace">
                    <find_cluster_in_range object="this.controlled" maxdistance="22" distances="$clusterstable" multiple="true" />
                    <!-- sort them-->
                    <set_value name="$sortedclusters" exact="$clusterstable.keys.sorted" />
                    <do_all exact="$sortedclusters.count" counter="$i">
                        <find_sector name="$clustersectors" multiple="true" space="$sortedclusters.{$i}" />
                        <do_all exact="$clustersectors.count" counter="$j">
                            <do_if value="$clustersectors.{$j}.policefaction == this.trueowner">
                                <set_value name="$targetspace" exact="$clustersectors.{$j}" />
                                <break />
                            </do_if>
                        </do_all>
                        <do_if value="$targetspace">
                            <debug_text chance="$debugchance" text="'targetspace successfully set.'" />
                            <break />
                        </do_if>
                    </do_all>
                    <remove_value name="clustersectors" />
                    <remove_value name="sortedclusters" />
                </do_if>
                <debug_text chance="$debugchance" text="'starting in sector %s. local police: %s. our faction: %s. target space: %s %s'.[this.sector.knownname,@this.sector.policefaction,this.trueowner,@$targetspace.class,@$targetspace.knownname]" />
            </do_if>
            <!-- checks: will we need any special equipment? if we don't, should we go get it? could we just assume that we have it? if we have it now, will we always have it?-->
            <do_if value="not $targetspace and this.ship.isjobship">
                <do_if value="this.zone.isclass.highway">
                    <debug_text chance="$debugchance" text="'%1 %2 is in a highway. waiting to exit.'.[this.ship.idcode,this.ship.knownname]" />
                    <wait />
                </do_if>
                <set_value name="$targetspace" exact="this.sector" />
            </do_if>
            <set_order_syncpoint_reached order="this.ship.order" />
            <label name="prep" />
            <set_value name="$num_timesreset" exact="@$num_timesreset + 1" />
            <!-- /* <do_if value=\"$num_timesreset gt 1 and (player.age - $time_lastreset) lt 2s\">
<debug_text text=\"'reset after %s seconds.\n in player sector? %s\n police? %s\n resourcescout? %s\n exploreupdate? %s\n blindtourist? %s'.[player.age - $time_lastreset, this.ship.attention ge attention.insector, $police, $resourcescout, $exploreupdate, $blindtourist]\"/>
<do_if value=\"not $init_debugchance?\">
<set_value name=\"$init_debugchance\" exact=\"$debugchance\"/>
</do_if><set_value name=\"$debugchance\" exact=\"100\"/>
</do_if>-->
            <set_value name="$time_lastreset" exact="player.age" />
            <!-- NB: this would be more efficient in init, but moved here in case we decide to change $targetspace dynamically via Faction Logic or by other means.-->
            <do_if value="$police">
                <set_command command="command.investigate" />
                <set_value name="$scanningrange" exact="[this.ship.maxradarrange, (this.assignedcontrolled.size / 2.0 + 1km)].min" />
            </do_if>
            <do_elseif value="$resourcescout">
                <set_command command="command.searchresources" />
                <set_value name="$scanningrange" exact="[this.ship.maxradarrange, (this.assignedcontrolled.size / 2.0 + 100m)].min" />
            </do_elseif>
            <do_else>
                <do_if value="this.ship.relationto.{$targetspace.owner} lt 0">
                    <set_command command="command.recon" />
                </do_if>
                <do_else>
                    <set_command command="command.explore" />
                </do_else>
                <set_value name="$scanningrange" exact="this.ship.maxradarrange" />
            </do_else>
            <do_if value="this.ship.jobexpired or ($timeout and $time_init? and (player.age ge $time_init + $timeout))">
                <resume label="end" />
            </do_if>
            <do_elseif value="$timeout and not $time_init?">
                <set_value name="$time_init" exact="player.age" />
            </do_elseif>
            <!-- reset all recon data. we should only be back here if we're starting fresh.-->
            <set_value name="$list_objectsscanned" exact="[]" />
            <set_value name="$list_threats" exact="[]" />
            <set_value name="$list_nonthreats" exact="[]" />
            <set_value name="$table_factionthreatlevels" exact="table[]" />
            <do_if value="$exploreupdate">
                <create_list name="$list_zonestoinvestigate" />
                <find_station name="$list_knownstations" multiple="true" checkoperational="true" knownto="this.owner" space="$targetspace" />
                <!-- <find_station name=\"$list_knownstations\" space=\"$targetspace\" checkoperational=\"true\" canhaveofferlocation=\"true\" knownto=\"this.owner\" multiple=\"true\"/>-->
                <do_all exact="$list_knownstations.count" counter="$i">
                    <do_if value="not $list_zonestoinvestigate.indexof.{$list_knownstations.{$i}.zone}">
                        <append_to_list name="$list_zonestoinvestigate" exact="$list_knownstations.{$i}.zone" />
                    </do_if>
                </do_all>
                <remove_value name="list_knownstations" />
            </do_if>
            <do_elseif value="$resourcescout">
                <!-- resourcescout should be called from order.mining.routine after we are already in a resource-rich area, so we shouldn't wander off at this point.-->
                <create_list name="$list_zonestoinvestigate" />
                <append_to_list name="$list_zonestoinvestigate" exact="this.zone" />
                <!-- $resourcezones populated only in high attention-->
                <find_zone name="$resourcezones" multiple="true" space="this.sector">
                    <match_content class="class.asteroid" />
                </find_zone>
                <shuffle_list list="$resourcezones" />
                <do_all exact="$resourcezones.count" counter="$i">
                    <append_to_list name="$list_zonestoinvestigate" exact="$resourcezones.{$i}" />
                </do_all>
                <debug_text chance="$debugchance" text="'resourcescout: found %s zones with resources (only works in high attention). %s zones to explore.'.[$resourcezones.count,$list_zonestoinvestigate.count]" />
            </do_elseif>
            <!-- blind tourist explores each zone as they are found, so no list of zones to explore.-->
            <do_elseif value="$blindtourist" />
            <do_else>
                <!-- make a sorted list of zones in targetspace.-->
                <find_zone name="$list_zonestoinvestigate" multiple="true" normalzone="true" space="$targetspace" />
            </do_else>
            <do_if value="@$list_zonestoinvestigate.count">
                <debug_text chance="$debugchance" text="'found %1 zones in %2'.[$list_zonestoinvestigate.count,$targetspace.knownname]" />
                <set_value name="$table_zones" exact="table[]" />
                <do_all exact="$list_zonestoinvestigate.count" counter="$i" reverse="true">
                    <set_value name="$table_zones.{$list_zonestoinvestigate.{$i}}" exact="this.ship.distanceto.{$list_zonestoinvestigate.{$i}}" />
                    <do_if value="$list_zonestoinvestigate.{$i}.isclass.highway">
                        <debug_text filter="error" text="'recon ship has highway in list of zones to investigate.\npolice? %s\nresourcescout? %s\nexploreupdate? %s\nblindtourist? %s'.[$police,$resourcescout,$exploreupdate,$blindtourist]" />
                        <remove_value name="list_zonestoinvestigate.{$i}" />
                    </do_if>
                </do_all>
                <set_value name="$list_zonestoinvestigate" exact="$table_zones.keys.sorted" />
                <remove_value name="table_zones" />
            </do_if>
            <debug_text chance="$debugchance" text="'%1 %2 starting reconnaissance mission:\n to %3 %4\n owned by %5.\n relation: %6\n police investigation? %7'.[this.ship.idcode,this.ship.knownname,$targetspace.class,$targetspace.knownname,$targetspace.owner,this.ship.relationto.{$targetspace.owner},$police]" />
            <label name="gothere" />
            <!-- are we there yet? if not, go there. (note: what is \"there\"? targetspace? an interim zone?)-->
            <do_if value="not this.hascontext.{$targetspace}">
                <debug_text chance="$debugchance" text="'%1 %2 moving to %3 %4,%5.'.[this.ship.idcode,this.ship.knownname,$targetspace.class,$targetspace.knownname,$targetspace.cluster.knownname]" />
                <run_script />
            </do_if>
            <params>
                <param name="destination" value="$targetspace" />
                <param name="endintargetzone" value="true" />
                <param name="debugchance" value="$debugchance" />
            </params>
            <do_if value="this.sector != $targetspace">
                <debug_text filter="error" text="'%1 %2 did not arrive at targetspace.\n targetspace: %3,%4\n at: %5,%6'.[this.ship.idcode,this.ship.knownname,$targetspace.knownname,$targetspace.cluster.knownname,this.sector.knownname,this.cluster.knownname]" />
                <resume label="gothere" />
            </do_if>
            <do_if value="$police and @this.zone.policefaction != this.trueowner">
                <debug_text filter="error" text="'police ship %s %s at targetspace but we are not the police faction here. local police: %s,our faction: %s\nat: %s %s owned by %s'.[this.controlled.knownname,this.controlled,@this.zone.policefaction,this.trueowner,this.zone.knownname,this.zone,this.zone.trueowner]" />
            </do_if>
            <!-- determine the zone we want to go to,-->
            <label name="proceedtonextzone" />
            <set_value name="$destination" exact="null" />
            <!-- Idle for a while. Important to prevent infinite loops in cases where there is absolutely nothing to explore. Shouldn't happen in normal game setup except for resource scout in low attention.-->
            <run_script />
            <params>
                <param name="Min" value="1min" />
                <param name="Max" value="5min" />
            </params>
            <do_if value="$blindtourist">
                <!-- try to find an undiscovered spot that's fairly close to me and is toward the sector center.-->
                <find_closest_undiscovered_position result="$pos_target" chance="this.assignedcontrolled.combinedskill" range="this.ship.maxradarrange * 2" sector="$targetspace">
                    <position object="$targetspace" />
                    <rangecenter object="this.assignedcontrolled" />
                </find_closest_undiscovered_position>
                <do_if value="not @$pos_target or $pos_target.distanceto.{$anchorpos} gt $radius">
                    <debug_text chance="$debugchance" text="'no close undiscovered areas found. widening search.'" />
                    <!-- /*  if there aren't any, find the closest undiscovered spot to me. cap at a sensible distance.
fallback pos changed back to unknown closest to me to mitigate multiple ships going to the same position.-->
                    <find_closest_undiscovered_position result="$pos_target" range="$radius" sector="$targetspace">
                        <position object="this.assignedcontrolled" />
                        <rangecenter value="$anchorpos" />
                    </find_closest_undiscovered_position>
                </do_if>
                <do_if value="@$pos_target">
                    <set_value name="$destination" exact="$targetspace" />
                    <create_position name="$temp_sectorpos" object="this.zone" chance="$debugchance" value="this.position" space="$targetspace" />
                    <debug_text chance="$debugchance" text="'blind tourist is going to %s in %s %s %s.\npresent position: %s\nmy distance to new pos: %s\nanchor distance to new pos: %s\nradius: %s\nsector center distance to new pos: %s'.[$pos_target,$destination.class,$destination.knownname,$destination,$temp_sectorpos,this.ship.distanceto.[$targetspace,$pos_target],$pos_target.distanceto.{$anchorpos},$radius,$targetspace.distanceto.[$targetspace,$pos_target]]" />
                    <remove_value name="temp_sectorpos" />
                </do_if>
                <do_else>
                    <debug_text chance="$debugchance" text="'blind tourist has nothing left to explore. reverting to normal reconnaisance.'" />
                    <edit_order_param param="'blindtourist'" value="false" order="this.assignedcontrolled.order" />
                </do_else>
            </do_if>
            <do_elseif value="@$list_zonestoinvestigate.{1">
                <do_if value="$list_zonestoinvestigate.{1}.exists">
                    <set_value name="$destination" exact="$list_zonestoinvestigate.{1}" />
                </do_if>
                <do_else>
                    <do_all exact="$list_zonestoinvestigate.count" counter="$i" reverse="true">
                        <do_if value="not $list_zonestoinvestigate.{$i}.exists">
                            <remove_value name="list_zonestoinvestigate.{$i}" />
                        </do_if>
                    </do_all>
                    <do_if value="@$list_zonestoinvestigate.{1}">
                        <set_value name="$destination" exact="$list_zonestoinvestigate.{1}" />
                    </do_if>
                </do_else>
            </do_elseif>
            <!-- if we're there, move around looking for trouble.-->
            <label name="lookfortrouble" />
            <do_if value="@$destination.exists">
                <!-- pos_target is only used by blindtourist at the moment.-->
                <do_if value="$pos_target?">
                    <get_safe_pos result="$safepos" radius="this.ship.size / 2" sector="$destination" value="$pos_target" space="$destination" />
                    <do_if value="true">
                        <!-- below optimistically assumes that safepos is fairly close to pos_target, close enough that one of the two distances checked is the hypotenuse. if we are very very close to either the safepos or pos_target, or if safepos is way off, then the distance to safepos calculation will be inaccurate. only used for debug output for now.-->
                        <do_if value="this.ship.distanceto.[$destination, $safepos] gt this.ship.distanceto.[$destination, $pos_target]">
                            <set_value name="$dist_hyp" exact="this.ship.distanceto.[$destination, $safepos]" />
                            <set_value name="$dist_nothyp" exact="this.ship.distanceto.[$destination, $pos_target]" />
                        </do_if>
                        <do_else>
                            <set_value name="$dist_hyp" exact="this.ship.distanceto.[$destination, $pos_target]" />
                            <set_value name="$dist_nothyp" exact="this.ship.distanceto.[$destination, $safepos]" />
                        </do_else>
                        <debug_text text="'safepos is:\n%s meters away from pos_target\n%s meters away from us\nand %s meters away from sector center.'.[sqrt(($dist_hyp^2)-($dist_nothyp^2)), this.ship.distanceto.[$destination, $safepos], $destination.distanceto.[$destination, $safepos]]" />
                        <remove_value name="dist_nothyp" />
                        <remove_value name="dist_hyp" />
                    </do_if>
                    <remove_value name="pos_target" />
                </do_if>
                <do_else>
                    <get_safe_pos result="$safepos" min="this.ship.maxradarrange" max="$destination.size / 3" zone="$destination" radius="this.ship.size / 2" />
                    <debug_text chance="$debugchance" text="'next safe pos is %s km from the center of %s %s.\nzone radius: %s km.\nradar range: %s km\npos: %s'.[$destination.distanceto.[$destination,$safepos] / 1000,$destination.class,$destination.knownname,$destination.size / 2 / 1000,this.ship.maxradarrange / 1000,$safepos]" />
                </do_else>
                <debug_text chance="$debugchance" text="'going to investigate %1 %2 in %3'.[$destination.class,$destination.knownname,$targetspace.knownname]" />
            </do_if>
            <do_else>
                <debug_text chance="$debugchance" text="'went through all of the zones.'" />
                <resume label="report" />
            </do_else>
            <!-- it's possible to wander to a different sector entirely while scanning a ship. in that case, go back to the targetspace.-->
            <do_if value="not this.hascontext.{$targetspace}">
                <debug_text chance="$debugchance" text="'%1 %2 wandered out of target space. Going back.\n am in: %3\n target space: %4'.[this.ship.idcode,this.ship.knownname,this.sector.knownname,$targetspace.knownname]" />
                <resume label="gothere" />
            </do_if>
            <do_if value="$police and $destination.isclass.[class.sector, class.zone] and @$destination.policefaction != this.trueowner">
                <debug_text filter="error" text="'police ship %s %s about to patrol area where they are not police. Check jobs setup or faction logic assignment,whichever applies.'.[this.controlled.knownname,this.controlled]" />
                <assert text="'police ship %s %s about to patrol area where they are not police. Check jobs setup (location) or faction logic assignment (targetspace),whichever applies.'.[this.controlled.knownname,this.controlled]" value="@$destination.policefaction == this.trueowner" />
            </do_if>
.........
Because I can, I'm here to disturb your mind.
iforgotmysocks
Posts: 1244
Joined: Fri, 8. Nov 13, 22:35
x4

Re: [TOOL][WIP] Script Editor X2S (XML -> Script -> XML)

Post by iforgotmysocks »

I actually really like the xml based scripting language. And i'm saying this being a .net developer. xD
Nice work on this project tho, hope u can figure it out. :)
SpaceCadet11864
Posts: 476
Joined: Tue, 4. Dec 18, 02:14
x4

Re: [TOOL][WIP] Script Editor X2S (XML -> Script -> XML)

Post by SpaceCadet11864 »

I played around with a tool to convert the XML to YAML, which makes it much easier to read, but it does funky suff properties of elements, making them values themselves but prefixing them with underscores.

Code: Select all

    <cue name="OperationCreated" instantiate="true" namespace="this" version="3">
      <conditions>
        <event_boarding_operation_created/>
      </conditions>
      <actions>
        <set_value name="$DebugChance" exact="0"/>

        <debug_text text="player.age + ' Boarding operation created ' + event.param"/>

        <set_value name="$Operation" exact="event.param"/>
        <set_value name="$CurrentPhase" exact="null"/>
        <set_value name="$Target" exact="$Operation.boardee"/>
        <set_value name="$PlayerInvolved" exact="false"/>
        <set_value name="$MissionCue" exact="OperationCreated"/>

        <set_value name="$HasStartedInfiltration" exact="false" comment="Helper value to state if the infiltration phase has been reached and any straggler marines can immediately cut through the hull"/>
        <set_value name="$HasStartedInternalFight" exact="false"/>
        <set_value name="$InfiltratingMarineProgress" exact="table[]" comment="Table of infiltrating marine templates and their progress percentage"/>
        <set_value name="$PreInfiltrationMaxProgress" exact="15.0f"/>
        <set_value name="$AllInfiltrated" exact="false"/>
        <set_value name="$AttackingTeams" exact="[]"/>
        <set_value name="$NumDefendersKilled" exact="0"/>

        <create_group groupname="$ApproachingBoardingPods"/>
        <create_group groupname="$AttachedBoardingPods"/>

        <set_value name="$PlacedSpeaker" exact="null"/>
        <set_value name="$CurrentSpeaker" exact="null"/>
        <set_value name="$CurrentPriority" exact="-1"/>
        <set_value name="$LastSpeakTime" exact="-1s"/>
        <set_value name="$LastSpeakTemplate" exact="null"/>
        <set_value name="$LastLaunchSpeakTime" exact="-1s"/>
        <set_value name="$LastPodSpeakTime" exact="-1s"/>
        <set_value name="$LastFightSpeakTime" exact="-1s"/>
        <set_value name="$LastFightSpeakLib" exact="null"/>

        <!--Try to keep track of the 'marine commander' so all commanding lines are coming from the same page. It may simply be the first marine to say something.-->
        <set_value name="$MarineCommanderTemplate" exact="null"/>
        <set_value name="$MarineCommanderPage" exact="null"/>
        <set_value name="$MarineCommanderShip" exact="null"/>
      </actions>
      <patch sinceversion="2">
        <set_value name="$PlayerInvolved" exact="false"/>
        <set_value name="$MissionCue" exact="OperationCreated"/>
      </patch>
      <patch sinceversion="3">
        <set_value name="$AttackingTeams" exact="[]"/>
        <set_value name="$NumDefendersKilled" exact="0"/>

        <set_value name="$PlacedSpeaker" exact="null"/>
        <set_value name="$CurrentSpeaker" exact="null"/>
        <set_value name="$CurrentPriority" exact="-1"/>
        <set_value name="$LastSpeakTime" exact="-1s"/>
        <set_value name="$LastSpeakTemplate" exact="null"/>
        <set_value name="$LastLaunchSpeakTime" exact="-1s"/>
        <set_value name="$LastPodSpeakTime" exact="-1s"/>
        <set_value name="$LastFightSpeakTime" exact="-1s"/>
        <set_value name="$LastFightSpeakLib" exact="null"/>

        <set_value name="$MarineCommanderTemplate" exact="null"/>
        <set_value name="$MarineCommanderPage" exact="null"/>
        <set_value name="$MarineCommanderShip" exact="null"/>
      </patch>

      </cues>
    </cue>
    
converted to yaml:

Code: Select all

cue: 
 conditions: 
  event_boarding_operation_created: ""
 actions: 
  set_value: 
   - 
   _name: "$DebugChance"
    _exact: 0
   - 
   _name: "$Operation"
    _exact: "event.param"
   - 
   _name: "$CurrentPhase"
    _exact: null
   - 
   _name: "$Target"
    _exact: "$Operation.boardee"
   - 
   _name: "$PlayerInvolved"
    _exact: false
   - 
   _name: "$MissionCue"
    _exact: OperationCreated
   - 
   _name: "$HasStartedInfiltration"
    _exact: false
    _comment: "Helper value to state if the infiltration phase has been reached and any straggler marines can immediately cut through the hull"
   - 
   _name: "$HasStartedInternalFight"
    _exact: false
   - 
   _name: "$InfiltratingMarineProgress"
    _exact: "table[]"
    _comment: "Table of infiltrating marine templates and their progress percentage"
   - 
   _name: "$PreInfiltrationMaxProgress"
    _exact: "15.0f"
   - 
   _name: "$AllInfiltrated"
    _exact: false
   - 
   _name: "$AttackingTeams"
    _exact: "[]"
   - 
   _name: "$NumDefendersKilled"
    _exact: 0
   - 
   _name: "$PlacedSpeaker"
    _exact: null
   - 
   _name: "$CurrentSpeaker"
    _exact: null
   - 
   _name: "$CurrentPriority"
    _exact: "-1"
   - 
   _name: "$LastSpeakTime"
    _exact: "-1s"
   - 
   _name: "$LastSpeakTemplate"
    _exact: null
   - 
   _name: "$LastLaunchSpeakTime"
    _exact: "-1s"
   - 
   _name: "$LastPodSpeakTime"
    _exact: "-1s"
   - 
   _name: "$LastFightSpeakTime"
    _exact: "-1s"
   - 
   _name: "$LastFightSpeakLib"
    _exact: null
   - 
   _name: "$MarineCommanderTemplate"
    _exact: null
   - 
   _name: "$MarineCommanderPage"
    _exact: null
   - 
   _name: "$MarineCommanderShip"
    _exact: null
  debug_text: 
   _text: "player.age + ' Boarding operation created ' + event.param"
  create_group: 
   - 
   _groupname: "$ApproachingBoardingPods"
   - 
   _groupname: "$AttachedBoardingPods"
 patch: 
  - 
   set_value: 
    - 
   _name: "$PlayerInvolved"
     _exact: false
    - 
   _name: "$MissionCue"
     _exact: OperationCreated
   _sinceversion: 2
  - 
   set_value: 
    - 
   _name: "$AttackingTeams"
     _exact: "[]"
    - 
   _name: "$NumDefendersKilled"
     _exact: 0
    - 
   _name: "$PlacedSpeaker"
     _exact: null
    - 
   _name: "$CurrentSpeaker"
     _exact: null
    - 
   _name: "$CurrentPriority"
     _exact: "-1"
    - 
   _name: "$LastSpeakTime"
     _exact: "-1s"
    - 
   _name: "$LastSpeakTemplate"
     _exact: null
    - 
   _name: "$LastLaunchSpeakTime"
     _exact: "-1s"
    - 
   _name: "$LastPodSpeakTime"
     _exact: "-1s"
    - 
   _name: "$LastFightSpeakTime"
     _exact: "-1s"
    - 
   _name: "$LastFightSpeakLib"
     _exact: null
    - 
   _name: "$MarineCommanderTemplate"
     _exact: null
    - 
   _name: "$MarineCommanderPage"
     _exact: null
    - 
   _name: "$MarineCommanderShip"
     _exact: null
   _sinceversion: 3
 cues: 
  cue: 
   - 
   conditions: 
     event_cue_signalled: ""
    actions: 
     assert: 
      _value: "event.param != $Operation.boardingphase"
      _text: "'ChangePhase called with the same phase as the current phase. Phase: ' + event.param + ' [Owen]'"
     debug_text: 
      _text: "player.age + ' Boarding operation ' + $Operation + ' Changing phase from ' + $Operation.boardingphase + ' to ' + event.param"
      _chance: "$DebugChance"
     do_if: 
      - 
   debug_text: 
        _text: "player.age + ' Boarding operation ' + $Operation + ' Stopping boarding cue ' + $CurrentPhase"
        _chance: "$DebugChance"
       cancel_cue: 
        _cue: "$CurrentPhase"
       set_value: 
        _name: "$CurrentPhase"
        _exact: null
       _value: "$CurrentPhase"
      - 
   signal_cue: 
        _cue: Phase_Pre_Approach
       _value: "event.param == boardingphase.pre_approach"
     set_boarding_phase: 
      _operation: "$Operation"
      _phase: "event.param"
     do_elseif: 
      - 
   signal_cue: 
        _cue: Phase_Approach
       _value: "event.param == boardingphase.approach"
      - 
   signal_cue: 
        _cue: Phase_Pre_Infiltration
       _value: "event.param == boardingphase.pre_infiltration"
      - 
   signal_cue: 
        _cue: Phase_Infiltration
       _value: "event.param == boardingphase.infiltration"
      - 
   signal_cue: 
        _cue: Phase_Internal_Fight
       _value: "event.param == boardingphase.internalfight"
     do_else: 
      assert: 
       _value: false
       _text: "'Boarding phase' + event.param + ' does not have a cue handler [Owen]'"
    _name: ChangePhase
    _instantiate: true
   - 
   conditions: 
     event_object_signalled: 
      _object: "$Target"
      _param: "$Operation"
      _param2: "'boarding__podstarted'"
    actions: 
     debug_text: 
      _text: "'Boarding operation: ' + $Operation + ' - Boarding pod launched ' + event.param3"
      _chance: "$DebugChance"
     add_to_group: 
      _groupname: "$ApproachingBoardingPods"
      _object: "event.param3"
    _name: ApproachingBoardingPodLaunched
    _instantiate: true
   - 
   conditions: 
     event_object_attacked: 
      _group: "$ApproachingBoardingPods"
     check_value: 
      _value: "$PlayerInvolved and player.age ge ($LastPodSpeakTime + 10s) and not event.param.isplayerowned"
    actions: 
     set_value: 
      _name: "this.$Pod"
      _exact: "event.object"
    cues: 
     cue: 
      delay: 
       _exact: 1s
      actions: 
       do_if: 
        set_value: 
         - 
   _name: "$LastPodSpeakTime"
          _exact: "player.age"
         - 
   _name: "$PossibleSpeaks"
          _exact: "[]"
         - 
   _name: "$Pod"
          _exact: "parent.$Pod"
        include_actions: 
         _ref: Speak__Pod_Hit
        do_if: 
         signal_cue_instantly: 
          _cue: Speak_Helper
          _param: "$PossibleSpeaks.random"
         _value: "$PossibleSpeaks.count"
        _value: "parent.$Pod.isoperational and player.age ge ($LastPodSpeakTime + 10s)"
      _name: ApproachingBoardingPodHit_Delay
    _name: ApproachingBoardingPodHit
    _instantiate: true
   - 
   conditions: 
     event_object_destroyed: 
      _group: "$ApproachingBoardingPods"
    actions: 
     set_value: 
      - 
   _name: "this.$Pod"
       _exact: "event.object"
      - 
   _name: "this.$Marines"
       _exact: "this.$Pod.people.marines.list"
     debug_text: 
      _text: "'Boarding operation: ' + $Operation + ' - Boarding pod killed ' + this.$Pod"
      _chance: "$DebugChance"
     do_all: 
      debug_text: 
       _text: "'Pod destroyed. Adding ' + this.$Pod.people.{this.$Marines.{$i}}.name + ' from pod ' +  this.$Pod + ' to the operation as an \\'killed\\' marine'"
       _chance: "$DebugChance"
      transfer_marine_to_operation: 
       _operation: "$Operation"
       _object: "this.$Pod"
       _template: "this.$Marines.{$i}"
       _status: killed
      _exact: "this.$Marines.count"
      _counter: "$i"
     remove_from_group: 
      _group: "$ApproachingBoardingPods"
      _object: "this.$Pod"
     do_if: 
      - 
   set_value: 
        - 
   _name: "$LastPodSpeakTime"
         _exact: "player.age"
        - 
   _name: "$PossibleSpeaks"
         _exact: "[]"
        - 
   _name: "$Pod"
         _exact: "this.$Pod"
       include_actions: 
        _ref: Speak__Pod_Destroyed
       do_if: 
        signal_cue_instantly: 
         _cue: Speak_Helper
         _param: "$PossibleSpeaks.random"
        _value: "$PossibleSpeaks.count"
       _value: "$PlayerInvolved and player.age ge ($LastPodSpeakTime + 5s)"
      - 
   debug_text: 
        _text: "'all boarding pods destroyed. ending operation'"
        _chance: "$DebugChance"
       signal_cue: 
        _cue: Boarding_Failed
       _value: "$ApproachingBoardingPods.count lt 1 and $AttachedBoardingPods.count lt 1"
    _name: ApproachingBoardingPodDestroyed
    _instantiate: true
   - 
   conditions: 
     event_object_signalled: 
      _object: "$Target"
      _param: "$Operation"
      _param2: "'boarding__podattached'"
    actions: 
     set_value: 
      - 
   _name: "this.$Pod"
       _exact: "event.param3"
      - 
   _name: "this.$Marines"
       _exact: "this.$Pod.people.marines.list"
     debug_text: 
      _text: "'Boarding operation: ' + $Operation + ' - Boarding pod attached ' + this.$Pod"
      _chance: "$DebugChance"
     remove_from_group: 
      _group: "$ApproachingBoardingPods"
      _object: "this.$Pod"
     add_to_group: 
      _groupname: "$AttachedBoardingPods"
      _object: "this.$Pod"
     do_if: 
      - 
   set_value: 
        - 
   _name: "$LastPodSpeakTime"
         _exact: "player.age"
        - 
   _name: "$PossibleSpeaks"
         _exact: "[]"
        - 
   _name: "$Pod"
         _exact: "this.$Pod"
       include_actions: 
        _ref: Speak__Pod_Arrived
       do_if: 
        signal_cue_instantly: 
         _cue: Speak_Helper
         _param: "$PossibleSpeaks.random"
        _value: "$PossibleSpeaks.count"
       _value: "$PlayerInvolved and player.age ge ($LastPodSpeakTime + 5s)"
      - 
   append_to_list: 
        _name: "$AttackingTeams"
        _exact: "table[$marines = table[]]"
       do_all: 
        debug_text: 
         _text: "'Adding ' + this.$Pod.people.{this.$Marines.{$i}}.name + ' from pod ' +  this.$Pod + ' to the operation as an \\'infiltrating\\' marine.\\n Number of marines now infiltrating: ' + ($Operation.marines.infiltrating.count + 1)"
         _chance: "$DebugChance"
        set_value: 
         _name: "$AttackingTeams.{$AttackingTeams.count}.$marines.{this.$Marines.{$i}}"
         _exact: "this.$Pod.people.{this.$Marines.{$i}}.combinedskill + 100"
        transfer_marine_to_operation: 
         _operation: "$Operation"
         _object: "this.$Pod"
         _template: "this.$Marines.{$i}"
         _status: infiltrating
        _exact: "this.$Marines.count"
        _counter: "$i"
       debug_text: 
        _text: "'Attacking team #' + $AttackingTeams.count + ' has ' + $AttackingTeams.{$AttackingTeams.count}.$marines.keys.count + ' marines'"
        _chance: "$DebugChance"
       _value: "this.$Marines.count"
    _name: ApproachingBoardingPodAttached
    _instantiate: true
   - 
   conditions: 
     event_boarding_operation_started: 
      _operation: "$Operation"
    actions: 
     debug_text: 
      _text: "player.age + ' Boarding operation started ' + event.param + ' ' + $Operation.boardingphase + ' target: ' + $Target"
      _chance: "$DebugChance"
     set_value: 
      _name: "$PlayerInvolved"
      _exact: false
     do_all: 
      do_if: 
       set_value: 
        _name: "$PlayerInvolved"
        _exact: true
       break: ""
       _value: "$Operation.attackers.{$i}.isplayerowned"
      _exact: "$Operation.attackers.count"
      _counter: "$i"
     do_if: 
      substitute_text: 
       - 
 ...
 
But really, for the libraries stuff where you have a lot of data, you could use JSON and then js for the action

I'm just glad they use a real language instead of a txt file, I hate games that just use a txt file everywhere with some weird made up language of theirs.

I might play around with this, I've got 15 years experience of using js professionally, and use it at my day job (ecmascript really which is better xD), use a lot of node and an electron app would be sweet because they're cross platform so no more csharp windows only bs lol) https://medium.com/@amitgupta.gwl/fast- ... 34e614d104
EterniaLogic
Posts: 26
Joined: Mon, 20. May 13, 17:49
x4

Re: [TOOL][WIP] Script Editor X2S (XML -> Script -> XML)

Post by EterniaLogic »

YAML is a bit odd, since you are required to keep track of tabbing. It is super easy to mess up with YAML if you do it wrong.
Unfortunately, there is a tag called #text and that tag normally gets counted for some reason It messes up pretty much every operation. My guess is that it is a leading or falling whitespace in the XML files or something.


---

It works pretty well with aiscripts now. Doesn't verify the files yet, but all of the differences between the Input and output XML are correct besides some reordering of parameters in the XML tags.
I want also to add DIFF support for mods since creating a diff is not easy. (DIFF GUI!)
I had a go of it for < cues >, but took a break over the weekend.

Planning on supporting cues, and possibly .xsd files. (.xsd is really hard to reference when there are 20 tags to define a single global variable filling your screen)

I'm not sure about library files, since they are different from cues and aiscripts as they mostly only hold data and not much of the programming.

Possible *.XSD Format ?

Code: Select all

simple labelname(namestring); // Label name
simple interrupt_actionsref(namestring); // Name of a library interrupt condition set

...

simple enum fbeahaviourlookup { // Flight behaviour
	simple string flightbehaviour.default,
	simple string flightbehaviour.none,
	....
}

...
simple commandlookup {
	simple string command.attackenemies(?);
	simple string command.attackobject(?);
}

....
// not sure here... kind of gets confusing with elements that have several inner parameters
element move_target_points(complex position nextposition{ 
			blockingaction {object}
			....		
		});
}
Because I can, I'm here to disturb your mind.

Return to “X4: Foundations - Scripts and Modding”