Back to Index Page generated: Aug 1, 2026, 4:49:40 AM

Expansion Pirate Coves

Content

Manifest

from Expansion Manager's OXP list from Expansion Manifest
Description Adds pirate bases to the system that are situated in rock hermits. Adds pirate bases to the system that are situated in rock hermits.
Identifier oolite.oxp.EricWalch.PirateCove oolite.oxp.EricWalch.PirateCove
Title Pirate Coves Pirate Coves
Category Ambience Ambience
Author Lazy Gun & Eric Walch Lazy Gun & Eric Walch
Version 2.1 2.1
Tags Hermits, Pirates Hermits, Pirates
Required Oolite Version
Maximum Oolite Version
Required Expansions
Optional Expansions
Conflict Expansions
Information URL http://wiki.alioth.net/index.php/Pirate_Coves_OXP n/a
Download URL https://wiki.alioth.net/img_auth.php/4/49/PirateCoves_2.1.oxz n/a
License CC-BY-NC-SA 3.0 CC-BY-NC-SA 3.0
File Size n/a
Upload date 1781984038

Relationships Diagram

Documentation

Also read http://wiki.alioth.net/index.php/Pirate%20Coves

Equipment

This expansion declares no equipment.

Ships

Name
pirate-cove
pirate-cove-chaotic
pirate-cove-pirate
pirate-cove-single

Models

This expansion declares no models.

Scripts

Path
Scripts/pirateCove.js
"use strict";
this.name = "Pirate_Coves";
this.author = "lazygun (JS adaption by EW)";
this.copyright = "lazygun";
this.description = "Adds pirate coves to the system.";
this.version = "2.0";

this._spawning = {
    neutral: {replace:true, chance:0.25}, // should be equivalent to roles = "rockhermit(0.25)";
    chaotic: {replace:false, chance:0.25},
    pirate: {replace:false, chance:0.25},
    single: {
        allowed: true,
        sampleAtDiso: true,
        // max min range calculate
        // if max = 0.3 and min = 0.1, range will be from -0.3 to -0.1 or from +0.1 to +0.3
        xRange: {max: 0.3, min: 0.1},
        yRange: {max: 0.3, min: 0.1},
        zRange: {max: 0.4, min: 0.1}
    }
}

// configuration settings for use in Lib_Config
this._pcConfig = {
	Name: this.name,
	Alias: expandDescription("[piratecoves_alias]"),
	Display: expandDescription("[piratecoves_replace_display]"),
	Alive: "_pcConfig",
	Bool: {
		B0: {Name: "_spawning.neutral.replace", Def: true, Desc: expandDescription("[piratecoves_replace_b0_desc]")},
		B1: {Name: "_spawning.chaotic.replace", Def: false, Desc: expandDescription("[piratecoves_replace_b1_desc]")},
		B2: {Name: "_spawning.pirate.replace", Def: false, Desc: expandDescription("[piratecoves_replace_b2_desc]")},
        Info: expandDescription("[piratecoves_replace_bool_info]")
	},
	SInt: {
		S0: {Name: "_spawning.neutral.chance", Def: 0.25, Min: 0.0, Max: 100, Desc: expandDescription("[piratecoves_replace_s0_desc]"), Float:true},
		S1: {Name: "_spawning.chaotic.chance", Def: 0.25, Min: 0.0, Max: 100, Desc: expandDescription("[piratecoves_replace_s1_desc]"), Float:true},
		S2: {Name: "_spawning.pirate.chance", Def: 0.25, Min: 0.0, Max: 100, Desc: expandDescription("[piratecoves_replace_s2_desc]"), Float:true},
		Info: expandDescription("[piratecoves_replace_sint_info]")
	}
};

