Page 1 of 2

Missile Beep

Posted: Sun Mar 26, 2017 6:58 pm
by hoqllnq
Greetings Commanders,

Today, I made my first OXP: Missile Beep.
(Edit: updated v1.3 here.)
(Another edit: link updated to point to the file on the wiki. This OXP is now available in the in-app OXP manager.)


It plays high pitched beeps whenever a missile is targeted at the player.

It is extremely small & simple, but it got me to look into what it takes to make a working OXP.
If the OXP is installed, it will just work. It is not buyable equipment. So I categorised it as Ambience.
Everything is based on what I saw in other OXPs.

Please let me know what I've omitted, done wrong, or could improve.

Thanks.

Re: Missile Beep

Posted: Sun Mar 26, 2017 7:07 pm
by Cody
Sweet! Seems to work fine, but threw a couple of these:

Code: Select all

20:05:43.672 [script.javaScript.exception.unexpectedType]: ***** JavaScript exception (Missile Beep 1.0): TypeError: things is undefined
20:05:43.672 [script.javaScript.exception.unexpectedType]:       ../AddOns/oolite.oxp.hoqllnq.missile_beep.oxz/Scripts/missile_beep.js, line 31.

Re: Missile Beep

Posted: Sun Mar 26, 2017 8:01 pm
by hoqllnq
Thanks Commander.

Can you or someone help me out? I'm not that familiar with javascript.

This the function where the exception is thrown:

Code: Select all

this._timer_hook = function()
{
	var things = player.ship.checkScanner(true);
	
	for (var i = 0;i < things.length;i++)	// <-- this is line 31
	{
		if (things[i].isMissile && things[i].target == player.ship)
		{
			this._sound_source.play();
			return;
		}
	}
	
	if (this._sound_source.isPlaying)
		this._sound_source.stop();
}
I was guessing that this happens when the scanner is empty, and checkScanner() returns null. (And therefor I can not reference its 'length' member.) I tested this, but I don't get this exception when my scanner is empty. Do you know when/why it happens / how to reproduce it?

After some more testing, I was able to get this exception by killing myself. So maybe at that time, player == null or player.ship == null?

Adding the following extra checks gets rid of the exception in the suicide case:

Code: Select all

this._timer_hook = function()
{
	var things;
 
	if (player && player.ship)
	{
		things = player.ship.checkScanner(true);
		
		if (things)
		{
			for (var i = 0;i < things.length;i++)
			{
				if (things[i].isMissile && things[i].target == player.ship)
				{
					this._sound_source.play();
					return;
				}
			}
		}
	}
	
	if (this._sound_source.isPlaying)
		this._sound_source.stop();
}
Is this the correct solution? Is my line of thinking correct as to why? Or is it overkill?

An updated OXP with the extra checks is here: oolite.oxp.hoqllnq.missile_beep-1.1.oxz

Thanks.

Re: Missile Beep

Posted: Sun Mar 26, 2017 8:09 pm
by Cody
That seems to have disappeared the error messages - thanks! Cookies in the jar!

Of javascript, I know nothing!

Re: Missile Beep

Posted: Sun Mar 26, 2017 8:21 pm
by hoqllnq
Wow, you're fast. Thank you for testing and confirming.

Re: Missile Beep

Posted: Sun Mar 26, 2017 10:13 pm
by ffutures
Do the beeps speed up as it gets closer? That would be a lovely bit of ambience.

Re: Missile Beep

Posted: Sun Mar 26, 2017 11:06 pm
by Diziet Sma
ffutures wrote: Sun Mar 26, 2017 10:13 pm
Do the beeps speed up as it gets closer? That would be a lovely bit of ambience.
Seconded! That would be very useful, as well as inducing a slight sense of panic in the player! :twisted:

Re: Missile Beep

Posted: Mon Mar 27, 2017 12:06 pm
by hoqllnq
ffutures wrote: Sun Mar 26, 2017 10:13 pm
Do the beeps speed up as it gets closer? That would be a lovely bit of ambience.
In this update, they do: oolite.oxp.hoqllnq.missile_beep-1.2.oxz

The previous version would check for incoming missiles once a second, and if one was found, play a 0.5 second beep.

To make the beep speed depend on missile distance, I had to change "everything":
* Rather than detecting just any incoming missile, I now have to find the nearest one.
* Calculate a beep-interval based on the distance to the nearest incoming missile.
* Register a FCB to turn the beep on/off when the beep-interval has expired.

It now plays 1 beep per second when the missile is at the edge of the scanner range, 10 bps when distance is zero. Maybe these numbers need some tweaking. The 'duty cycle' is always 50%, i.e. the duration of the beeps is half the period.


Edit to add:

And here is version 1.3 --> oolite.oxp.hoqllnq.missile_beep-1.3.oxz
Functionality is the same as V1.2, but I made some code changes. Hopefully improvements...

* Lower the timer interval to 0.5 sec. while missiles are present, so that the change in beeping speed is smoother. (Put it back to 1 sec. when no more incoming missiles are around.)
* Added shipAttackedWithMissile() event handler that calls the timer hook when an incoming missile is fired, so beeping starts immediately instead of at the next timer interval.

