mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 22:55:35 +00:00
Total range, priority management, other tweaks
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
angular.module('app', ['ui.router', 'ct.ui.router.extras.sticky', 'ui.sortable', 'shipyard', 'ngLodash', 'app.templates'])
|
||||
.run(['$rootScope', '$location', '$window', '$document','$state','commonArray','shipPurpose','shipSize','hardPointClass','internalGroupMap','hardpointsGroupMap', 'Persist', function ($rootScope, $location, $window, $doc, $state, CArr, shipPurpose, sz, hpc, igMap, hgMap, Persist) {
|
||||
.run(['$rootScope', '$location', '$window', '$document','$state','commonArray','shipPurpose','shipSize','hardPointClass','GroupMap', 'Persist', function ($rootScope, $location, $window, $doc, $state, CArr, shipPurpose, sz, hpc, GroupMap, Persist) {
|
||||
// App is running as a standalone web app on tablet/mobile
|
||||
var isStandAlone = $window.navigator.standalone || ($window.external && $window.external.msIsSiteMode && $window.external.msIsSiteMode());
|
||||
|
||||
@@ -31,10 +31,15 @@ angular.module('app', ['ui.router', 'ct.ui.router.extras.sticky', 'ui.sortable',
|
||||
$rootScope.SP = shipPurpose;
|
||||
$rootScope.SZ = sz;
|
||||
$rootScope.HPC = hpc;
|
||||
$rootScope.igMap = igMap;
|
||||
$rootScope.hgMap = hgMap;
|
||||
$rootScope.GMAP = GroupMap;
|
||||
$rootScope.STATUS = ['','DISABLED', 'OFF', 'ON'];
|
||||
$rootScope.STATUS_CLASS = ['','disabled', 'warning', 'secondary-disabled'];
|
||||
$rootScope.title = 'Coriolis';
|
||||
|
||||
$rootScope.cName = function (c) {
|
||||
return c.c? c.c.name? c.c.name : GroupMap[c.c.grp] : null;
|
||||
}
|
||||
|
||||
// Formatters
|
||||
$rootScope.fCrd = d3.format(',.0f');
|
||||
$rootScope.fPwr = d3.format(',.2f');
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
angular.module('app').controller('OutfitController', ['$window','$rootScope','$scope', '$state', '$stateParams', 'ShipsDB', 'Ship', 'Components', 'Serializer', 'Persist', function ($window, $rootScope, $scope, $state, $p, Ships, Ship, Components, Serializer, Persist) {
|
||||
angular.module('app').controller('OutfitController', ['$window','$rootScope','$scope', '$state', '$stateParams', 'ShipsDB', 'Ship', 'Components', 'Serializer', 'Persist', 'lodash', function ($window, $rootScope, $scope, $state, $p, Ships, Ship, Components, Serializer, Persist, _) {
|
||||
var data = Ships[$p.shipId]; // Retrieve the basic ship properties, slots and defaults
|
||||
var ship = new Ship($p.shipId, data.properties, data.slots); // Create a new Ship instance
|
||||
var win = angular.element($window); // Angularized window object for event triggering
|
||||
|
||||
// Update the ship instance with the code (if provided) or the 'factory' defaults.
|
||||
if ($p.code) {
|
||||
@@ -22,11 +23,18 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s
|
||||
$scope.ft = ship.common[6]; // Fuel Tank
|
||||
$scope.hps = ship.hardpoints;
|
||||
$scope.internal = ship.internal;
|
||||
$scope.costList = ship.costList;
|
||||
$scope.powerList = ship.powerList;
|
||||
$scope.priorityBands = ship.priorityBands;
|
||||
$scope.availCS = Components.forShip(ship.id);
|
||||
$scope.selectedSlot = null;
|
||||
$scope.savedCode = Persist.getBuild(ship.id, $scope.buildName);
|
||||
$scope.canSave = Persist.isEnabled();
|
||||
$scope.fuel = 0;
|
||||
$scope.pwrDesc = false;
|
||||
$scope.pwrPredicate = null;
|
||||
$scope.costDesc = true;
|
||||
$scope.costPredicate = 'c.cost';
|
||||
|
||||
$scope.jrSeries = {
|
||||
xMin: 0,
|
||||
@@ -149,39 +157,71 @@ angular.module('app').controller('OutfitController', ['$window','$rootScope','$s
|
||||
* @param {object} item The component being toggled
|
||||
*/
|
||||
$scope.toggleCost = function(item) {
|
||||
item.incCost = !item.incCost;
|
||||
ship.updateTotals();
|
||||
ship.setCostIncluded(item, !item.incCost);
|
||||
};
|
||||
|
||||
/**
|
||||
* [sortCost description]
|
||||
* @param {[type]} key [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
$scope.sortCost = function (key) {
|
||||
$scope.costDesc = ($scope.costPredicate == key)? !$scope.costDesc : $scope.costDesc;
|
||||
$scope.costPredicate = key;
|
||||
};
|
||||
|
||||
$scope.sortPwr = function (key) {
|
||||
$scope.pwrDesc = ($scope.pwrPredicate == key)? !$scope.pwrDesc : $scope.pwrDesc;
|
||||
$scope.pwrPredicate = key;
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle the power on/off for the selected component
|
||||
* @param {object} item The component being toggled
|
||||
*/
|
||||
$scope.togglePwr = function(item) {
|
||||
// Update serialize code
|
||||
// updateState();
|
||||
item.enabled = !item.enabled;
|
||||
ship.updateTotals();
|
||||
$scope.togglePwr = function(c) {
|
||||
ship.setSlotEnabled(c, !c.enabled);
|
||||
$scope.code = Serializer.fromShip(ship);
|
||||
updateState();
|
||||
};
|
||||
|
||||
$scope.incPriority = function (c) {
|
||||
if (ship.changePriority(c, c.priority + 1)) {
|
||||
$scope.code = Serializer.fromShip(ship);
|
||||
updateState();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.decPriority = function (c) {
|
||||
if (ship.changePriority(c, c.priority - 1)) {
|
||||
$scope.code = Serializer.fromShip(ship);
|
||||
updateState();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.fuelChange = function (fuel) {
|
||||
$scope.fuel = fuel;
|
||||
angular.element($window).triggerHandler('render');
|
||||
win.triggerHandler('render');
|
||||
};
|
||||
|
||||
$scope.statusRetracted = function (slot) {
|
||||
return ship.getSlotStatus(slot, false);
|
||||
};
|
||||
|
||||
$scope.statusDeployed = function (slot) {
|
||||
return ship.getSlotStatus(slot, true);
|
||||
};
|
||||
|
||||
// Utilify functions
|
||||
|
||||
function updateState() {
|
||||
$state.go('outfit', {shipId: ship.id, code: $scope.code, bn: $scope.buildName}, {location:'replace', notify:false});
|
||||
$scope.jrSeries.xMax = ship.cargoCapacity;
|
||||
$scope.jrSeries.yMax = ship.jumpRangeWithMass(ship.unladenMass);
|
||||
$scope.jrSeries.mass = ship.unladenMass;
|
||||
win.triggerHandler('pwrchange');
|
||||
}
|
||||
|
||||
// Hide any open menu/slot/etc if escape key is pressed
|
||||
$scope.$on('escape', function () {
|
||||
$scope.selectedSlot = null;
|
||||
$scope.$apply();
|
||||
});
|
||||
// Hide any open menu/slot/etc if the background is clicked
|
||||
$scope.$on('close', function () {
|
||||
$scope.selectedSlot = null;
|
||||
|
||||
119
app/js/directives/directive-power-bands.js
Normal file
119
app/js/directives/directive-power-bands.js
Normal file
@@ -0,0 +1,119 @@
|
||||
angular.module('app').directive('powerBands', ['$window', function ($window) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope:{
|
||||
bands: '=',
|
||||
available: '='
|
||||
},
|
||||
link: function(scope, element) {
|
||||
var color = d3.scale.ordinal().range([ '#7b6888', '#6b486b', '#3182bd', '#a05d56', '#d0743c']),
|
||||
margin = {top: 20, right: 130, bottom: 20, left: 40},
|
||||
barHeight = 20,
|
||||
innerHeight = (barHeight * 2) + 3,
|
||||
height = innerHeight + margin.top + margin.bottom + 1,
|
||||
wattScale = d3.scale.linear(),
|
||||
pctScale = d3.scale.linear().domain([0, 1]),
|
||||
wattFmt = d3.format('.2f');
|
||||
pctFmt = d3.format('.1%');
|
||||
wattAxis = d3.svg.axis().scale(wattScale).outerTickSize(0).orient('top').tickFormat(d3.format('.2r')),
|
||||
pctAxis = d3.svg.axis().scale(pctScale).outerTickSize(0).orient('bottom').tickFormat(d3.format('%')),
|
||||
// Create chart
|
||||
svg = d3.select(element[0]).append('svg'),
|
||||
vis = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'),
|
||||
deployed = vis.append('g'),
|
||||
retracted = vis.append('g');
|
||||
|
||||
// Create Y Axis SVG Elements
|
||||
vis.append('g').attr('class', 'watt axis');
|
||||
vis.append('g').attr('class', 'pct axis');
|
||||
vis.append("text").attr('x', -35).attr('y', 15).attr('class','primary').text('RET');
|
||||
vis.append("text").attr('x', -35).attr('y', barHeight + 17).attr('class','primary').text('DEP');
|
||||
|
||||
var retLbl = vis.append("text").attr('y', 15)
|
||||
var depLbl = vis.append("text").attr('y', barHeight + 17).attr('class','primary');
|
||||
|
||||
// Watch for changes to data and events
|
||||
scope.$watchCollection('available', render);
|
||||
angular.element($window).bind('orientationchange resize pwrchange', render);
|
||||
|
||||
function render() {
|
||||
var bands = scope.bands,
|
||||
available = scope.available,
|
||||
width = element[0].offsetWidth,
|
||||
w = width - margin.left - margin.right,
|
||||
maxBand = bands[bands.length - 1];
|
||||
maxPwr = Math.max(available, maxBand.deployedSum);
|
||||
|
||||
// Update chart size
|
||||
svg.attr('width', width).attr('height', height);
|
||||
|
||||
// Remove existing elements
|
||||
retracted.selectAll('rect').remove();
|
||||
retracted.selectAll('text').remove();
|
||||
deployed.selectAll('rect').remove();
|
||||
deployed.selectAll('text').remove();
|
||||
|
||||
// Update X & Y Axis
|
||||
wattScale.range([0, w]).domain([0, maxPwr]);
|
||||
pctScale.range([0, w]).domain([0, maxPwr / available]);
|
||||
|
||||
vis.selectAll('.watt.axis').call(wattAxis);
|
||||
vis.selectAll('.pct.axis').attr('transform', 'translate(0,' + innerHeight + ')').call(pctAxis);
|
||||
|
||||
retLbl
|
||||
.attr('x', wattScale(maxBand.retractedSum) + 5 )
|
||||
.attr('class',maxBand.retractedSum > available? 'warning': 'primary')
|
||||
.text(wattFmt(maxBand.retractedSum) + ' (' + pctFmt(maxBand.retractedSum / available) + ')');
|
||||
depLbl
|
||||
.attr('x', wattScale(maxBand.deployedSum) + 5 )
|
||||
.attr('class',maxBand.deployedSum > available? 'warning': 'primary')
|
||||
.text(wattFmt(maxBand.deployedSum) + ' (' + pctFmt(maxBand.deployedSum / available) + ')');
|
||||
|
||||
retracted.selectAll("rect").data(bands).enter().append("rect")
|
||||
.attr("height", barHeight)
|
||||
.attr("width", function(d) { return d.retracted? (wattScale(d.retracted) - 1) : 0; })
|
||||
.attr("x", function(d) { return wattScale(d.retractedSum) - wattScale(d.retracted); })
|
||||
.attr('y', 1)
|
||||
.attr('class',function(d){ return (d.retractedSum > available)? 'warning' :'primary'; });
|
||||
|
||||
retracted.selectAll("text").data(bands).enter().append("text")
|
||||
.attr('x', function(d) { return wattScale(d.retractedSum) - (wattScale(d.retracted) / 2); })
|
||||
.attr('y', 15)
|
||||
.style('text-anchor', 'middle')
|
||||
.attr('class','primary-bg')
|
||||
.text(function(d,i) { return bandText(d.retracted, i, available); });
|
||||
|
||||
deployed.selectAll("rect").data(bands).enter().append("rect")
|
||||
.attr("height", barHeight)
|
||||
.attr("width", function(d) { return (d.deployed || d.retracted)? (wattScale(d.deployed + d.retracted) - 1) : 0; })
|
||||
.attr("x", function(d) { return wattScale(d.deployedSum) - wattScale(d.retracted) - wattScale(d.deployed); })
|
||||
.attr('y', barHeight + 2)
|
||||
.attr('class',function(d){ return (d.deployedSum > available)? 'warning' :'primary'; });
|
||||
|
||||
deployed.selectAll("text").data(bands).enter().append("text")
|
||||
.attr('x', function(d) { return wattScale(d.deployedSum) - ((wattScale(d.retracted) + wattScale(d.deployed)) / 2); })
|
||||
.attr('y', barHeight + 17)
|
||||
.style('text-anchor', 'middle')
|
||||
.attr('class','primary-bg')
|
||||
.text(function(d,i) { return bandText(d.deployed + d.retracted, i, available); });
|
||||
|
||||
}
|
||||
|
||||
function bandText(val, index, available) {
|
||||
if (val > 0) {
|
||||
if( wattScale(val) > 100) {
|
||||
return (index + 1) + ' (' + wattFmt(val) + ' MW)';
|
||||
}
|
||||
if( wattScale(val) > 10) {
|
||||
return index + 1;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
angular.element($window).unbind('orientationchange resize pwrchange', render);
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
||||
@@ -9,8 +9,7 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
|
||||
*/
|
||||
function Ship(id, properties, slots) {
|
||||
this.id = id;
|
||||
this.incCost = true;
|
||||
this.cargoScoop = { enabled: true, c: Components.cargoScoop() };
|
||||
this.cargoScoop = { enabled: true, c: Components.cargoScoop(), type: 'SYS' };
|
||||
this.bulkheads = { incCost: true, maxClass: 8 };
|
||||
|
||||
for (var p in properties) { this[p] = properties[p]; } // Copy all base properties from shipData
|
||||
@@ -22,6 +21,37 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
|
||||
group.push({id: null, c: null, enabled: true, incCost: true, maxClass: slotGroup[i]});
|
||||
}
|
||||
}
|
||||
this.c = { incCost: true, c: { name: this.name, cost: this.cost } }; // Make a 'Ship' component similar to other components
|
||||
|
||||
this.costList = _.union(this.internal, this.common, this.hardpoints);
|
||||
this.costList.push(this.bulkheads); // Add The bulkheads
|
||||
this.costList.unshift(this.c); // Add the ship itself to the list
|
||||
|
||||
this.powerList = _.union(this.internal, this.hardpoints);
|
||||
this.powerList.unshift(this.cargoScoop);
|
||||
this.powerList.unshift(this.common[1]); // Add Thrusters
|
||||
this.powerList.unshift(this.common[5]); // Add Sensors
|
||||
this.powerList.unshift(this.common[4]); // Add Power Distributor
|
||||
this.powerList.unshift(this.common[3]); // Add Life Support
|
||||
this.powerList.unshift(this.common[2]); // Add FSD
|
||||
this.powerList.unshift(this.common[0]); // Add Power Plant
|
||||
this.priorityBands = [
|
||||
{deployed: 0, retracted: 0},
|
||||
{deployed: 0, retracted: 0},
|
||||
{deployed: 0, retracted: 0},
|
||||
{deployed: 0, retracted: 0},
|
||||
{deployed: 0, retracted: 0}
|
||||
];
|
||||
|
||||
// Reset cumulative and aggragate stats
|
||||
this.fuelCapacity = 0;
|
||||
this.cargoCapacity = 0;
|
||||
this.ladenMass = 0;
|
||||
this.armourAdded = 0;
|
||||
this.shieldMultiplier = 1;
|
||||
this.totalCost = this.cost;
|
||||
this.unladenMass = this.mass;
|
||||
this.armourTotal = this.armour;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,142 +62,68 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
|
||||
var internal = this.internal;
|
||||
var common = this.common;
|
||||
var hps = this.hardpoints;
|
||||
var bands = this.priorityBands;
|
||||
var i,l;
|
||||
|
||||
this.bulkheads.id = comps.bulkheads || 0;
|
||||
this.bulkheads.c = Components.bulkheads(this.id, this.bulkheads.id);
|
||||
this.useBulkhead(comps.bulkheads || 0, true);
|
||||
this.cargoScoop.priority = 0; // TODO set from comps
|
||||
bands[this.cargoScoop.priority].retracted += this.cargoScoop.c.power;
|
||||
|
||||
for(i = 0, l = comps.common.length; i < l; i++) {
|
||||
common[i].id = comps.common[i];
|
||||
common[i].c = Components.common(i, comps.common[i]);
|
||||
common[i].enabled = true; // TODO set enabled from comps
|
||||
common[i].priority = 0; // TODO set from comps
|
||||
common[i].type = 'SYS';
|
||||
this.use(common[i], comps.common[i], Components.common(i, comps.common[i]), true);
|
||||
}
|
||||
|
||||
common[1].type = 'ENG'; // Thrusters
|
||||
common[2].type = 'ENG'; // FSD
|
||||
|
||||
for(i = 0, l = comps.hardpoints.length; i < l; i++) {
|
||||
hps[i].enabled = true; // TODO set enabled from comps
|
||||
hps[i].priority = 0; // TODO set from comps
|
||||
hps[i].type = hps[i].maxClass? 'WEP' : 'SYS';
|
||||
if (comps.hardpoints[i] !== 0) {
|
||||
hps[i].id = comps.hardpoints[i];
|
||||
hps[i].c = Components.hardpoints(comps.hardpoints[i]);
|
||||
this.use(hps[i], comps.hardpoints[i], Components.hardpoints(comps.hardpoints[i]), true);
|
||||
} else {
|
||||
hps[i].c = hps[i].id = null;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0, l = comps.internal.length; i < l; i++) {
|
||||
internal[i].enabled = true; // TODO set enabled from comps
|
||||
internal[i].priority = 0; // TODO set from comps
|
||||
internal[i].type = 'SYS';
|
||||
if (comps.internal[i] !== 0) {
|
||||
internal[i].id = comps.internal[i];
|
||||
internal[i].c = Components.internal(comps.internal[i]);
|
||||
this.use(internal[i], comps.internal[i], Components.internal(comps.internal[i]), true);
|
||||
} else {
|
||||
internal[i].id = internal[i].c = null;
|
||||
internal[i].id = internal[i].c = null;
|
||||
}
|
||||
}
|
||||
this.updateTotals();
|
||||
|
||||
// Update aggragated stats
|
||||
this.updatePower();
|
||||
this.updateJumpStats();
|
||||
this.updateShieldStrength();
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the ship totals based on the components for every slot.
|
||||
*/
|
||||
Ship.prototype.updateTotals = function() {
|
||||
var c = _.reduce(this.common, optsSum, {cost: 0, power: 0, mass: 0});
|
||||
var i = _.reduce(this.internal, optsSum, {cost: 0, power: 0, mass: 0, fuel: 0, cargo: 0, armouradd: 0});
|
||||
var h = _.reduce(this.hardpoints, hpSum, {cost: 0, active: 0, passive: 0, mass: 0, shieldmul: 1});
|
||||
var fsd = this.common[2].c; // Frame Shift Drive;
|
||||
var sgSI = this.findInternalByGroup('sg'); // Find Shield Generator slot Index if any
|
||||
|
||||
this.totalCost = c.cost + i.cost + h.cost + (this.incCost? this.cost : 0) + (this.bulkheads.incCost? this.bulkheads.c.cost : 0);
|
||||
this.unladenMass = c.mass + i.mass + h.mass + this.mass + this.bulkheads.c.mass;
|
||||
this.powerAvailable = this.common[0].c.pGen; // Power Plant
|
||||
this.fuelCapacity = this.common[6].c.capacity + i.fuel; // Fuel Tank + Internal Fuel Tanks
|
||||
this.maxMass = this.common[1].c.maxmass; // Thrusters Max Mass
|
||||
this.cargoCapacity = i.cargo;
|
||||
this.ladenMass = this.unladenMass + this.cargoCapacity + this.fuelCapacity;
|
||||
this.powerRetracted = c.power + i.power + h.passive + (this.cargoScoop.enabled? this.cargoScoop.c.power : 0);
|
||||
this.powerDeployed = this.powerRetracted + h.active;
|
||||
this.armourAdded = i.armouradd;
|
||||
this.shieldMultiplier = h.shieldmul;
|
||||
this.shieldStrength = sgSI != -1? calcShieldStrength(this.mass, this.shields, this.internal[sgSI].c, this.shieldMultiplier) : 0;
|
||||
this.armourTotal = this.armourAdded + this.armour;
|
||||
|
||||
// Jump Range and total range calculations
|
||||
var fuelRemaining = this.fuelCapacity % fsd.maxfuel; // Fuel left after making N max jumps
|
||||
var jumps = this.fuelCapacity / fsd.maxfuel;
|
||||
this.unladenRange = calcJumpRange(this.unladenMass + fsd.maxfuel, fsd, this.fuelCapacity); // Include fuel weight for jump
|
||||
this.fullTankRange = calcJumpRange(this.unladenMass + this.fuelCapacity, fsd, this.fuelCapacity); // Full Tanke
|
||||
this.ladenRange = calcJumpRange(this.ladenMass, fsd, this.fuelCapacity);
|
||||
this.maxJumpCount = Math.ceil(jumps); // Number of full fuel jumps + final jump to empty tank
|
||||
|
||||
// Going backwards, start with the last jump using the remaining fuel
|
||||
this.unladenTotalRange = fuelRemaining > 0? calcJumpRange(this.unladenMass + fuelRemaining, fsd, fuelRemaining): 0;
|
||||
this.ladenTotalRange = fuelRemaining > 0? calcJumpRange(this.unladenMass + this.cargoCapacity + fuelRemaining, fsd, fuelRemaining): 0;
|
||||
|
||||
// For each max fuel jump, calculate the max jump range based on fuel left in the tank
|
||||
for (var j = 0, l = Math.floor(jumps); j < l; j++) {
|
||||
fuelRemaining += fsd.maxfuel;
|
||||
this.unladenTotalRange += calcJumpRange(this.unladenMass + fuelRemaining, fsd);
|
||||
this.ladenTotalRange += calcJumpRange(this.unladenMass + this.cargoCapacity + fuelRemaining, fsd);
|
||||
}
|
||||
|
||||
// TODO: armor bonus / damage reduction for bulkheads
|
||||
// TODO: Damage / DPS total (for all weapons)
|
||||
};
|
||||
|
||||
/**
|
||||
* Utilify function for summing the components properties
|
||||
*
|
||||
* @private
|
||||
* @param {object} sum Sum of cost, power, mass, capacity
|
||||
* @param {object} slot Slot object
|
||||
* @return {object} The mutated sum object
|
||||
*/
|
||||
function optsSum(sum, slot) {
|
||||
var c = slot.c;
|
||||
if (c) { // The slot has a component installed
|
||||
sum.cost += (slot.incCost && c.cost)? c.cost : 0;
|
||||
sum.power += (slot.enabled && c.power)? c.power : 0;
|
||||
sum.mass += c.mass || 0;
|
||||
if (c.grp == 'ft') { // Internal Fuel Tank
|
||||
sum.fuel += c.capacity;
|
||||
}
|
||||
else if (c.grp == 'cr') { // Internal Cargo Rack
|
||||
sum.cargo += c.capacity;
|
||||
}
|
||||
sum.armouradd += c.armouradd || 0;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilify function for summing the hardpoint properties
|
||||
*
|
||||
* @private
|
||||
* @param {object} sum Sum of cost, power, etc
|
||||
* @param {object} slot Slot object
|
||||
* @return {object} The mutated sum object
|
||||
*/
|
||||
function hpSum(sum, slot) {
|
||||
var c = slot.c;
|
||||
if (c) { // The slot has a component installed
|
||||
sum.cost += (slot.incCost && c.cost)? c.cost : 0;
|
||||
sum[c.passive? 'passive': 'active'] += slot.enabled? c.power : 0;
|
||||
sum.mass += c.mass || 0;
|
||||
sum.shieldmul += c.shieldmul || 0;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
Ship.prototype.useBulkhead = function(index) {
|
||||
Ship.prototype.useBulkhead = function(index, preventUpdate) {
|
||||
var oldBulkhead = this.bulkheads.c;
|
||||
this.bulkheads.id = index;
|
||||
this.bulkheads.c = Components.bulkheads(this.id, index);
|
||||
this.updateTotals(); // Update mass, range, shield strength, armor
|
||||
this.updateStats(this.bulkheads, this.bulkheads.c, oldBulkhead, preventUpdate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update a slot with a the component if the id is different from the current id for this slot.
|
||||
* Has logic handling components that you may only have 1 of (Shield Generator or Refinery).
|
||||
*
|
||||
* @param {object} slot The component slot
|
||||
* @param {string} id Unique ID for the selected component
|
||||
* @param {object} component Properties for the selected component
|
||||
* @param {object} slot The component slot
|
||||
* @param {string} id Unique ID for the selected component
|
||||
* @param {object} component Properties for the selected component
|
||||
* @param {boolean} preventUpdate If true, do not update aggregated stats
|
||||
*/
|
||||
Ship.prototype.use = function(slot, id, component) {
|
||||
Ship.prototype.use = function(slot, id, component, preventUpdate) {
|
||||
if (slot.id != id) { // Selecting a different component
|
||||
var slotIndex = this.internal.indexOf(slot);
|
||||
// Slot is an internal slot, is not being emptied, and the selected component group/type must be of unique
|
||||
@@ -177,17 +133,20 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
|
||||
// If another slot has an installed component with of the same type
|
||||
if (similarSlotIndex != -1 && similarSlotIndex != slotIndex) {
|
||||
// Empty the slot
|
||||
this.internal[similarSlotIndex].id = null;
|
||||
this.internal[similarSlotIndex].c = null;
|
||||
var similarSlot = this.internal[similarSlotIndex];
|
||||
this.updateStats(similarSlot, null, similarSlot.c, true); // Update stats but don't trigger a global update
|
||||
similarSlot.id = null;
|
||||
similarSlot.c = null;
|
||||
}
|
||||
}
|
||||
// Update slot with selected component (or empty)
|
||||
var oldComponent = slot.c;
|
||||
slot.id = id;
|
||||
slot.c = component;
|
||||
this.updateTotals();
|
||||
this.updateStats(slot, component, oldComponent, preventUpdate);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calculate jump range using the installed FSD and the
|
||||
* specified mass which can be more or less than ships actual mass
|
||||
@@ -205,11 +164,179 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
|
||||
* @param {string} group Component group/type
|
||||
* @return {number} The index of the slot in ship.internal
|
||||
*/
|
||||
Ship.prototype.findInternalByGroup = function(group) {
|
||||
Ship.prototype.findInternalByGroup = function (group) {
|
||||
return _.findIndex(this.internal, function (slot) {
|
||||
return slot.c && slot.c.grp == group;
|
||||
});
|
||||
};
|
||||
|
||||
Ship.prototype.changePriority = function (slot, newPriority) {
|
||||
if(newPriority >= 0 && newPriority < this.priorityBands.length) {
|
||||
var oldPriority = slot.priority;
|
||||
slot.priority = newPriority;
|
||||
|
||||
if (slot.enabled) {
|
||||
var usage = (slot.c.passive || this.hardpoints.indexOf(slot) == -1)? 'retracted' : 'deployed';
|
||||
this.priorityBands[oldPriority][usage] -= slot.c.power;
|
||||
this.priorityBands[newPriority][usage] += slot.c.power;
|
||||
this.updatePower();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Ship.prototype.setCostIncluded = function (item, included) {
|
||||
if (item.incCost != included && item.c) {
|
||||
this.totalCost += included? item.c.cost : -item.c.cost;
|
||||
}
|
||||
item.incCost = included;
|
||||
}
|
||||
|
||||
Ship.prototype.setSlotEnabled = function (slot, enabled) {
|
||||
if (slot.enabled != enabled && slot.c) { // Enabled state is changing
|
||||
var usage = (slot.c.passive || this.hardpoints.indexOf(slot) == -1)? 'retracted' : 'deployed';
|
||||
this.priorityBands[slot.priority][usage] += enabled? slot.c.power : -slot.c.power;
|
||||
this.updatePower();
|
||||
}
|
||||
slot.enabled = enabled;
|
||||
}
|
||||
|
||||
Ship.prototype.getSlotStatus = function (slot, deployed) {
|
||||
if(!slot.c) { // Empty Slot
|
||||
return 0; // No Status (Not possible)
|
||||
}
|
||||
else if (!slot.enabled) {
|
||||
return 1; // Disabled
|
||||
}
|
||||
else if (deployed) {
|
||||
return this.priorityBands[slot.priority].deployedSum > this.powerAvailable? 2 : 3; // Offline : Online
|
||||
}
|
||||
else if (this.hardpoints.indexOf(slot) != -1 && !slot.c.passive) { // Active hardpoints have no retracted status
|
||||
return 0; // No Status (Not possible)
|
||||
}
|
||||
return this.priorityBands[slot.priority].retractedSum > this.powerAvailable? 2 : 3; // Offline : Online
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ship's cumulative and aggregated stats based on the component change.
|
||||
*/
|
||||
Ship.prototype.updateStats = function(slot, n, old, preventUpdate) {
|
||||
var isHardPoint = this.hardpoints.indexOf(slot) != -1;
|
||||
var powerChange = slot == this.common[0];
|
||||
|
||||
if (old) { // Old component now being removed
|
||||
switch (old.grp) {
|
||||
case 'ft':
|
||||
this.fuelCapacity -= old.capacity;
|
||||
break;
|
||||
case 'cr':
|
||||
this.cargoCapacity -= old.capacity;
|
||||
break;
|
||||
case 'hr':
|
||||
this.armourAdded -= old.armouradd;
|
||||
break;
|
||||
case 'sb':
|
||||
this.shieldMultiplier -= old.shieldmul;
|
||||
break;
|
||||
}
|
||||
|
||||
if (slot.incCost && old.cost) {
|
||||
this.totalCost -= old.cost;
|
||||
}
|
||||
|
||||
if(old.power) {
|
||||
this.priorityBands[slot.priority][(isHardPoint && !old.passive)? 'deployed' : 'retracted'] -= old.power;
|
||||
powerChange = true;
|
||||
}
|
||||
this.unladenMass -= old.mass || 0;
|
||||
}
|
||||
|
||||
if (n) {
|
||||
switch (n.grp) {
|
||||
case 'ft':
|
||||
this.fuelCapacity += n.capacity;
|
||||
break;
|
||||
case 'cr':
|
||||
this.cargoCapacity += n.capacity;
|
||||
break;
|
||||
case 't':
|
||||
this.maxMass = n.maxmass;
|
||||
break;
|
||||
case 'hr':
|
||||
this.armourAdded += n.armouradd;
|
||||
break;
|
||||
case 'sb':
|
||||
this.shieldMultiplier += n.shieldmul;
|
||||
break;
|
||||
}
|
||||
|
||||
if (slot.incCost && n.cost) {
|
||||
this.totalCost += n.cost;
|
||||
}
|
||||
|
||||
if (n.power) {
|
||||
this.priorityBands[slot.priority][(isHardPoint && !n.passive)? 'deployed' : 'retracted'] += n.power;
|
||||
powerChange = true;
|
||||
}
|
||||
this.unladenMass += n.mass || 0;
|
||||
}
|
||||
|
||||
this.ladenMass = this.unladenMass + this.cargoCapacity + this.fuelCapacity;
|
||||
this.armourTotal = this.armourAdded + this.armour;
|
||||
|
||||
if(!preventUpdate) {
|
||||
if (powerChange) {
|
||||
this.updatePower();
|
||||
}
|
||||
this.updateJumpStats();
|
||||
this.updateShieldStrength();
|
||||
}
|
||||
};
|
||||
|
||||
Ship.prototype.updatePower = function() {
|
||||
var bands = this.priorityBands;
|
||||
var prevRetracted = 0, prevDeployed = 0;
|
||||
|
||||
for(var i = 0, l = bands.length; i < l; i++) {
|
||||
var band = bands[i];
|
||||
prevRetracted = band.retractedSum = prevRetracted + band.retracted;
|
||||
prevDeployed = band.deployedSum = prevDeployed + band.deployed + band.retracted;
|
||||
}
|
||||
|
||||
this.powerAvailable = this.common[0].c.pGen;
|
||||
this.powerRetracted = prevRetracted;
|
||||
this.powerDeployed = prevDeployed;
|
||||
};
|
||||
|
||||
Ship.prototype.updateShieldStrength = function() {
|
||||
var sgSI = this.findInternalByGroup('sg'); // Find Shield Generator slot Index if any
|
||||
this.shieldStrength = sgSI != -1? calcShieldStrength(this.mass, this.shields, this.internal[sgSI].c, this.shieldMultiplier) : 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Jump Range and total range calculations
|
||||
*/
|
||||
Ship.prototype.updateJumpStats = function() {
|
||||
var fsd = this.common[2].c; // Frame Shift Drive;
|
||||
var fuelRemaining = this.fuelCapacity % fsd.maxfuel; // Fuel left after making N max jumps
|
||||
var jumps = this.fuelCapacity / fsd.maxfuel;
|
||||
this.unladenRange = calcJumpRange(this.unladenMass + fsd.maxfuel, fsd, this.fuelCapacity); // Include fuel weight for jump
|
||||
this.fullTankRange = calcJumpRange(this.unladenMass + this.fuelCapacity, fsd, this.fuelCapacity); // Full Tanke
|
||||
this.ladenRange = calcJumpRange(this.ladenMass, fsd, this.fuelCapacity);
|
||||
this.maxJumpCount = Math.ceil(jumps); // Number of full fuel jumps + final jump to empty tank
|
||||
|
||||
// Going backwards, start with the last jump using the remaining fuel
|
||||
this.unladenTotalRange = fuelRemaining > 0? calcJumpRange(this.unladenMass + fuelRemaining, fsd, fuelRemaining): 0;
|
||||
this.ladenTotalRange = fuelRemaining > 0? calcJumpRange(this.unladenMass + this.cargoCapacity + fuelRemaining, fsd, fuelRemaining): 0;
|
||||
|
||||
// For each max fuel jump, calculate the max jump range based on fuel left in the tank
|
||||
for (var j = 0, l = Math.floor(jumps); j < l; j++) {
|
||||
fuelRemaining += fsd.maxfuel;
|
||||
this.unladenTotalRange += calcJumpRange(this.unladenMass + fuelRemaining, fsd);
|
||||
this.ladenTotalRange += calcJumpRange(this.unladenMass + this.cargoCapacity + fuelRemaining, fsd);
|
||||
}
|
||||
};
|
||||
|
||||
return Ship;
|
||||
}]);
|
||||
|
||||
@@ -19,41 +19,51 @@ angular.module('shipyard', ['ngLodash'])
|
||||
'Sensors',
|
||||
'Fuel Tank'
|
||||
])
|
||||
.value('internalGroupMap', {
|
||||
fs:'Fuel Scoop',
|
||||
// Map to lookup group labels/names for component grp
|
||||
.value('GroupMap', {
|
||||
// Common
|
||||
pp:'Power Plant',
|
||||
t:'Thrusters',
|
||||
fsd:'Frame Shift Drive',
|
||||
ls:'Life Support',
|
||||
pd:'Power Distributor',
|
||||
s:'Sensors',
|
||||
ft:'Fuel Tank',
|
||||
|
||||
// Internal
|
||||
fs:'Fuel Scoop',
|
||||
sc:'Scanners',
|
||||
am:'Auto Field-Maintenance Unit',
|
||||
am:'Auto Field-Maint. Unit',
|
||||
cr:'Cargo Racks',
|
||||
fi:'FSD Interdictor',
|
||||
hb:'Hatch Breaker Limpet Ctrl',
|
||||
hr:'Hull Reinforcement Package',
|
||||
rf:'Refinery',
|
||||
sb:'Shield Cell Bank',
|
||||
scb:'Shield Cell Bank',
|
||||
sg:'Shield Generator',
|
||||
dc:'Docking Computer',
|
||||
fx:'Fuel Transfer Limpet Ctrl',
|
||||
pc:'Prospector Limpet Ctrl',
|
||||
cc:'Collector Limpet Ctrl'
|
||||
})
|
||||
.value('hardpointsGroupMap', {
|
||||
'bl': "Beam Laser",
|
||||
'ul': "Burst Laser",
|
||||
'c': "Cannon",
|
||||
'cs': "Cargo Scanner",
|
||||
'cm': "Countermeasure",
|
||||
'fc': "Fragment Cannon",
|
||||
'fs': "Frame Shift Wake Scanner",
|
||||
'kw': "Kill Warrant Scanner",
|
||||
'nl': "Mine Launcher",
|
||||
'ml': "Mining Laser",
|
||||
'mr': "Missile Rack",
|
||||
'pa': "Plasma Accelerator",
|
||||
'mc': "Multi-cannon",
|
||||
'pl': "Pulse Laser",
|
||||
'rg': "Rail Gun",
|
||||
'sb': "Shield Booster",
|
||||
'tp': "Torpedo Pylon"
|
||||
cc:'Collector Limpet Ctrl',
|
||||
|
||||
// Hard Points
|
||||
bl: "Beam Laser",
|
||||
ul: "Burst Laser",
|
||||
c: "Cannon",
|
||||
cs: "Cargo Scanner",
|
||||
cm: "Countermeasure",
|
||||
fc: "Fragment Cannon",
|
||||
ws: "Frame Shift Wake Scanner",
|
||||
kw: "Kill Warrant Scanner",
|
||||
nl: "Mine Launcher",
|
||||
ml: "Mining Laser",
|
||||
mr: "Missile Rack",
|
||||
pa: "Plasma Accelerator",
|
||||
mc: "Multi-cannon",
|
||||
pl: "Pulse Laser",
|
||||
rg: "Rail Gun",
|
||||
sb: "Shield Booster",
|
||||
tp: "Torpedo Pylon"
|
||||
})
|
||||
.value('shipPurpose', {
|
||||
mp: 'Multi Purpose',
|
||||
@@ -93,7 +103,7 @@ angular.module('shipyard', ['ngLodash'])
|
||||
title: 'Speed',
|
||||
props: ['speed', 'boost'],
|
||||
lbls: ['Thrusters', 'Boost'],
|
||||
unit: 'M/s',
|
||||
unit: 'm/s',
|
||||
fmt: 'fRound'
|
||||
},
|
||||
{ // 2
|
||||
@@ -105,13 +115,13 @@ angular.module('shipyard', ['ngLodash'])
|
||||
{ // 3
|
||||
title: 'Shields',
|
||||
props: ['shieldStrength'],
|
||||
unit: 'Mj',
|
||||
unit: 'MJ',
|
||||
fmt: 'fRound'
|
||||
},
|
||||
{ // 4
|
||||
title: 'Jump Range',
|
||||
props: ['unladenRange', 'ladenRange'],
|
||||
lbls: ['Unladen', 'Laden'],
|
||||
props: ['unladenRange', 'fullTankRange', 'ladenRange'],
|
||||
lbls: ['Max', 'Full Tank', 'Laden'],
|
||||
unit: 'LY',
|
||||
fmt: 'fRound'
|
||||
},
|
||||
@@ -146,7 +156,14 @@ angular.module('shipyard', ['ngLodash'])
|
||||
props: ['totalCost'],
|
||||
unit: 'CR',
|
||||
fmt: 'fCrd'
|
||||
}
|
||||
},
|
||||
{ // 10
|
||||
title: 'Total Range',
|
||||
props: ['unladenTotalRange', 'ladenTotalRange'],
|
||||
lbls: ['Unladen', 'Laden'],
|
||||
unit: 'LY',
|
||||
fmt: 'fRound'
|
||||
},
|
||||
])
|
||||
/**
|
||||
* Calculate the maximum single jump range based on mass and a specific FSD
|
||||
|
||||
@@ -32,13 +32,26 @@ angular.module('shipyard').service('Components', ['lodash', 'ComponentsDB', 'Shi
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Looks up the bulkhead component for a specific ship and bulkhead
|
||||
* @param {string} shipId Unique ship Id/Key
|
||||
* @param {number} bulkheadsId Id/Index for the specified bulkhead
|
||||
* @return {object} The bulkhead component object
|
||||
*/
|
||||
this.bulkheads = function(shipId, bulkheadsId) {
|
||||
return C.bulkheads[shipId][bulkheadsId];
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new ComponentSet that contains all available components
|
||||
* that the specified ship is eligible to use.
|
||||
*
|
||||
* @param {string} shipId Unique ship Id/Key
|
||||
* @return {ComponentSet} The set of components the ship can install
|
||||
*/
|
||||
this.forShip = function (shipId) {
|
||||
var ship = Ships[shipId];
|
||||
return new ComponentSet(C, ship.properties.mass, ship.slots.common, ship.slots.internal[0], ship.slots.hardpoints[0]);
|
||||
return new ComponentSet(C, ship.properties.mass + 5, ship.slots.common, ship.slots.internal[0], ship.slots.hardpoints[0]);
|
||||
};
|
||||
|
||||
}]);
|
||||
Reference in New Issue
Block a user