this._singleConfig = {
    Name: this.name,
    Alias: expandDescription("[piratecoves_alias]"),
    Display: expandDescription("[piratecoves_single_display]"),
    Alive: "_singleConfig",
    Bool: {
        B0: {Name: "_spawning.single.allowed", Def: true, Desc: expandDescription("[piratecoves_single_b0_desc]")},
        B1: {Name: "_spawning.single.sampleAtDiso", Def: true, Desc: expandDescription("[piratecoves_single_b1_desc]")},
        Info: expandDescription("[piratecoves_single_bool_info]")
    },
    SInt: {
        S0: {Name: "_spawning.single.xRange.max", Def:0.3, Min:0.01, Max: 1, Desc: expandDescription("[piratecoves_single_s0_desc]"), Float:true},
        S1: {Name: "_spawning.single.xRange.min", Def:0.0, Min:0.0, Max: 1, Desc: expandDescription("[piratecoves_single_s1_desc]"), Float:true},
        S2: {Name: "_spawning.single.yRange.max", Def:0.3, Min:0.01, Max: 1, Desc: expandDescription("[piratecoves_single_s2_desc]"), Float:true},
        S3: {Name: "_spawning.single.yRange.min", Def:0.0, Min:0.0, Max: 1, Desc: expandDescription("[piratecoves_single_s3_desc]"), Float:true},
        S4: {Name: "_spawning.single.zRange.max", Def:0.4, Min:0.1, Max: 0.8, Desc: expandDescription("[piratecoves_single_s4_desc]"), Float:true},
        S5: {Name: "_spawning.single.zRange.min", Def:0.1, Min:0.05, Max: 0.5, Desc: expandDescription("[piratecoves_single_s5_desc]"), Float:true},
        Info: expandDescription("[piratecoves_single_sint_info]")
    }
};

//-------------------------------------------------------------------------------------------------------------
this.startUp = function () {
    if (missionVariables.pirate_cove_spawning) {
        this._spawning = JSON.parse(missionVariables.pirate_cove_spawning);
    }
    // restore the systems where a pirate cove is missing
    if (missionVariables.pirate_cove_systems) {
        this.pirateCoveSystems = JSON.parse(missionVariables.pirate_cove_systems);
    } else {
        this.pirateCoveSystems = new Array(256);
    }

    this.basicDefenders = Math.round(Math.log(1 + player.score));
};

//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function() {
	// register our settings, if Lib_Config is present
	if (worldScripts.Lib_Config) {
        worldScripts.Lib_Config._registerSet(this._pcConfig);
        worldScripts.Lib_Config._registerSet(this._singleConfig);
    }
}

//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function () {
    // encode the systems were a pirate cove is missing
    missionVariables.pirate_cove_systems = JSON.stringify(this.pirateCoveSystems);
    missionVariables.pirate_cove_spawning = JSON.stringify(this._spawning);
}

//-------------------------------------------------------------------------------------------------------------
this.playerEnteredNewGalaxy = function () {
    // reset the systems
    this.pirateCoveSystems = new Array(256);
}

//-------------------------------------------------------------------------------------------------------------
this.systemWillPopulate = function () {
    // Test for [System.countShipsWithRole("pirate-cove") == 0] because the populator might already have added them as rock hermit replacement.
    if (!system.isInterstellarSpace && system.countShipsWithRole("pirate-cove") === 0 && !system.sun.hasGoneNova && this._spawning.single.allowed) {
        this.$addRockHermit();
    }
};

//-------------------------------------------------------------------------------------------------------------
this.$initialiseSystem = function () {
    // initialise
    if (system.countShipsWithPrimaryRole("pirate") > 9) {
        this.pirateCoveSystems[system.ID] = false; // allow stations until killed.
    } else {
        this.pirateCoveSystems[system.ID] = clock.days; // don't allow a station for the next 20 days.
    }
}

