JSDD wrote:
@ TOOL:
would be a great idea to "wrap" the node-tree MD stuff into procedural (and easier to read) code (back and forth without losses) if that is possible (?) ... but who`s gonna write it ?
there are no x4 freaks with programming knowledge around (at release i guess) ...
It is certainly possible. As I see it, there are two problems with the MD as-is: xml makes a bit of a scary mess of things, and there should be a translation layer between the runtime engine and the design time script development. For fun, I spitballed some code to convert MD xml to something slightly more recognizable.
Example input, taken from whatever script I landed on when scrolling through the cat file list for rebirth (trade.performplayertraderun.xml in this case):
Original snippet (skipping some debug stuff that's mostly text prints):
Code: Select all
<?xml version="1.0" encoding="iso-8859-1" ?>
<aiscript name="trade.performplayertraderun" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="aiscripts.xsd" priority="60">
<params>
<param name="debugchance" default="0"/>
</params>
<attention min="unknown">
<actions>
<!-- debugging -->
<set_value name="$failreason" exact="''" />
<wait min="1s" max="2s" />
<!-- get top item from shopping list -->
<label name="check shoppinglist" />
<get_trade_from_shoppinglist object="this.ship" result="$shoppinglist" multiple="true" />
<do_all exact="$shoppinglist.count" counter="$c">
<set_value name="$tradeorder" exact="$shoppinglist.{$c}" />
<do_if value="$tradeorder.seller.exists">
<add_ware_reservation object="$tradeorder.seller" result="$reserved" ware="$tradeorder.ware" entity="this" amount="0" />
<do_if value="$reserved" min="$tradeorder.amount">
<add_ware_reservation object="$tradeorder.seller" ware="$tradeorder.ware" entity="this" amount="$reserved" replace="true" duration="5h" />
</do_if>
<do_else>
<add_ware_reservation object="$tradeorder.seller" ware="$tradeorder.ware" entity="this" amount="$tradeorder.amount" duration="5h" />
</do_else>
</do_if>
<do_elseif value="not $tradeorder.unbundle">
<add_ware_reservation object="$tradeorder.buyer" result="$reserved" ware="$tradeorder.ware" entity="this" amount="0" />
<do_if value="$reserved" min="$tradeorder.amount">
<add_ware_reservation object="$tradeorder.buyer" ware="$tradeorder.ware" entity="this" amount="$reserved" replace="true" duration="5h" />
</do_if>
<do_else>
<add_ware_reservation object="$tradeorder.buyer" ware="$tradeorder.ware" entity="this" amount="$tradeorder.amount" duration="5h" />
</do_else>
</do_elseif>
</do_all>
After a couple hours writing a reformatter:
Code: Select all
@aiscript (priority = 60)
def trade_performplayertraderun (debugchance = 0):
# debugging
failreason = ''
wait (max = "2s", min = "1s")
# get top item from shopping list
label .check_shoppinglist
shoppinglist = get_trade_from_shoppinglist (multiple = True, object = "this.ship")
for c in range(shoppinglist.count):
tradeorder = shoppinglist[c]
if tradeorder.seller.exists:
reserved = add_ware_reservation (amount = 0, entity = "this", object = "tradeorder.seller", ware = "tradeorder.ware")
if reserved:
add_ware_reservation (amount = "reserved", duration = "5h", entity = "this", object = "tradeorder.seller", replace = True, ware = "tradeorder.ware")
else:
add_ware_reservation (amount = "tradeorder.amount", duration = "5h", entity = "this", object = "tradeorder.seller", ware = "tradeorder.ware")
elif not tradeorder.unbundle:
reserved = add_ware_reservation (amount = 0, entity = "this", object = "tradeorder.buyer", ware = "tradeorder.ware")
if reserved:
add_ware_reservation (amount = "reserved", duration = "5h", entity = "this", object = "tradeorder.buyer", replace = True, ware = "tradeorder.ware")
else:
add_ware_reservation (amount = "tradeorder.amount", duration = "5h", entity = "this", object = "tradeorder.buyer", ware = "tradeorder.ware")
Or maybe this looks better:
Code: Select all
@aiscript (priority = 60)
def trade_performplayertraderun (debugchance = 0):
# debugging
failreason = ''
wait (
max = "2s",
min = "1s",
)
# get top item from shopping list
label .check_shoppinglist
shoppinglist = get_trade_from_shoppinglist (
multiple = True,
object = "this.ship",
)
for c in range(shoppinglist.count):
tradeorder = shoppinglist[c]
if tradeorder.seller.exists:
reserved = add_ware_reservation (
amount = 0,
entity = "this",
object = "tradeorder.seller",
ware = "tradeorder.ware",
)
if reserved:
add_ware_reservation (
amount = "reserved",
duration = "5h",
entity = "this",
object = "tradeorder.seller",
replace = True,
ware = "tradeorder.ware",
)
else:
add_ware_reservation (
amount = "tradeorder.amount",
duration = "5h",
entity = "this",
object = "tradeorder.seller",
ware = "tradeorder.ware",
)
elif not tradeorder.unbundle:
reserved = add_ware_reservation (
amount = 0,
entity = "this",
object = "tradeorder.buyer",
ware = "tradeorder.ware",
)
if reserved:
add_ware_reservation (
amount = "reserved",
duration = "5h",
entity = "this",
object = "tradeorder.buyer",
replace = True,
ware = "tradeorder.ware",
)
else:
add_ware_reservation (
amount = "tradeorder.amount",
duration = "5h",
entity = "this",
object = "tradeorder.buyer",
ware = "tradeorder.ware",
)
Of course it needs a fair amount of work on evaluating expressions to pick out nested variables and such, according to MD rules, and a lot of other little things would need sorting out as well; 2-3 hours only covers so much, especially without any experience Rebirth modding.
You might notice the second form is in python syntax. If you slap a framework behind it with various node definitions, code writing can then be done in any nice python IDE (pycharm, visual studio, etc.) with the usual autocompletions and documentation popups and such. Scripts would also be runnable as python code, though initially it would probably just be for some logic checks. Eventually, functional test support could be added.
Conversion from python back to MD xml would be slightly tougher, since it would take some ast parsing, but isn't that big of a deal (python makes it really easy to do ast traversal). All sorts of functionality can be injected into the reverse conversion, like compilation warnings/errors if node fields look wrong, function inlining, debug code injection or pruning, dead code removal, etc.