Join us at the Oolite Anniversary Party -- London, 7th July 2024, 1pm
More details in this thread.

Scripters cove

Discussion and information relevant to creating special missions, new ships, skins etc.

Moderators: winston, another_commander

User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Post by Eric Walch »

Today I found a powerful command I was looking for for some time: "setRoles: myRole".

First of al, oolite has a lot of intended AI commands, but also several commands that are only mend for internal use. Some of them can also be used for scripting when they have no parameters (like: fireECM, fireMissile, fireDirectLaserShot) or only a string parameter (like setRoles: ). The only thing is that the author could change the names or behavior of these internal commands at will. But this is not likely for most of them.

From these commands I just today thought at the use of "setRoles:" As I suspected It works in the launch_actions of a ship and in the AI of a ship. (I tested both)

In launch_actions it is very useful when you want to add a special ship, but should interact with the system like a trader. The populater does the same. When it needs a sunskim-trader it adds one but then changes the role to trader so it will be found by pirates like normal traders. I think e.g. that several liners, fuel-transportships and more that are around would also benefit from this. And this method does not deed a new version of oolite as this will work with most current versions.

So adding a ship adding with your private role makes sure the right ship is added to the system and adding in launch_actions of a ship the command: setRoles: trader will make sure the ship has a role of trader after adding to the universe.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

An interesting thing about undocumented methods is that they might not be there in future.

For instance, setRoles: is gone in the trunk, because the role handling was severely broken and has been replaced with a new model of multiple roles + a primary role. In part, this was done to support precisely the type of thing you’re advocating, except the method to use will be setPrimaryRole:.
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Post by Eric Walch »

Ahruman wrote:
An interesting thing about undocumented methods is that they might not be there in future.
I know this as I said in my message. But as this command is so often used throughout the internal code I assumed it would survive the new role handling that was incorporated. Using the old name however would make it possible to write scripts were this rolechanging would work with old as new oolite versions.
User avatar
Arexack_Heretic
Dangerous Subversive Element
Dangerous Subversive Element
Posts: 1876
Joined: Tue Jun 07, 2005 7:32 pm
Location: [%H] = Earth surface, Lattitude 52°10'58.19"N, longtitude 4°30'0.25"E.
Contact:

Post by Arexack_Heretic »

Question:
I am mixing plist- and JS-scripts in a single OXP,
do JS defined missionVariables get a mission_ prefix automatically when interpreted by the legacy-plist parser?

or do I need to define the parameters with the (in JS not needed) prefix?



I remember that the oolite-legacy-trumble-misionvariables are querried by the JS-script as
missionVariable.trumbles not missionVariable.mission_trumbles.
ed: although I cannot find the reference anymore now.

Maybe it was in the wiki as an example...edit: from the wiki:
This is how the script can get and set mission variables whose values are made persistent in the save game file. To get the value of a mission variable use syntax like missionVariables["mymission_status"], where “mymission_status” is the mission variable name. To set the value of a mission variable just assign a new value like missionVariables["mymission_status"] = "MYMISSION_STARTED". Note that unlike plist scripts, no “mission_” prefix is required; “mymission_status” in JavaScript is equivalent to “mission_mymission_status” in a plist.
Riding the Rocket!
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

You appear to have found your answer. :-)
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Post by Eric Walch »

I need some layout hints for the wiki "OXP howto AI" page. I want to split the messages in two groups.

In short. Only a few understand how the statemachine works. The wiki article is a stub of two lines and I want to give it more explanation. A lot of people (including me some months ago) don't know how the timing and "pauseAI" works. In practice it is very simple.
Whitin a state there is by default a short update time of 0.125 seconds. Whenever it executes a pauseAI, this time is added to the next-updatetime. The line itself is not paused.

When the state receives a message it is added to the stack, but does nothing with it. On the next updatetime it reads out the stack and executes all messages that are appropriate. Then clears the stack.

But there are other messages that have priority and are executed immediately and don't wait till the next update. The wiki now mentions the existence of both types but does not tell witch one is witch.

My idea is to display the priority messages in boldface and the normal in italics. The ones that are unclear (= not sorted out yet) stay in plain writing. Or has someone better ideas with quotation or colors?

EDIT: Updated http://wiki.alioth.net/index.php/State_machine
EDIT2: Added an example script to the stateMachine explanation to make things more clear. Not everybody realizes that the state still executes its remaining commands even when it has received a setStateTo: command.
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Post by Eric Walch »

McLane,

I was just looking at your Anarchie.oxp (v0.1) and remembering a previous message I send you a few months ago. Maybe interesting for other too.

Your script tries to save the legal status just before jumping. As you know by now, leaving witchspace is not registered by the script. (it will be when programmed in javascript). Butsome week ago I noticed a undocumented feature in the code. I already placed it in the wiki under "How to OXP AI". Whenever the player makes a jump, the system sends ALL ships the message: "PLAYER WITCHSPACE". I didn't know what NPC ships can do with it as the old system is about to disappear.

