Scripts/rockHermitBeacons.js |
"use strict";
this.name = "RockHermitBeacons";
this.author = "Dybal";
this.copyright = "2020 Dybal";
this.license = "CC BY-NC-SA 4.0";
this.description = "Adds beacons to Rock Hermits";
this.version = "1.3";
this.$debug = false;
this.$logging = true;
this.$beep = new SoundSource;
this.$beep.sound = "rhb_chime.ogg";
this.$beaconCreationTimer;
// this.$rangeProbabilitiesProfiles has a list of profiles, each profile is a list of probablities for each range in this.$beaconRanges
this.$rangeProbabilitiesProfiles = [
// array of profiles with the probabilities for each beacon range
// the actual range value is found at similar position from the profile array in this.$beaconRanges
// probability for range 5 (range value 100000, unlimited) is 1-sum(probablities other ranges)
[ 0.02, 0.05, 0.15, 0.25, 0.30 ], // profile 0: system free+dangerous, honest
[ 0.10, 0.30, 0.20, 0.20, 0.15 ], // profile 1: system controlled+safe, honest
[ 0.05, 0.20, 0.40, 0.20, 0.10 ], // profile 2: system meddling+safe, honest
[ 0.05, 0.10, 0.40, 0.35, 0.10 ], // profile 3: system chaotic, honest
[ 0.03, 0.07, 0.30, 0.30, 0.30 ], // profile 4: system free+dangerous, pirate
[ 0.05, 0.10, 0.40, 0.40, 0.05 ], // profile 5: system controlled+safe, pirate
[ 0.05, 0.10, 0.30, 0.50, 0.05 ], // profile 6: system meddling+safe, pirate
[ 0.05, 0.10, 0.40, 0.35, 0.10 ], // profile 7: system chaotic, pirate
];
this.$beaconRanges = [ 0, 100, 500, 1000, 2000, 100000 ]; // in km
this.$asteroidQtt = [ 50, 100, 50, 20, 20, 20 ];
this.$accumProbProfiles = []; // derived from this.$rangeProbabilitiesProfiles, each profile has the accumulated probabilities up to that range inclusive
this.$rolesRH = [ // Rock Hermit roles and the range probability profile (index of this.$rangeProbabilitiesProfiles) based on system government
// by Government: An Fe Mu Di Cm Cf De Cp
{ role: "rockhermit", rangeProfile: [ 0, 0, 3, 2, 1, 3, 2, 1 ], salt: 1 },
{ role: "rockhermit-chaotic", rangeProfile: [ 0, 0, 3, 2, 1, 3, 2, 1 ], salt: 2 },
{ role: "rockhermit-pirate", rangeProfile: [ 4, 4, 7, 6, 5, 7, 6, 5 ], salt: 3 },
{ role: "pirate-cove", rangeProfile: [ 4, 4, 7, 6, 5, 7, 6, 5 ], salt: 4 }
];
this.$RHs = []; // list of Rock Hermits with beacons
this.$rangeVerifyTimer;
this.$populationSize = 20;
//------------------------------------------------------------------------------------------------------------
// WorldScript Event Handlers
//
//------------------------------------------------------------------------------------------------------------
this.startUpComplete = this.shipExitedWitchspace = function _init() {
var _rangeProfiles = this.$rangeProbabilitiesProfiles;
var _acRangeProfiles = this.$accumProbProfiles;
var i, j, r;
for (i=0; i<_rangeProfiles.length; i++) {
r = [];
r.push(_rangeProfiles[i][0])
for (j=1; j<_rangeProfiles[i].length; j++) {
r.push(r[j-1] + _rangeProfiles[i][j]);
}
_acRangeProfiles.push(r);
}
// allow some some time for other OXPs to create the Rock Hermits
if (this.$beaconsCreationTimer)
this.$beaconsCreationTimer.start();
else
this.$beaconsCreationTimer = new Timer(this, this.$addBeacons, 3);
}
//------------------------------------------------------------------------------------------------------------
this.shipExitedWitchspace = function _shipExitedWitchspace() {
if (this.$beaconsCreationTimer)
this.$beaconsCreationTimer.start();
else
this.$beaconsCreationTimer = new Timer(this, this.$addBeacons, 3);
this.$startRangeVerifyTimer();
}
//------------------------------------------------------------------------------------------------------------
this.shipWillEnterWitchspace = function _shipWillEnterWitchspace() {
this.$stopRangeVerifyTimer();
this.$stopAsteroidRepopulatorTimer();
this.$RHs.length = 0;
}
//------------------------------------------------------------------------------------------------------------
this.shipLaunchedFromStation = function _shipWilllLaunchFromStation() {
this.$startRangeVerifyTimer();
}
//------------------------------------------------------------------------------------------------------------
this.shipWillDockWithStation = function _shipWilllDockWithStation() {
this.$stopRangeVerifyTimer();
}
//------------------------------------------------------------------------------------------------------------
// Internal functions
//
this.$addBeacons = function _addBeacons() {
var ws = this;
var _shipsWithPrimaryRole = system.shipsWithPrimaryRole.bind(system);
var _scrambledPseudoRandomNumber = system.scrambledPseudoRandomNumber.bind(system);
var _rolesDescList = this.$rolesRH;
var _RHsWithBeacons = this.$RHs;
var gov = system.government;
var logging = ws.$logging;
var i = _rolesDescList.length;
var roleDesc, rh_list, j, rh, index, asteroids;
delete this.$beaconsCreationTimer;
while (i--) {
roleDesc = _rolesDescList[i];
if (ws.$debug) log (ws.name, "Adding beacons to Rock Hermits with role "+roleDesc.role);
rh_list = _shipsWithPrimaryRole(roleDesc.role);
var j = rh_list.length;
while (j--) {
rh = rh_list[j];
if (rh.roles.indexOf("astromine") >= 0) {
rh.script.$rhbCreateBeacon = "no";
rh.script.$rhbAsteroidsQtt = 100;
}
if (rh.isValid) {
if (!rh.script || !rh.script.$rhbCreateBeacon || rh.script.$rhbCreateBeacon !== "no") {
// RH doesn't have a (creator/owner)-managed beacon
if (!rh.script) rh.setScript("oolite-default-ship-script");
rh.beaconLabel = rh.displayName; // nice name for ASC to display
rh.script.$rhbBeaconRange = this.$giveRange(_scrambledPseudoRandomNumber(roleDesc.salt), roleDesc.rangeProfile[gov]);
index = this.$beaconRanges.indexOf(rh.script.$rhbBeaconRange);
if (index >= 0) {
asteroids = this.$addAsteroids(this.$asteroidQtt[index], rh.position);
rh.script.$rhbAsteroidsQtt = asteroids.length;
if (logging) log(this.name, "Created "+asteroids.length+" asteroids around "+rh.displayName);
}
if (logging) log(ws.name, rh.displayName+" (role "+roleDesc.role+") has a beacon with range of "+rh.script.$rhbBeaconRange+"km");
} else {
// RH is an astromine, OR
// RH creator said we shouldn't create a beacon, but let's look if we should manage visibility or asteroid field
if (logging) log(ws.name, rh.displayName+" is a managed Rock Hermit, any beacon should be turned On/Off by its creator");
if (rh.script && rh.script.$rhbAsteroidsQtt && rh.script.$rhbAsteroidsQtt > 0) {
asteroids = this.$addAsteroids(rh.script.$rhbAsteroidsQtt, rh.position);
if (logging) log(this.name, "Created "+asteroids.length+" asteroids around "+rh.displayName);
}
}
if (rh.script && rh.script.$rhbBeaconRange && rh.script.$rhbBeaconRange > 0)
_RHsWithBeacons.push(rh);
}
}
}
this.$startAsteroidRepopulatorTimer();
}
//------------------------------------------------------------------------------------------------------------
this.$repopulateAsteroids = function _repopulateAsteroids() {
var ws = this;
var _shipsWithPrimaryRole = system.shipsWithPrimaryRole.bind(system);
var _scrambledPseudoRandomNumber = system.scrambledPseudoRandomNumber.bind(system);
var _rolesDescList = this.$rolesRH;
var _RHsWithBeacons = this.$RHs;
var gov = system.government;
var population_size = this.$populationSize;
var logging = ws.$logging;
var i = _rolesDescList.length;
var roleDesc, rh_list, j, rh, n, asteroids;
if (ws.$debug) log(this.name, "Verifying asteroid population around Rock Hermits");
while (i--) {
roleDesc = _rolesDescList[i];
rh_list = _shipsWithPrimaryRole(roleDesc.role);
var j = rh_list.length;
while (j--) {
rh = rh_list[j];
if (rh.isValid) {
if (rh.script && rh.script.$rhbAsteroidsQtt && rh.script.$rhbAsteroidsQtt > 0) {
n = system.countShipsWithPrimaryRole("asteroid", rh, 12000*Math.ceil(rh.script.$rhbAsteroidsQtt/population_size));
if (ws.$debug) log(this.name, "Found "+n+" asteroids less than "+12*Math.ceil(rh.script.$rhbAsteroidsQtt/population_size)+" km from "+rh.displayName);
if (n < rh.script.$rhbAsteroidsQtt) {
if (logging) log(this.name, "Asteroid population around "+rh.displayName+" decreased, there should be "+rh.script.$rhbAsteroidsQtt+", creating "+(rh.script.$rhbAsteroidsQtt - n)+" new asteroids");
this.$addAsteroids(rh.script.$rhbAsteroidsQtt - n, rh.position);
}
}
}
}
}
}
//------------------------------------------------------------------------------------------------------------
this.$addAsteroids = function _addAsteroids(num, pos) {
var radius, n, i, asteroid;
var asteroids = [];
var population_size = this.$populationSize;
var steps = Math.floor(n/population_size);
i = 0;
while (num > 0) {
i++;
n = (num > population_size) ? population_size : num;
num -= population_size;
while (n--) {
asteroid = system.addShips("asteroid", 1, pos.add(Vector3D.randomDirection(i*12000)), 1);
if (!asteroid)
log(this.name, "Could not add asteroid near Rock Hermit");
else
asteroids.push(asteroid);
}
}
return asteroids;
}
//------------------------------------------------------------------------------------------------------------
this.$giveRange = function _giveRange(rnd, rangeProfileIndex) {
var ws = this;
var _rangeProfile = this.$accumProbProfiles[rangeProfileIndex];
var _ranges = this.$beaconRanges;
var i = 0;
if (ws.$debug) log(ws.name, "accumulated range probabilities:"+_rangeProfile);
while (i < _rangeProfile.length && rnd > _rangeProfile[i]) {
if (ws.$debug) log(ws.name, i+":"+_rangeProfile[i]+", random:"+rnd.toFixed(3));
i++ ;
}
if (ws.$debug) log(this.name, i+":"+_rangeProfile[i]+", random:"+rnd.toFixed(3));
return _ranges[i];
}
//------------------------------------------------------------------------------------------------------------
this.$verifyBeaconsRange = function _switchBeaconsOn() {
var ws = this;
var _ship = player.ship;
var _RHsWithBeacons = this.$RHs;
var pos = _ship.position;
var debug = ws.$debug;
var logging = ws.$logging;
var i = _RHsWithBeacons.length;
var rh, distance;
while (i--) {
rh = _RHsWithBeacons[i];
if (rh.isValid && rh.script && rh.script.$rhbBeaconRange) {
distance = pos.distanceTo(rh)
if (debug) log(this.name, rh.displayName+" has beacon range "+rh.script.$rhbBeaconRange+"km, distance:"+(distance/1000).toFixed(3)+", isVisible:"+rh.isVisible+", is ON:"+(rh.beaconCode != null))
if (pos.distanceTo(rh) <= 1000 * rh.script.$rhbBeaconRange) {
// beacon in range
if (!rh.script.$rhbCreateBeacon || rh.script.$rhbCreateBeacon != "no") {
// RH's creator alows beacon visibility management
if (rh.beaconCode == null) {
// beaocn is Off, turn it On
rh.beaconCode = (rh.script.$rhbBeaconCode ? rh.script.$rhbBeaconCode : "Rock Hermit");
if (logging) log(ws.name, rh.displayName+" beacon in range");
if (_ship.equipmentStatus("EQ_ADVANCED_COMPASS") === "EQUIPMENT_OK") {
this.$beep.play();
player.commsMessage(rh.displayName+" beacon detected", 5)
}
}
}
} else if (rh.beaconCode) {
// RH out of beacon range, turn beacon OFF
rh.script.$rhbBeaconCode = rh.beaconCode;
rh.beaconCode = null;
if (logging) log(ws.name, rh.displayName+" beacon out of range");
}
}
}
}
//------------------------------------------------------------------------------------------------------------
this.$startRangeVerifyTimer = function _startRangeVerifyTimer() {
if (this.$rangeVerifyTimer)
this.$rangeVerifyTimer.start()
else
this.$rangeVerifyTimer = new Timer(this, this.$verifyBeaconsRange, 1, 2);
}
//------------------------------------------------------------------------------------------------------------
this.$stopRangeVerifyTimer = function _stopRangeVerifyTimer() {
if (this.$rangeVerifyTimer) {
if (this.$rangeVerifyTimer.isRunning)
this.$rangeVerifyTimer.stop();
delete this.$rangeVerifyTimer;
}
}
//------------------------------------------------------------------------------------------------------------
this.$startAsteroidRepopulatorTimer = function _startAsteroidRepopulatorTimer() {
if (this.$asteroidRepopulatorTimer)
this.$asteroidRepopulatorTimer.start()
else
this.$asteroidRepopulatorTimer = new Timer(this, this.$repopulateAsteroids, 1, 300); // every 5 minutes
}
//------------------------------------------------------------------------------------------------------------
this.$stopAsteroidRepopulatorTimer = function _stopAsteroidRepopulatorTimer() {
if (this.$asteroidRepopulatorTimer) {
if (this.$asteroidRepopulatorTimer.isRunning)
this.$asteroidRepopulatorTimer.stop()
delete this.$asteroidRepopulatorTimer;
}
}
|