Back to Index Page generated: Nov 12, 2024, 11:02:04 PM

Expansion Draven

Content

Warnings

  1. http://wiki.alioth.net/index.php/Draven -> 404 Not Found
  2. Low hanging fuit: Information URL exists...
  3. Required Expansions mismatch between OXP Manifest and Expansion Manager at character position 0071 (DIGIT ZERO vs LATIN SMALL LETTER N)
  4. No version in dependency reference to oolite.oxp.phkb.MarketScriptInterface:null

Manifest

from Expansion Manager's OXP list from Expansion Manifest
Description Adds some eye candy and background story to the Ooniverse, a customized Veisin system (in Galaxy 6). Adds some eye candy and background story to the Ooniverse, a customized Veisin system (in Galaxy 6).
Identifier oolite.oxp.IronFist.Draven oolite.oxp.IronFist.Draven
Title Draven Draven
Category Dockables Dockables
Author IronFist, phkb IronFist, phkb
Version 2.0.2 2.0.2
Tags
Required Oolite Version
Maximum Oolite Version
Required Expansions
  • oolite.oxp.phkb.MarketScriptInterface:0
  • oolite.oxp.phkb.MarketScriptInterface:
  • Optional Expansions
    Conflict Expansions
    Information URL https://wiki.alioth.net/index.php/Draven_OXP n/a
    Download URL https://wiki.alioth.net/img_auth.php/8/8a/Draven_2.0.oxz n/a
    License CC-BY-NC-SA 3.0 CC-BY-NC-SA 3.0
    File Size n/a
    Upload date 1724719822

    Documentation

    Readme.txt

    Draven.oxp by Ironfist 
    Updated by phkb.
    
    ----------------------------------------------------------------------------------------------------------
    Changelog:
    
    V2.0.2 -- 26/08/2024 Added a description to the manifest file.
    V2.0.1 -- 24/05/2024 Small tweaks to shipdata.plist
    V2.0 -- 11/05/2024 Fully updated to Oolite 1.90
        - Added planet textures for Veisin.
        - Refreshed all ship and station textures, added normal and specular maps.
        - Fixed errors in shipdata.
        - Switched Gunship model to be a duplicate of Raider, as there were errors in the texture mapping that could not be resolved.
        - Trader is included in package, but will not be spawned.
    V1.0 -- 09/11/2011 initial version.
    
    ----------------------------------------------------------------------------------------------------------
    
    This work is licensed under the Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License.
    To view a copy of this license, visit:
    
    http://creativecommons.org/licenses/by-nc-sa/3.0/
    
    or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. 
    
    You may not use any part of this in any commercial product, and it may not be distributed in any commercial product even if included "at no cost"

    Equipment

    Name Visible Cost [deci-credits] Tech-Level
    Manufacturer's Tag yes 0 1+
    Draven Ship Systems no 0 1+
    Turret Switcher yes 0 1+

    Ships

    Name
    Draven Big Turret
    Draven Carrier
    Draven Carrier
    Draven Carrier Dock
    Draven Gunship
    Draven Gunship
    Draven Gunship Turret
    Draven Raider
    Draven Raider
    Draven Shuttle
    Draven Shuttle
    Draven Oodulldoff Station
    draven_station_dock
    Draven Trader
    Draven Trader
    Draven Turret
    Draven Turret
    Kracken
    Kracken

    Models

    This expansion declares no models. This may be related to warnings.

    Scripts

    Path
    Scripts/draven_conditions.js
    "use strict";
    this.name        = "Draven_Conditions";
    this.author      = "phkb";
    this.copyright   = "2024 phkb";
    this.description = "Condition script for ships and equipment.";
    this.licence     = "CC BY-NC-SA 3.0";
    
    //-------------------------------------------------------------------------------------------------------------
    this.allowAwardEquipment = function (equipment, ship, context) {
        if (context === "scripted") return true;
        return false;
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.allowOfferShip = function(shipKey) {
        if (system.ID == 25 && galaxyNumber == 5 && player.ship.docked && player.ship.dockedStation.dataKey == "draven_station") return true;
        return false;
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.allowSpawnShip = function(shipKey) {
        // always allow the player ship
        if (shipKey.indexOf("-player") >= 0) return true;
        switch (shipKey) {
            case "draven_raider":
            case "draven_gunship":
            case "draven_trader":
            case "draven_shuttle":
            case "draven_carrier":
            case "kracken":
                if (galaxyNumber == 5) return true;
        }
        return false;
    }
    Scripts/draven_market.js
    "use strict";
    this.name = "Draven_Markets";
    this.author = "phkb";
    this.copyright = "2024 phkb";
    this.license = "CC BY-NC-SA 4.0";
    
    this.$originalDefs = {
        "oodulldoff1": {
            "food": [0, 0, 20, -2, 0, 0, 3, 0, 0],
            "textiles": [0, 0, 20, -1, 0, 0, 3, 0, 0],
            "radioactives": [0, 0, 42, -3, -3, 1, 7, 15, 0],
            "slaves": [0, 0, 40, -5, 0, 0, 31, 0, 0],
            "liquor_wines": [0, 0, 105, -5, 0, 0, 15, 0, 0],
            "luxuries": [0, 0, 190, 8, 0, 0, 9, 0, 0],
            "narcotics": [0, 0, 235, 29, 0, 0, 120, 0, 0],
            "computers": [0, 0, 140, 14, 0, 0, 15, 0, 0],
            "machinery": [0, 0, 120, 6, 0, 0, 15, 0, 0],
            "alloys": [0, 0, 70, 1, 0, 10, 15, 15, 0],
            "firearms": [0, 0, 100, 13, 0, 0, 31, 0, 0],
            "furs": [0, 0, 160, -9, 0, 0, 63, 0, 0],
            "minerals": [0, 0, 16, -1, -3, 10, 3, 31, 0],
            "gold": [0, 0, 73, -1, -1, 5, 7, 3, 1],
            "platinum": [0, 0, 145, -2, -2, 6, 31, 7, 1],
            "gem_stones": [0, 0, 25, -1, -1, 5, 15, 10, 2],
            "alien_items": [0, 0, 100, 1, 0, 0, 31, 0, 0]
        },
        "oodulldoff2": {
            "food": [0, 0, 15, -2, 0, 0, 3, 0, 0],
            "textiles": [0, 0, 15, -1, 0, 0, 3, 0, 0],
            "radioactives": [0, 0, 35, -3, -3, 1, 7, 15, 0],
            "slaves": [0, 0, 30, -5, 0, 0, 31, 0, 0],
            "liquor_wines": [0, 0, 105, -5, 0, 0, 15, 0, 0],
            "luxuries": [0, 0, 190, 8, 0, 0, 9, 0, 0],
            "narcotics": [0, 0, 235, 29, 0, 0, 120, 0, 0],
            "computers": [0, 0, 140, 14, 0, 0, 15, 0, 0],
            "machinery": [0, 0, 120, 6, 0, 0, 15, 0, 0],
            "alloys": [0, 0, 50, 1, 0, 10, 15, 15, 0],
            "firearms": [0, 0, 100, 13, 0, 0, 31, 0, 0],
            "furs": [0, 0, 160, -9, 0, 0, 63, 0, 0],
            "minerals": [0, 0, 12, -1, -3, 10, 3, 31, 0],
            "gold": [0, 0, 73, -1, -1, 5, 7, 3, 1],
            "platinum": [0, 0, 145, -2, -2, 6, 31, 7, 1],
            "gem_stones": [0, 0, 25, -1, -1, 5, 15, 10, 2],
            "alien_items": [0, 0, 100, 1, 0, 0, 31, 0, 0]
        },
    };
    
    //-------------------------------------------------------------------------------------------------------------
    this.startUp = function() {
    	var msi = worldScripts.MarketScriptInterface_Main;
    	msi.$addMarketInterface("station_local", "$updateLocalCommodityDefinition", this.name);
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.$updateLocalCommodityDefinition = function (goodDefinition, station) {
    
    	if (!station) return goodDefinition;
        if (station.dataKey != "draven_station") return goodDefinition;
    
    	var key = "oodulldoff1";
        if (player.ship.equipmentStatus("EQ_DRAVEN") === "EQUIPMENT_OK") {
            key = "oodulldoff2";
        }
    
        var commodity = goodDefinition.key;
        if (this.$originalDefs[key] == undefined) return goodDefinition;
        var oldDefs = this.$originalDefs[key][commodity];
    
        if (oldDefs) {
            var market_base_price = oldDefs[2];
            var market_eco_adjust_price = oldDefs[3];
            var market_eco_adjust_quantity = oldDefs[4];
            var market_base_quantity = oldDefs[5];
            var market_mask_price = oldDefs[6];
            var market_mask_quantity = oldDefs[7];
            var market_rnd = Math.floor(Math.random() * 256);
    
            var economy = system.economy;
    
            var price = (market_base_price + (market_rnd & market_mask_price) + (economy * market_eco_adjust_price)) & 255;
            price *= 0.4;
    
            var quantity = (market_base_quantity + (market_rnd & market_mask_quantity) - (economy * market_eco_adjust_quantity)) & 255;
            if (quantity > 127) quantity = 0;
            quantity &= 63;
    
            goodDefinition.price = price * 10;
            goodDefinition.quantity = quantity;
        }
        //no definition found. nullify the goods.
        else {
            goodDefinition.price = 0;
            goodDefinition.quantity = 0;
        }
        return goodDefinition;
    }
    
    Scripts/draven_ship_player.js
    "use strict";
    this.name = "draven_ship_player.js";
    this.author = "Ironfist";
    this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
    this.description = "Ship script for draven player ships";
    this.version = "2.0";
    // based on scripts from Thargoid for the Vortex
    
    this._hairSpace = String.fromCharCode(31);
    this._hairSpaceLength = defaultFont.measureString(this._hairSpace);
    this._ellip = "…";
    
    this.allShipNames = ["Draven Gunship", "Draven Trader", "Draven Carrier", "Kracken", "Draven Shuttle", "Draven Raider"];
    this.shipNames = ["Draven Gunship", "Draven Trader", "Draven Carrier", "Kracken"];
    this.debug = false;
    // General, start and GUI
    
    //-------------------------------------------------------------------------------------------------------------
    this.startUp = function () {
    	if (this.allShipNames.indexOf(player.ship.name) == -1) return;
    	if (this.debug) log(this.name, "Script Name " + this.name);
    
    	if (this.debug) log(this.name, "draven Startup");
    	if (player.ship.equipmentStatus("EQ_DRAVEN") !== "EQUIPMENT_OK") {
    		player.ship.awardEquipment("EQ_DRAVEN");
    	}
    	if (player.ship.subEntities.length > 0) {
    		if (player.ship.equipmentStatus("EQ_DRAVEN_TURRET") !== "EQUIPMENT_OK") {
    			if (this.debug) log(this.name, "draven Set Kit");
    			player.ship.awardEquipment("EQ_DRAVEN_SHIP");
    			player.ship.awardEquipment("EQ_DRAVEN_TURRET");
    		}
    		if (this.debug) log(this.name, "draven Player Script Initialised");
    		if (this.debug) log(this.name, missionVariables.DRAVEN_TURRET);
    		if (missionVariables.DRAVEN_TURRET === null) { 
    			missionVariables.DRAVEN_TURRET = "active"; 
    		}
    		if (this.debug) log(this.name, missionVariables.DRAVEN_TURRET);
    	}
    	this.incompatibleEquipment = ["EQ_REPAIRBOTS_CONTROLLER", "EQ_MIL_INJECTION"];
    	this.oldTarget = "NONE";
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.resetdraven = function () {
    	missionVariables.DRAVEN_TURRET = null;
    	if (this.debug) log(this.name, "draven Reset");
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.playerBoughtNewShip = function (ship) {
    	this.resetdraven();
    
    	if (this.shipNames.indexOf(player.ship.name) >= 0) { // if we just bought a Draven Gunship
    		this.startUp();
    		mission.runScreen({ title: "Ship Specifications (" + player.ship.name + ")", model: player.ship.dataKey });
    		this.AIName = "MAC" + Math.floor((Math.random() * 0.999999) * 10) + Math.floor((Math.random() * 0.999999) * 10) + Math.floor((Math.random() * 0.999999) * 10) + Math.floor((Math.random() * 0.999999) * 10);
    		mission.addMessageText("Designation: " + player.ship.name + " - ");
    		mission.addMessageText("MAC registration: " + this.AIName + "\n");
    		mission.addMessageText(this.$padText("Shipyard: Oodulldoff Corporation Veisin G5", 20) + this.$padText("Special Capabilities:", 11, true));
    		mission.addMessageText(this.$padText("Source: " + player.ship.scriptInfo.draven.infoSource, 20) + this.$padText("Military grade AI", 11, true));
    		mission.addMessageText(this.$padText("Max speed: " + player.ship.maxSpeed, 20) + this.$padText("Damage auto-repair", 11, true));
    		mission.addMessageText(this.$padText("Cargo capacity: " + player.ship.cargoSpaceCapacity + "t", 20) + this.$padText("Missile defense screen", 11, true));
    		mission.addMessageText(this.$padText("Missile capacity: " + player.ship.missileCapacity + " pylons", 20) + this.$padText("Regenerative fuel injection", 11, true));
    		mission.addMessageText(this.$padText("Energy: " + parseInt(player.ship.maxEnergy / 64) + " banks", 20) + this.$padText("Pitch/Roll/Yaw: " + player.ship.maxPitch.toFixed(1) + "/" + player.ship.maxRoll.toFixed(1) + "/" + player.ship.maxYaw.toFixed(1), 11, true));
    		mission.addMessageText("\n\n\nGreetings Commander " + expandDescription("[commander_name]") + ".\n\nI am " + this.AIName + ", the military AI construct of your new ASC " + player.ship.name + " craft, the " + player.ship.shipClassName + ". Self-diagnostics shows all systems functional and ready - I await your command.");
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.turretToggle = function () {
    	if (missionVariables.DRAVEN_TURRET === "active") {
    		if (this.debug) log(this.name, "draven turret active " + player.ship + "::" + player.ship.target);
    		player.ship.restoreSubEntities();
    		if (player.ship && player.ship.target) {
    			var subCounter = player.ship.subEntities.length;
    			if (this.debug) log(this.name, "Setting guns to target.");
    			while (subCounter--) { 
    				player.ship.subEntities[subCounter].target = player.ship.target; 
    			}
    		}
    	} else {
    		if (this.debug) log(this.name, player.ship + "::" + missionVariables.DRAVEN_TURRET);
    		var subCounter = player.ship.subEntities.length;
    		while (subCounter--) { 
    			player.ship.subEntities[subCounter].remove(); 
    		}
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.playerBoughtEquipment = function (equipment) {
    	if (this.shipNames.indexOf(player.ship.name) == -1) return;
    
    	if (this.incompatibleEquipment.indexOf(equipment) != -1) {
    		mission.runScreen({ title: "Incompatible Equipment Detected" });
    		mission.addMessageText("Message from MAC:\n\nUnfortunately Commander " + expandDescription("[commander_name]") + " the " + EquipmentInfo.infoForKey(equipment).name + " that you purchased is incompatible with my systems and cannot be integrated.\n\nI will arrange for it to be returned and for a refund of " + EquipmentInfo.infoForKey(equipment).price + "? to be made into your account.\n\nEnd of Line.");
    		player.credits += EquipmentInfo.infoForKey(equipment).price;
    		player.ship.removeEquipment(equipment);
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.equipmentDamaged = function (equipment) {
    	if (this.shipNames.indexOf(player.ship.name) == -1) return;
    
    	if (player.ship.docked) return;
    
    	if (!this.damageControlTimer || !this.damageControlTimer.isRunning) {
    		var repairTime = 30 + Math.ceil(Math.random() * player.ship.scriptInfo.draven.repairTime);
    		player.consoleMessage("MAC - Regeneration in progress", 6);
    		this.damageControlTimer = new Timer(this, this.repairSystems, repairTime);
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.shipLaunchedFromStation = function (station) {
    	if (this.shipNames.indexOf(player.ship.name) == -1) return;
    
    	this.turretToggle(); // remove turrets if they are de-activated (having been restored on docking to avoid overhaul offer)
    	this.startFuelTimer();
    	this.startMissileTimer();
    	this.repairCheck();
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.shipWillDockWithStation = function (station) {
    	if (this.shipNames.indexOf(player.ship.name) == -1) return;
    
    	player.ship.restoreSubEntities(); // if the turrets are not on the ship on docking, a maintenance overhaul is offered to restore them!
    	this.stopFuelTimer();
    	this.stopMissileTimer();
    
    	if (this.damageControlTimer && this.damageControlTimer.isRunning) this.damageControlTimer.stop();
    
    	player.ship.awardEquipment("EQ_RENOVATION");
    	player.ship.removeEquipment("EQ_RENOVATION"); // as it's a self-repairing ship, should never need a maintenance overhaul...
    }
    
    //-------------------------------------------------------------------------------------------------------------
    /*this.guiScreenChanged = function (to, from) {
    	if (this.shipNames.indexOf(player.ship.name) == -1) return;
    
    	if (from == "GUI_SCREEN_MARKET") {
    		if (this.debug) log(this.name, player.ship.name + "07a");
    		player.consoleMessage(" \n \n \n \n \n \n \n \n", 1); // remove the current bay text if we leave the screen
    	}
    
    	if (from == "GUI_SCREEN_EQUIP_SHIP") {
    		if (this.debug) log(this.name, player.ship.name + "07b");
    		player.consoleMessage(" \n \n \n \n \n \n \n \n", 1); // remove the current bay text if we leave the screen
    	}
    
    	if (to == "GUI_SCREEN_MARKET") {
    		if (from == "GUI_SCREEN_MANIFEST") { // access only from manifest to market
    			if (this.debug) log(this.name, player.ship.name + "07c");
    		} else {
    			if (this.debug) log(this.name, player.ship.name + "07d");
    		}
    	}
    }*/
    
    // Self repair
    
    //-------------------------------------------------------------------------------------------------------------
    this.repairCheck = function () {
    	this.checkSystems();
    	if (this.playerDamagedList.length > 0) {
    		var repairTime = 30 + Math.ceil(Math.random() * player.ship.scriptInfo.draven.repairTime);
    		player.consoleMessage("MAC - Regeneration restarted", 6);
    		this.damageControlTimer = new Timer(this, this.repairSystems, repairTime);
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.checkSystems = function () {
    	this.playerDamagedList = [];
    	var repairCounter = 0;
    	for (repairCounter = 0; repairCounter < player.ship.equipment.length; repairCounter++) {
    		var scriptEq = EquipmentInfo.infoForKey(player.ship.equipment[repairCounter].equipmentKey);
    		if (player.ship.equipmentStatus(player.ship.equipment[repairCounter].equipmentKey) == "EQUIPMENT_DAMAGED"
    			&& ((scriptEq.scriptInfo.IronfistRepairBotChance === undefined
    				|| isNaN(scriptEq.scriptInfo.IronfistRepairBotChance))
    				|| (!isNaN(scriptEq.scriptInfo.IronfistRepairBotChance)
    					&& scriptEq.scriptInfo.IronfistRepairBotChance > 0))
    		) { 
    			this.playerDamagedList.push(player.ship.equipment[repairCounter].equipmentKey); 
    		}
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.repairSystems = function () {
    	this.checkSystems();
    	if (this.playerDamagedList.length == 0) {
    		player.consoleMessage("MAC - Regeneration completed", 5);
    		return;
    	} else {
    		var damagedEquipment = Math.floor(Math.random() * this.playerDamagedList.length); // pick a random element from the list...
    		this.fixedItem = this.playerDamagedList[damagedEquipment]; // ...define the item...
    		this.fixedName = EquipmentInfo.infoForKey(this.fixedItem).name; // define it's screen name
    		this.fixedTech = EquipmentInfo.infoForKey(this.fixedItem).effectiveTechLevel // tech level of the item
    
    		switch (true) {
    			case (EquipmentInfo.infoForKey(this.fixedItem).scriptInfo.IronfistRepairBotChance !== undefined && !isNaN(EquipmentInfo.infoForKey(this.fixedItem).scriptInfo.IronfistRepairBotChance)):
    				this.fixChance = EquipmentInfo.infoForKey(this.fixedItem).scriptInfo.IronfistRepairBotChance; 
    				break;
    			case (this.fixedTech < 9): 
    				this.fixChance = 1; 
    				break;
    			case ((this.fixedTech > 8) && (this.fixedTech < 17)): 
    				this.fixChance = 1 - ((this.fixedTech - 8) / 10); 
    				break;
    			case (this.fixedTech == 99): 
    				this.fixChance = 0.1; 
    				break;
    			default: 
    				this.fixChance = 0.2; 
    				break;
    		}
    	}
    
    	if (Math.random() < this.fixChance) {
    		this.fixItem();
    	} else {
    		var repairTime = 30 + Math.ceil(Math.random() * player.ship.scriptInfo.draven.repairTime);
    		player.consoleMessage("MAC - " + this.fixedName + " regeneration ongoing", 5);
    		this.damageControlTimer = new Timer(this, this.repairSystems, repairTime);
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.fixItem = function () {
    	player.ship.setEquipmentStatus(this.fixedItem, "EQUIPMENT_OK"); // and actually fix the thing!
    	player.consoleMessage("MAC - " + this.fixedName + " regenerated", 5)
    	switch (this.fixedItem) { // specific OXP equipment which need rebooting after fixing, or have other issues.
    		case "EQ_FUEL_INJECTION":
    			this.startFuelTimer();
    			break;
    		case "EQ_FRAME_FUEL_COLLECTOR":
    			if (worldScripts["Fuel Collector"]) { worldScripts["Fuel Collector"].shipLaunchedFromStation(); }
    			break;
    		case "EQ_FRAME_BOUNTY_SCANNER":
    			if (worldScripts["Bounty Scanner"]) { worldScripts["Bounty Scanner"].shipLaunchedFromStation(); }
    			break;
    		case "EQ_EEU":
    			if (worldScripts["Emergency Energy Unit"]) { worldScripts["Emergency Energy Unit"].shipLaunchedFromStation(); }
    			break;
    		case "EQ_ROCKHERMIT_SCANNER":
    			if (worldScripts["rockHermit_Locator"]) { worldScripts["rockHermit_Locator"].shipLaunchedFromStation(); }
    			break;
    	}
    
    	if (this.playerDamagedList.length > 1) {
    		var repairTime = 30 + Math.ceil(Math.random() * player.ship.scriptInfo.draven.repairTime);
    		player.consoleMessage("MAC - Further regeneration required, working", 6);
    		this.damageControlTimer = new Timer(this, this.repairSystems, repairTime);
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.startMissileTimer = function () {
    	if (player.ship.docked) return;
    	if (this.missileWatchTimer) { 
    		this.missileWatchTimer.start(); 
    	} else { 
    		this.missileWatchTimer = new Timer(this, this.checkMissileBays, 0, 1); 
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.stopMissileTimer = function () {
    	if (this.missileWatchTimer && this.missileWatchTimer.isRunning) this.missileWatchTimer.stop();
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.checkMissileBays = function () {
    	if (!player.ship || !player.ship.isValid) {
    		this.stopMissileTimer();
    		return;
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.shipFiredMissile = function (missile, target) {
    	if (this.shipNames.indexOf(player.ship.name) == -1) return;
    
    	if (this.missileWatchTimer && !this.missileWatchTimer.isRunning) {
    		this.startMissileTimer();
    	}
    }
    
    // Defense System
    
    //-------------------------------------------------------------------------------------------------------------
    this.shipAttackedWithMissile = function (missile, whom) {
    	if (this.shipNames.indexOf(player.ship.name) == -1) return;
    
    	if (missile.scanClass != "CLASS_MISSILE" || (this.defenseTimer && this.defenseTimer.isRunning) || (whom && whom.isPlayer)) return;
    
    	if (missile) {
    		this.missileData = missile; 
    	} else {
    		this.missileData = null; 
    	}
    	if (whom) {
    		this.whomData = whom;
    	} else {
    		this.whomData = null;
    	}
    	player.consoleMessage("MAC - Defense screen activated.", 2);
    	this.defenseTimer = new Timer(this, this.defendMissile, 1, 1);
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.defendMissile = function () {
    	if (!this.missileData || !this.missileData.isValid) {
    		this.defenseTimer.stop();
    		delete this.defenseTimer;
    		this.missileData = null;
    		this.whomData = null;
    		this.missileScan();
    		return;
    	}
    
    	if (player.ship.position.distanceTo(this.missileData.position) < 5000) {
    		if (player.ship.target && this.oldTarget === "NONE") {
    			this.oldTarget = player.ship.target;
    		}
    		player.ship.target = this.missileData;
    	}
    
    	if (player.ship.position.distanceTo(this.missileData.position) < 2500) { // if missile is within 2.5km or so, try ECM'ing it too.
    		player.ship.fireECM;
    		if (!this.missileData || !this.missileData.isValid) {
    			this.defenseTimer.stop();
    			delete this.defenseTimer;
    			this.missileData = null;
    			this.whomData = null;
    			this.missileScan();
    			return;
    		}
    	}
    
    	this.defenseChance = Math.random();
    	if (this.defenseChance > 0.25 || !this.missileData) return;
    
    	if (this.defenseChance < 0.05 && this.whomData && this.whomData.isValid) {
    		this.missileData.target = this.whomData;
    	} else {
    		this.missileData.target = null;
    		this.missileData.reactToAIMessage("TARGET_LOST");
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.missileScan = function () {
    	function incomingMissiles(entity) { return entity.isMissile && entity.target && entity.target.isPlayer };
    	let targetMissiles = system.filteredEntities(this, incomingMissiles, player.ship, 25600);
    	if (targetMissiles.length > 0 && !this.defenseTimer) {
    		this.missileData = targetMissiles[0];
    		player.ship.target = targetMissiles[0];
    		this.whomData = null;
    		this.defenseTimer = new Timer(this, this.defendMissile, 1, 1);
    	} else {
    		player.consoleMessage("MAC - Defense screen deactivated.", 2);
    		if (this.oldTarget && this.oldTarget != "NONE" && this.oldTarget.isValid && (player.ship.position.distanceTo(this.oldTarget.position) < 25600) && !player.ship.target) { 
    			player.ship.target = this.oldTarget; 
    		}
    		this.oldTarget = "NONE";
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.shipBeingAttacked = function (whom) {
    	if (this.shipNames.indexOf(player.ship.name) == -1) return;
    	if (Math.random() < 0.25 && whom && whom.target && whom.target.isPlayer) {
    		whom.target = null;
    		whom.reactToAIMessage("TARGET_LOST");
    	}
    
    	if (player.ship.energy < 33 && player.ship.fuel > 0) {
    		this.xDistance = ((Math.random() * 2) - 1) * 1024000;
    		this.yDistance = ((Math.random() * 2) - 1) * 1024000;
    		this.zDistance = ((Math.random() * 2) - 1) * 1024000;
    		this.newPosition = player.ship.position.add([this.xDistance, this.yDistance, this.zDistance]);
    
    		let probe = player.ship.spawnOne('splinter');
    		probe.position = this.newPosition;
    		if (probe && probe.isValid) {
    			probe.remove();
    			player.ship.position = this.newPosition;
    			player.ship.fuel -= 0.1;
    			player.consoleMessage("MAC - Emergency displacement activated.", 8);
    		}
    	}
    }
    
    // Military grade fuel injection	
    
    //-------------------------------------------------------------------------------------------------------------
    this.startFuelTimer = function () {
    	this.fuelFlag = 0;
    	if (this.fuelWatchTimer) { 
    		this.fuelWatchTimer.start(); 
    	} else { 
    		this.fuelWatchTimer = new Timer(this, this.checkSpeed, 0, 1); 
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.stopFuelTimer = function () {
    	this.fuelFlag = 0;
    	if (this.fuelWatchTimer && this.fuelWatchTimer.isRunning) this.fuelWatchTimer.stop();
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.checkSpeed = function () {
    	if (player.ship.equipmentStatus("EQ_FUEL_INJECTION") !== "EQUIPMENT_OK") {
    		this.stopFuelTimer();
    		return;
    	}
    
    	if (player.ship.speed == 7 * player.ship.maxSpeed) { // if the ship is under fuel injection (FI is speed x 7, torus is speed x 32)
    		// increment flag, looping between 0 and 9
    		this.fuelFlag += 1;
    		this.fuelFlag = this.fuelFlag % 8;
    	} else 
    		return; // if not under fuel injection, exit function
    
    	if (this.fuelFlag == 7) { // every 8th second under fuel injection
    		player.ship.fuel += 0.1;
    		player.ship.velocity = player.ship.velocity.multiply(1.25);
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    // appends space to currentText to the specified length in 'em'
    this.$padText = function $padText(currentText, desiredLength, leftSwitch, centreSwitch) {
        var that = $padText;
        var hairSpace = (that.hairSpace = that.hairSpace || this._hairSpace);
        var ellip = (that.ellip = that.ellip || this._ellip);
        var hairSpaceLength = (that.hairSpaceLength = that.hairSpaceLength || this._hairSpaceLength);
        var measure = (that.measure = that.measure || defaultFont.measureString);
    
        if (currentText == null) currentText = "";
        var currentLength = measure(currentText.replace(/%%/g, "%"));
        // calculate number needed to fill remaining length
        var padsNeeded = ~~((desiredLength - currentLength) / hairSpaceLength);
        if (padsNeeded < 1) {
            // text is too long for column, so start pulling characters off
            var tmp = currentText;
            do {
                tmp = tmp.substring(0, tmp.length - 2) + ellip;
                if (tmp === ellip) break;
            } while (measure(tmp.replace(/%%/g, "%")) > desiredLength);
            currentLength = measure(tmp.replace(/%%/g, "%"));
            padsNeeded = ~~((desiredLength - currentLength) / hairSpaceLength);
            currentText = tmp;
        }
        // quick way of generating a repeated string of that number
        if (!leftSwitch || leftSwitch === false) {
            if (!centreSwitch || centreSwitch === false) {
                return currentText + new Array(padsNeeded).join(hairSpace);
            } else {
                return currentText + new Array(parseInt(padsNeeded / 2)).join(hairSpace);
            }
        } else {
            if (!centreSwitch || centreSwitch === false) {
                return new Array(padsNeeded).join(hairSpace) + currentText;
            } else {
                return new Array(parseInt(padsNeeded / 2)).join(hairSpace) + currentText;
            }
        }
    }
    
    Scripts/draven_station.js
    /*
    draven_station.js
    
    Script for draven.oxp version 1.0.
    
    Oolite
    Copyright © 2004-2011 Giles C Williams and contributors
    
    This work is licensed under the Creative Commons
    Attribution-Noncommercial-Share Alike 3.0 Unported License.
    
    To view a copy of this license, visit
    http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter
    to Creative Commons, 171 Second Street, Suite 300, San Francisco,
    California, 94105, USA.
    */
    
    "use strict";
    this.name = "draven_station";
    this.description = "Script for draven station";
    this.author = "Ironfist";
    this.copyright = "© 2011 Ironfist";
    this.license = "CC-by-nc-sa 3.0";
    this.version = "1.0.2";
    
    this.debug = false;
    
    //-------------------------------------------------------------------------------------------------------------
    this.startUp = function () {
    	this.draven_station_log = 0;
    	if (this.debug) log(this.name, "Initialising " + player.ship.dockedStation.name);
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.startUpComplete = function() {
    	if (worldScripts["Docking Fees"]) {
    		this._feeTimer = new Timer(this, this.$updateFee.bind(this), 2, 0);
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.$updateFee = function $updateFee() {
    	worldScripts["Docking Fees"].$addFee("draven_st", {fee:0, messageKey:""});
    	worldScripts["Docking Fees"].$addFee("draven_c", {fee:0, messageKey:""});
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.shipWillDockWithStation = function(station) {
    	if (station.dataKey == "draven_station") this.$removeNonDravenShips(station);
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.systemWillPopulate = function() {
    	if (galaxyNumber == 5 && system.ID == 25) {
    		var posDS = Vector3D(0.2, 0.6, 1.5).fromCoordinateSystem("wpu");
    		system.setPopulator("draven_populator", {
    			location: "COORDINATES",
    			coordinates: posDS,
    			deterministic:true,
    			callback: function(pos) {
    				var dr = system.addShips("draven_st", 1, pos, 0);
    				if (!dr || dr.length === 0) {
    					log("draven_populator", "OOPS! No station spawned");
    				} else {
    					var ds = worldScripts.draven_station;
    					ds._station = dr[0];
    					ds._msgTimer = new Timer(ds, ds.$transmitInitialHail.bind(ds), 3, 3);
    					// only put a beacon on the station if the player has fully docked once.
    					if (missionVariables.DravenStation_Docked) {
    						dr[0].beaconCode = "O";
    						dr[0].beaconLabel = "Oodulldoff Station";
    					}
    				}
    			}}
    		);
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.$transmitInitialHail = function $transmitInitialHail() {
    	if (player.ship.docked && player.ship.dockedStation.dataKey == "draven_station") {
    		this._msgTimer.stop();
    		return;
    	}
    	if (player.ship.docked) return; // don't do calcs when docked
    
    	var dist = this._station.position.distanceTo(player.ship);
    	if (dist < player.ship.scannerRange * 0.95) {
    		var msg = "[Oodulldoff-station-hail]";
    		if (player.ship.hasRole("drvn")) {
    			msg = "[Oodulldoff-station-hail-draven]";
    		}
    		this._station.commsMessage(expandDescription(msg), player.ship);
    		this._msgTimer.stop();
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.$removeNonDravenShips = function (station) {
    	var i = station.shipyard.length;
    	while (i--) {
    		if (station.shipyard[i].shipdata_key.indexOf("draven") == -1 && station.shipyard[i].shipdata_key.indexOf("kracken") == -1) {
    			log(this.name, "removing " + station.shipyard[i].shipdata_key);
    			station.removeShipFromShipyard(i);
    		}
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.missionScreenOpportunity = function () {
    	this.missionScreens();
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.missionScreens = function () {
    	if (guiScreen == "GUI_SCREEN_MISSION" || !player.ship.docked) return;
    	if (player.ship.docked && player.ship.dockedStation.hasRole("draven_st") && this.draven_station_log == 0) {
    		if (this.debug) {
    			var shipRoles = "";
    			shipRoles = player.ship.roles;
    			var subCounter = shipRoles.length;
    			log(this.name, "A " + player.ship.name + " docked at " + player.ship.dockedStation.name + " " + player.ship.roles.length);
    			while (subCounter--) { 
    				if (this.debug) log(this.name, shipRoles[subCounter]); 
    			}
    		}
    		this.draven_station_log += 1;
    		// free fuel and entrance if we built the ship
    		if (player.ship.equipmentStatus("EQ_DRAVEN") === "EQUIPMENT_OK") {
    			// display dr_mt_1
    			mission.runScreen({ title: "Oodulldoff Station Welcome", messageKey: "dr_mt_0" });
    			if (this.debug) log(this.name, "Fuel added");
    			player.ship.fuel = 7.0;
    		} else {
    			if (this.debug) log(this.name, "A " + player.ship.name + " docked at " + player.ship.dockedStation.name);
    			// display dr_mt_10
    			mission.runScreen({ title: "Oodulldoff Station Welcome", messageKey: "dr_mt_10", choicesKey: "dr_choices" }, this.choices);
    		}
    	}
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.shipWillLaunchFromStation = function (station) {
    	this.draven_station_log = 0;
    }
    
    //-------------------------------------------------------------------------------------------------------------
    this.choices = function (choice) {
    	switch (choice) {
    		case "1_Yes":
    			if (this.debug) log(this.name, "Yes");
    			player.credits -= 1000;
    			this.showScreen = "firstPage";
    			missionVariables.DravenStation_Docked = true;
    			break;
    		case "2_No":
    			if (this.debug) log(this.name, "No");
    			player.ship.launch();
    			break;
    	}
    }
    Scripts/draven_turretToggle.js
    "use strict";
    this.name = "draven_carrier_turretToggle.js";
    this.author = "Ironfist";
    this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
    this.description = "Script for swapping over turrets from active to inactive and back.";
    this.version = "1.0.0";
    
    /* based on Vortex script by Thargoid */
    
    this.activated = function () {
    	log(this.name, "Turret Toggle script");
    	if (missionVariables.DRAVEN_TURRET === "active") {
    		player.consoleMessage("MAC - turrets are now deactivated.", 6);
    		missionVariables.DRAVEN_TURRET = "inactive";
    	} else {
    		player.consoleMessage("MAC - turrets are now activated.", 6);
    		missionVariables.DRAVEN_TURRET = "active";
    	}
    
    	worldScripts["draven_ship_player.js"].turretToggle();
    }