This is the entire thing:

Code: Select all

// This is the world script for Missile Beep OXP
"use strict";
this.name        = "Missile Beep";
this.description = "Missile Beep World Script";
this.version     = "1.3";
this.author      = "hoqllnq";
this.copyright   = "2017 hoqllnq";
this.licence     = "CC-BY-NC-SA 3.0";

this._timer = null;         // To periodically check for incoming missiles
this._fcb = null;           // To time the beeps
this._sound_source = null;  // To play the beeps
this._beep_time = 0;        // How long beep (or silence) has been playing
this._beep_interval = 0;    // How long beep (or silence) should last
this._beep_on = false;      // Currently beeping?

this._debug = false;        // Logging


// Initialize the timer and the sound source
this.startUp = function()
{
    if (!this._timer)
    {
        this._timer = new Timer(this, this._timer_hook.bind(this),1,1);
    }
    
    if (!this._sound_source)
    {
        this._sound_source = new SoundSource();
        this._sound_source.loop = false;
        this._sound_source.sound = "[missile_beep]";
    }
    
    if (this._debug)
        log(this.name,"Started");
}

// A missile was fired at us, it could be the nearest one,
// don't wait for timer to fire, call timer hook now to check
this.shipAttackedWithMissile = function(missile,whom)
{
    if (this._debug)
        log(this.name,"Missile fired");
    
    this._timer_hook();
}

// Periodic check for presence and distance of incoming missiles
this._timer_hook = function()
{
    var things;
    var dist;
 
    if (player && player.ship)
    {    // Find all missiles in scanner range
        things = system.entitiesWithScanClass("CLASS_MISSILE",player.ship,25600);
        
        // See if any of them are targeted at us
        for (var i = 0;i < things.length;i++)
        {
            if (things[i].target == player.ship)
            {    // Yes.
                // This is the nearest one, since the list is ordered by distance
                dist = things[i].position.distanceTo(player.ship.position);
                if (this._debug)
                    log(this.name,"Missile at " + dist);
                
                // Beep duration based on distance
                this._beep_interval = 0.05 + dist / 51200.0;
                
                // If we weren't beeping yet, start now
                if(!isValidFrameCallback(this._fcb))
                {
                    this._fcb = addFrameCallback(this._fcb_hook.bind(this));
                    this._timer.interval = 0.5;
                }
                
                return;
            }
        }
    }
    
    // No incoming missiles
    // If we were beeping, stop now.
    if (isValidFrameCallback(this._fcb))
    {
        if (this._debug)
            log(this.name,"No more incoming missiles");
        
        removeFrameCallback(this._fcb);
        this._timer.interval = 1.0;
    }
    
    if (this._sound_source.isPlaying)
        this._sound_source.stop();
    
    this._beep_time = 0;
    this._beep_on = false;
}

// Frame callback to turn beep on/off
// This does the timing for the beeping speed
this._fcb_hook = function(delta)
{
    this._beep_time += delta;
    
    // Time for next beep/silence?
    if (this._beep_time < this._beep_interval)
        return; // No.
    
    // Reset time
    this._beep_time -= this._beep_interval;
    
    // Toggle beep
    if (this._beep_on)
    {
        if (this._sound_source.isPlaying)
            this._sound_source.stop();
        
        this._beep_on = false;
    }
    else
    {
        if (this._debug)
            log(this.name,"Beep!");
        
        if (!this._sound_source.isPlaying)
            this._sound_source.play();
        
        this._beep_on = true;
    }
}
I'm happy with how it works. Testing / playing around with it, I found it quite useful. When you fire at another ship, get the 'incoming missile' warning, but no beeping, you know you've already shot the missile, no need for ECM. Conversely, when multiple ships fire multiple missiles, and you hit your ECM, and the beeping continues, then you know it's time to ECM some more and make evasive manoeuvres. Unless there are problems (or really good ideas), I will leave it like this. It fulfils its original purpose, which was continuous alert for incoming missiles, and with the variable beeping speed (thanks cmdr. ffutures!) it does it even nicer than I originally envisioned.

Re: Missile Beep

Posted: Mon Mar 27, 2017 12:16 pm
by Cody
Cool!

Re: Missile Beep

Posted: Fri May 12, 2017 10:22 pm
by ffutures
Is this on the manager yet, or only manual install?

Re: Missile Beep

Posted: Fri May 12, 2017 11:17 pm
by Cody
Currently manual only, I believe - and damned useful kit it is too!

Re: Missile Beep

Posted: Sat May 13, 2017 3:52 pm
by hoqllnq
I shall go through the steps outlined in the all-in-one guide to add it to the wiki and the manger.

Re: Missile Beep

Posted: Sat May 13, 2017 3:53 pm
by Cody
It'd be a good idea to update the first post with the latest version too.

Re: Missile Beep

Posted: Wed May 17, 2017 7:26 pm
by hoqllnq
This OXP is now available in the manager. It's in the Ambience category.
Edited the first post to state this, and to add a link to the current version for manual installation.
I've also added it to the sortable list. Are there more steps I should take?
Thanks.

Re: Missile Beep

Posted: Wed May 17, 2017 8:50 pm
by Cody
All is good - thanks.