//-------------------------------------------------------------------------------------------------------------
this.$addRockHermit = function () {
    var coordinateSystem = (this.$randomZ() < 0.1) ? "psu" : "wpu";
    
    // get the x axis range
    var xRangeMax = this._spawning.single.xRange.max;
    // ensure range will be valid
    if (this._spawning.single.xRange.min > xRangeMax) this._spawning.single.xRange.min = 0;
    var xRangeMin = this._spawning.single.xRange.min;
    
    // get the y-axis range
    var yRangeMax = this._spawning.single.yRange.max;
    // ensure range will be valid
    if (this._spawning.single.yRange.min > yRangeMax) this._spawning.single.yRange.min = 0;
    var yRangeMin = this._spawning.single.yRange.min;
    
    // get the z-axis range
    var zRangeMax = this._spawning.single.zRange.max;
    // ensure range will be valid
    if (this._spawning.single.zRange.min > zRangeMax) this._spawning.single.zRange.min = 0.1;
    var zRangeMin = this._spawning.single.zRange.min;
    
    // 50% chance or either a positive or negative range
    var x = (this.$randomX() > 0.5 ? this.$randomX() * (xRangeMax - xRangeMin) + xRangeMin : this.$randomX() * ((xRangeMax * -1) - (xRangeMin * -1)) - xRangeMin);
    var y = (this.$randomY() > 0.5 ? this.$randomY() * (yRangeMax - yRangeMin) + yRangeMin : this.$randomY() * ((yRangeMax * -1) - (yRangeMin * -1)) - yRangeMin);
    // z range is always positive
    var z = this.$randomZ() * (zRangeMax - zRangeMin) + zRangeMin;
    
    var stnPos = Vector3D(x, y, z).fromCoordinateSystem(coordinateSystem);

    system.setPopulator("piratecove", {
        callback: function(pos) {
            var ws = worldScripts["Pirate_Coves"];
            var isDead = ws.pirateCoveSystems[system.ID];
            if (isDead == undefined || (isDead && (isDead + 20 < clock.days))) {
                ws.$initialiseSystem();
            }
            // make sure diso has a separate pirate cove
            if (galaxyNumber == 0 && system.ID == 147 && ws._spawning.single.sampleAtDiso == true) {
                ws.pirateCoveSystems[system.ID] = false;
            }

            if (!ws.pirateCoveSystems[system.ID]) {
                log("piratecove-populator", "adding pirate cove to system");
                var station = system.addShips("[pirate-cove-single]", 1, pos, 0)[0];
                station.orientation = [1, 0, 0, 0]; // orientate it along the z-axis like oolite hermits.
                var asteroidCount = Math.ceil(3 + ws.$randomZ() * 8);
                system.addShips("asteroid", asteroidCount, pos, 15000);
            }
        },
        location:"COORDINATES",
        coordinates: stnPos,
        deterministic: true 
    });
};

//-------------------------------------------------------------------------------------------------------------
this.$randomZ = function () {
    // return mostly the same value, but change over time.
    var salt = (clock.days & 0xE0) + 15; // change once every 32 days
    return system.scrambledPseudoRandomNumber(salt);
};

//-------------------------------------------------------------------------------------------------------------
this.$randomX = function () {
    var salt = (clock.days & 0xE0) + 20; // change once every 32 days
    return system.scrambledPseudoRandomNumber(salt);
}

//-------------------------------------------------------------------------------------------------------------
this.$randomY = function () {
    var salt = (clock.days & 0xE0) + 25; // change once every 32 days
    return system.scrambledPseudoRandomNumber(salt);
}
Scripts/pirateCoveConditions.js
"use strict";
this.name = "pirate_cove_conditions";
this.author = "phkb";
this.copyright = "2023 phkb";
this.license = "CC BY-NC-SA 4.0";

//-------------------------------------------------------------------------------------------------------------
this.allowSpawnShip = function(shipkey) {
    var typ = "";
    switch (shipkey) {
        case "pirate-cove":
            typ = "neutral";
            break;
        case "pirate-cove-chaotic":
            typ = "chaotic";
            break;
        case "pirate-cove-pirate":
            typ = "pirate";
            break;
        default:
            return false;
    }
    var pc = worldScripts.Pirate_Coves;
    if (!pc._spawning[typ].replace) return false;
    var chance = pc._spawning[typ].chance;
    // if the following returns true, it will be true for a whole month
    // so if you jump back into the system inside of a month, the same RH should be switched to a pirate cove
    if (system.scrambledPseudoRandomNumber((clock.daysComponent / 30) % 12 + this.$rockHermitSeedVariation(typ)) < chance) {
        if (worldScripts.Hermitage_Main) {
            var hm = worldScripts.Hermitage_Main;
            // don't take over a rh in a hermitage system
            if (hm.$checkHermitageExists("route1") || hm.$checkHermitageExists("route2") || hm.$checkHermitageExists("offlane")) return false;
        }
        return true;
    }
    return false;
}

//----------------------------------------------------------------------------------------
// get a different seed value for each type, so they will be checked independently of each other
this.$rockHermitSeedVariation = function(typ) {
    switch (typ) {
        case "neutral": return system.countShipsWithRole("rockhermit");
        case "chaotic": return system.countShipsWithRole("rockhermit-chaotic") + 10;
        case "pirate": return system.countShipsWithRole("rockhermit-pirate") + 20;
    }
    return 0;
}
Scripts/pirateCoveMarket.js
"use strict";
this.name = "pirate_cove_market";
this.author = "phkb";
this.copyright = "2023 phkb";
this.license = "CC BY-NC-SA 4.0";