Looking at your script I think it can be used. Make sure you have a ship or rock that checks for this condition. It could be a ship far away from the space lanes. This ship then could execute the legacy script for a last time. This way the IN_FLIGHT condition is checked just before the jump and your legal status is always updated properly.

There are two ways to do this.
1) Do a scriptActionOnTarget. Just a dummy action is enough, but you need a target. After this command the legacy script is started automatical to update changes.
2) You also can call the legacy scripting directly with "checkScript". This is a non-official function that starts the legacy scripting engine. I use it in a death_action and it works at least with versions until 1.69.1 (1.70 not yet tested). Maybe we must Ahruman ask to declare it an official function so that the name and method of it will never change.

You could even set a specific mission_ variable with the scriptaction that signals this is the last IN_FLIGHT condition before the jump.

Code: Select all

{
    GLOBAL = {
        ENTER = (performIdle); 
        EXIT = (); 
	"PLAYER WITCHSPACE" = (setTargetToSystemStation, "scriptActionOnTarget: set: mission_my_witchspace_exit [%H]");
        UPDATE = (); 
    }; 
}
and than in the legacy script

Code: Select all

        {
            conditions = ("status_string equal STATUS_IN_FLIGHT"); 
            do = (
                {
                    conditions = ("mission_my_witchspace_exit undefined"); 
                    do = (); 
                    else = (
                        "consoleMessage6s: EXITTING WITCHSPACE  [mission_my_witchspace_exit] ([%H])", 
                        "reset: mission_my_witchspace_exit"
                    ); 
                }
            ); 
        }, 
I tested this under 1.65 and 1.70 and both work. It just confused me at first as the consolemessage only came some time after I entered the new system. Just as if the script did not run directly. But the printed text was correct. "mission_my_witchspace_exit" displayed the name of the old system, as "%H" displayed the current system name. So with "scriptActionOnTarget: set: mission_my_witchspace_exit [legalStatus_number]" the variable mission_my_witchspace_exit will contain the actual legal status just before the jump.
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Post by Eric Walch »

One thing left me bothering about my last message: Why did I get the message, triggered by "player witchspace" only in the new system. The script did a in flight check.

Suddenly, when I was doing something completely else, it popped into my mind: EXITTING_WITCHSPACE. I had to test it this morning, and yes, McLane was its time far ahead with checking this condition.

Till now I have seen two OXP's that were trying to do something when condition "status_string equal STATUS_ENTERING_WITCHSPACE". This is wrong as the scripting never runs during this condition. But, the message "player witchspace" IS send to all ships at this moment. So when you trigger the scripting engine at this moment as described in my previous message, the script runs in condition STATUS_ENTERING_WITCHSPACE.

So, setting up variables is not necessary. Just checking "status_string equal STATUS_ENTERING_WITCHSPACE" will do. This check that was inside anarchie v0.1 doing nothing, suddenly does it thing as originally was intended. The only thing the dummy asteroid needs to do it use the command "checkScript" at the right moment.

Im just hoping this unofficial function will stay in place with all the changes in scripting.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

I don’t have any intention of breaking that, although of course it might happen. It would actually be nice to have an extensive test suite for the old scripting system, but that’d have to be written by someone actually familiar with it… nudge, nudge.

It would be remiss of me not to point out that JavaScript world scripts can get an equivalent notification by implementing a shipWillEnterWitchspace() event handler. (Hmm, I really should update the event handler docs on the wiki, what with the changed names and stuff.)
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Post by Eric Walch »

Ahruman wrote:
It would actually be nice to have an extensive test suite for the old scripting system, but that’d have to be written by someone actually familiar with it… nudge, nudge.
I agree. I already tried to do some tests on the timing of the old scripting, but this failed. The ships-clock is not updated during script execution, thus always giving a 0.0000 sec time result.

The way the legacy script works horrifies me since I really understood how it works. 99% of the script execution is a waist of time. The only way to limit the amount of executed script is to bury most of it behind conditions that are normally false. Only a few do this very well. Svengalli is among them: Not checking a lot of conditions to check which message to show, but just setting a flag and do one check that a message must be shown. An if yes, only than check witch message to show.

But most programmers don't bother about execution time, so it would be useful to have a tool than measures the total script-time so programmers can compare their work against a standard reference. I tried setting the script-timer at the start and reading it out at the end, always resulting in zero because it is not updated during script-time. What's the function of a timer when it can't time? It probably only times the time between script execution.

The way javascript works, makes it sure that only that code is evaluated that is necessary. It's much better for execution speed. When I would start a new UPS-courier, I would do it in JS, but the main part of the oxp I wrote before I new this fine board and without documentation. (Just browsing with a text-editor through oolite itself and finding text pieces that could be commands.) But for now I want this program stay compatible with 1.65 commands and for this program will not use new features.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