this.$originalDefs = {
	"food": [0, 0, 20, -1, 0, 0, 1, 0, 0],
	"textiles": [0, 0, 20, -1, 0, 0, 3, 0, 0],
	"radioactives": [0, 0, 42, -3, -3, 1, 7, 3, 0],
	"slaves": [0, 0, 40, -5, 0, 0, 15, 0, 0],
	"liquor_wines": [0, 0, 105, -5, 0, 0, 15, 0, 0],
	"luxuries": [0, 0, 236, 8, 0, 0, 3, 0, 0],
	"narcotics": [0, 0, 235, 29, 0, 0, 120, 0, 0],
	"computers": [0, 0, 150, 14, 0, 0, 3, 0, 0],
	"machinery": [0, 0, 120, 6, 0, 0, 7, 0, 0],
	"alloys": [0, 0, 70, 1, 0, 0, 31, 2, 0],
	"firearms": [0, 0, 130, 13, 0, 29, 7, 0, 0],
	"furs": [0, 0, 160, -9, 0, 0, 63, 0, 0],
	"minerals": [0, 0, 16, -1, -1, 85, 3, 3, 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, 250, 15, 15, 2],
	"alien_items": [0, 0, 140, 1, 0, 0, 3, 0, 0]
};

//-------------------------------------------------------------------------------------------------------------
this.updateLocalCommodityDefinition = function (goodDefinition) {
	var commodity = goodDefinition.key;
	var oldDefs = this.$originalDefs[commodity];
	//old style definition found for the good. calculate it the old way
	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/pirateCoveRock.js
"use strict";
this.name = "Pirate Cove Rock";
this.author = "eric walch";
this.description = "Script for the Pirate_Cove Rock by Lazy Gun";
this.version = "1.5";

this.defenderCount = worldScripts["Pirate_Coves"].basicDefenders + Math.floor(Math.random() * 3);  // defenderCount is redefined by the main script.

//-------------------------------------------------------------------------------------------------------------
this.shipSpawned = function () {
    //if (this.ship.dataKey !== "pirate-cove-single") log(this.name, "pirate cove replacing rock hermit for role " + this.ship.primaryRole);
    //this.ship.primaryRole = "pirate-cove"; // needed to set the market correct. << not needed now
    this.maxAirbornDefenders = 3 + Math.floor(Math.random() * 4);
    if (player.score > 512) this.maxAirbornDefenders++;
    if (player.score > 6400) this.maxAirbornDefenders++;
}

//-------------------------------------------------------------------------------------------------------------
// Correct clean hits.
this.shipBeingAttacked = function (attacker) {
    if (attacker && attacker.owner === this.ship && attacker.target === this.ship) {
        attacker.reactToAIMessage("TARGET_LOST");
        //log(this.name, "Pirate cove was friendly hit by: " + attacker.displayName);
    }
}

//-------------------------------------------------------------------------------------------------------------
this.stationLaunchedShip = function (ship) {
    if (ship.primaryRole === "defense_ship") ship.switchAI("pirateCovePirateAI.plist");
    if (ship.primaryRole === "scavenger" && !ship.bounty) ship.bounty = 5 + Math.random() * 25;
    if (ship.primaryRole === "escort" && !ship.bounty) ship.bounty = 5 + Math.random() * 15;
}

//-------------------------------------------------------------------------------------------------------------
this.otherShipDocked = function (ship) {
    if (ship.isPlayer) {
        // we are spotted!
        this.ship.displayName.replace(expandDescription("[piratecove_rhname]"), expandDescription("[piratecove_name]")); 
    }
    if (ship.primaryRole == "pirate") this.defenderCount++;
}

//-------------------------------------------------------------------------------------------------------------
/*
In addition to the below launched pirates, the AI can also launch pirates as defense ships
when directly attacked.
*/
this.$launchDefender = function () {
    if (this.defenderCount > 0 && this.ship.target
        && this.ship.position.distanceTo(this.ship.target.position) < 15000
        && this.ship.group.count < this.maxAirbornDefenders) {
        var defender = this.ship.launchShipWithRole("pirate");
        if (defender) {
            defender.switchAI("pirateCovePirateAI.plist");
            defender.target = this.ship.target;
            this.ship.group.addShip(defender);
            defender.group = this.ship.group;
            if (!this.ship.bounty && this.ship.target.isPlayer) {
                this.ship.bounty = (this.ship.scanClass == "CLASS_NEUTRAL") ? 100 : 1000; // the cove is no longer clean now.
            }
            this.defenderCount--;
        }
    }
};

//-------------------------------------------------------------------------------------------------------------
this.shipDied = function () {
    // store the time of death
    worldScripts.Pirate_Coves.pirateCoveSystems[system.ID] = clock.days;
};