Eric Walch wrote:
Ahruman wrote:
It would actually be nice to have an extensive test suite for the old scripting system, but that’d have to be written by someone actually familiar with it… nudge, nudge.
I agree. I already tried to do some tests on the timing of the old scripting, but this failed. The ships-clock is not updated during script execution, thus always giving a 0.0000 sec time result.

The way the legacy script works horrifies me since I really understood how it works. 99% of the script execution is a waist of time. The only way to limit the amount of executed script is to bury most of it behind conditions that are normally false. Only a few do this very well.
Yep. This is one of the major motivations for the JavaScript transition: event handlers are much, much more efficient. The JS interpreter should run code much faster, too, but that’s less important; the fastest code is always code that isn’t run.
But most programmers don't bother about execution time, so it would be useful to have a tool than measures the total script-time so programmers can compare their work against a standard reference.
While that would probably be useful, I was referring to a functionality test for regression testing, i.e. something to tell me when I’ve broken stuff. But yes, a benchmark option would probably be doable.
I tried setting the script-timer at the start and reading it out at the end, always resulting in zero because it is not updated during script-time. What's the function of a timer when it can't time? It probably only times the time between script execution.
The script timer runs in game real time, which updates once per simulation frame (or “tick”). This is normal, near universal, in games. There is almost no context where using the actual time, rather than a per-tick time, is the right thing – except benchmarking.
User avatar
Svengali
Commander
Commander
Posts: 2370
Joined: Sat Oct 20, 2007 2:52 pm

Post by Svengali »

But does it make sense to write something for the 'old' scripting method?
I think most of the scripters will do any new work with JS. And it seems that JS is not so complicated - even if you haven't done anything with it before (like me). And to 'translate' old scripts should be possible after studying some tutorials and online-references. There is a lot of stuff online and I think that Ahruman, Eric and some other experienced ones can answer a lot of questions.
User avatar
Eric Walch
Slightly Grand Rear Admiral
Slightly Grand Rear Admiral
Posts: 5536
Joined: Sat Jun 16, 2007 3:48 pm
Location: Netherlands

Post by Eric Walch »

Talking about a waist of execution time. Military.oxp is a good example of this.

Every time script executes al 33 conditions are evaluated. None of them is grouped together. For a mission oxp you remove after finishing this could be acceptable, but military.oxp brings some nice ships you want to keep after finishing. So even when you have finished the OXP, all 33 game conditions keep being evaluated.

On my computer I put them all behind a single condition "MIL_COMPLETE". When complete this is just evaluateting the off-mission encounters. Only when not complete, the whole lot of other conditions get evaluated. This ensures the oxp uses a minimum of time when kept in place after finishing.

I even found a bug in the last line: that should be "mission_military_fiasco equal MIL_DELAY" but it reads "status_string equal MIL_DELAY". This way is never will restart after a delay .

I don't know the author. It is not in my package. But maybe it should get a new update. I could mail him the improved script. (Or someone else that can upload a new complete version). The version on my computer it is also MOP-proof. (compatible with other mission offering oxp's)
User avatar
Svengali
Commander
Commander
Posts: 2370
Joined: Sat Oct 20, 2007 2:52 pm

Post by Svengali »

Eric wrote:
Talking about a waist of execution time.
When I started (it's only 4 month ago) scripting for Oolite, I took a look in a lot of OXPs and that's what i thought too. So I took a paper and wrote down all necessary conditions and splitted the big parts into small ones. That's no magic - just planning and totaly easy. Maybe we should all together work out some optimization for 'old' OXPs. If we can get a 'crew' of people who are willing to do this for the OXPs where the author isn't here anymore, or not able to do this we can get some major speed improvements (even with a lot of installed OXPs). Eric has already done it for some OXPs, I'm willing to join too. Anybody else?

And a second thing. Yesterday I've tested to split the hyperradio.OXP into two OXPs. One with a function library and one with the ingame-used things. It works fine - so maybe we should also make a general OXP-Function-Library that can be used by all scripters. That would be a great improvement for scripting new OXPs.
User avatar
Svengali
Commander
Commander
Posts: 2370
Joined: Sat Oct 20, 2007 2:52 pm

Post by Svengali »

Ok. Here is the first one. I've used something similiar in the hyperradio.oxp and it works fine.

Code: Select all

// Get an random entry from descriptions.plist
function GetDescriptionEntry(entrykey)
{
	let entryname = "["+ExpandDescription(entrykey)+"]";
	let Listofentries = [ExpandDescription(entryname), ExpandDescription(entryname),
		 ExpandDescription(entryname), ExpandDescription(entryname)];
	let entryselect = Math.floor(Math.random() * Listofentries.length);
	let expandedName = Listofentries[entryselect];
	return (expandedName);
}
It's called by:

Code: Select all

GetDescriptionEntry("hyperradio_songlist")
hyperradio_songlist is a key in descriptions.plist and returns an entry from the array.
Post Reply