Compare commits

...

21 Commits

Author SHA1 Message Date
Colin McLeod
a8d66b22af Bumping version to 1.0.2 2015-07-02 20:43:13 -07:00
Colin McLeod
bdc1e622f9 UI Tweaks, scrolling fixes and improvements 2015-07-02 20:42:46 -07:00
Colin McLeod
ad8130ae9b Add missing class 7 & 8 internal fuel tanks 2015-07-02 19:24:52 -07:00
Colin McLeod
394a3bb9f1 Revert to ui-router-extras 0.0.13 due to bug introduced in 0.0.14 2015-07-02 10:41:24 -07:00
Colin McLeod
54907b462c Tweak select look and feel on firefox, safari 2015-07-01 23:41:32 -07:00
Colin McLeod
1350de1910 Empty retrofitting table color fix 2015-06-30 21:55:38 -07:00
Colin McLeod
89d3fd69e1 UI tweaks for mobile 2015-06-30 21:46:05 -07:00
Colin McLeod
7325081ec9 Version 1.0.0 :D 2015-06-30 21:09:19 -07:00
Colin McLeod
680872a302 Retrofitting costs added to outfit page 2015-06-30 21:06:12 -07:00
Colin McLeod
a3c65d6c69 Code comments 2015-06-30 19:25:57 -07:00
Colin McLeod
a189265326 Fix svg click bug in Chrome 2015-06-30 19:25:36 -07:00
Colin McLeod
b447e913ff no need to abbreviate shield details 2015-06-29 16:28:41 -07:00
Colin McLeod
b5a249fb4b Change latest version date format 2015-06-29 16:27:56 -07:00
Colin McLeod
ae081c147e Fix negative power for limpet controllers 2015-06-29 15:42:35 -07:00
Colin McLeod
3ce0d0bdd8 Add dps to outfit page 2015-06-29 15:03:04 -07:00
Colin McLeod
a71abd9fe3 Bumping version to 0.14.1 2015-06-29 14:59:30 -07:00
Colin McLeod
f1d804e3a1 Adding total DPS to outfit page and comparisons 2015-06-29 11:57:29 -07:00
Colin McLeod
aa7479d111 Updating Federal Dropship agility 2015-06-29 10:36:49 -07:00
Colin McLeod
7c23fb3884 Bumping version to 0.14.0 2015-06-26 11:00:46 -07:00
Colin McLeod
b707015d9c Fix power plant toggle power bug 2015-06-26 11:00:11 -07:00
Colin McLeod
10d5611dcd Seperate discounts for ship and components, added discounts for 5,10,15,20,25 percent off 2015-06-25 20:26:22 -07:00
26 changed files with 512 additions and 174 deletions

View File

@@ -7,7 +7,7 @@
<!-- Standard headers --> <!-- Standard headers -->
<meta name="description" content="A ship outfitting and comparison tool for Elite Dangerous"> <meta name="description" content="A ship outfitting and comparison tool for Elite Dangerous">
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
<meta name="viewport" content="width = device-width, initial-scale = 1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<link rel="manifest" href="/images/logo/manifest.json"> <link rel="manifest" href="/images/logo/manifest.json">
<link rel="icon" sizes="152x152 192x192" type="image/png" href="/images/logo/192x192.png"> <link rel="icon" sizes="152x152 192x192" type="image/png" href="/images/logo/192x192.png">
<link rel="shortcut icon" href="/images/logo/favicon.ico"> <link rel="shortcut icon" href="/images/logo/favicon.ico">

View File

@@ -1,6 +1,6 @@
angular.module('app', ['ui.router', 'ct.ui.router.extras.sticky', 'ui.sortable', 'shipyard', 'ngLodash', 'app.templates']) 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', 'GroupMap', 'Persist', .run(['$rootScope', '$location', '$window', '$document', '$state', 'commonArray', 'shipPurpose', 'shipSize', 'hardPointClass', 'GroupMap', 'Persist', 'Discounts',
function($rootScope, $location, $window, $doc, $state, CArr, shipPurpose, sz, hpc, GroupMap, Persist) { function($rootScope, $location, $window, $doc, $state, CArr, shipPurpose, sz, hpc, GroupMap, Persist, Discounts) {
// App is running as a standalone web app on tablet/mobile // App is running as a standalone web app on tablet/mobile
var isStandAlone; var isStandAlone;
// This was causing issues on Windows phones ($window.external was causing Angular js to throw an exception). Backup is to try this and set isStandAlone to false if this fails. // This was causing issues on Windows phones ($window.external was causing Angular js to throw an exception). Backup is to try this and set isStandAlone to false if this fails.
@@ -40,13 +40,18 @@ function($rootScope, $location, $window, $doc, $state, CArr, shipPurpose, sz, hp
$rootScope.HPC = hpc; $rootScope.HPC = hpc;
$rootScope.GMAP = GroupMap; $rootScope.GMAP = GroupMap;
$rootScope.insurance = { opts: [{ name: 'Standard', pct: 0.05 }, { name: 'Alpha', pct: 0.025 }, { name: 'Beta', pct: 0.035 }] }; $rootScope.insurance = { opts: [{ name: 'Standard', pct: 0.05 }, { name: 'Alpha', pct: 0.025 }, { name: 'Beta', pct: 0.035 }] };
$rootScope.discounts = { opts: [{ name: 'None', pct: 1 }, { name: 'Founders World - 10%', pct: 0.90 }] }; $rootScope. discounts = { opts: Discounts };
$rootScope.STATUS = ['', 'DISABLED', 'OFF', 'ON']; $rootScope.STATUS = ['', 'DISABLED', 'OFF', 'ON'];
$rootScope.STATUS_CLASS = ['', 'disabled', 'warning', 'secondary-disabled']; $rootScope.STATUS_CLASS = ['', 'disabled', 'warning', 'secondary-disabled'];
$rootScope.title = 'Coriolis'; $rootScope.title = 'Coriolis';
$rootScope.cName = function(c) { /**
return c.c ? c.c.name ? c.c.name : GroupMap[c.c.grp] : null; * Returns the name of the component mounted in the specified slot
* @param {Object} slot The slot object
* @return {String} The component name
*/
$rootScope.cName = function(slot) {
return slot.c ? slot.c.name ? slot.c.name : GroupMap[slot.c.grp] : null;
}; };
// Formatters // Formatters

View File

@@ -1,7 +1,8 @@
angular.module('app').controller('OutfitController', ['$window', '$rootScope', '$scope', '$state', '$stateParams', 'ShipsDB', 'Ship', 'Components', 'Serializer', 'Persist', 'calcTotalRange', 'calcSpeed', function($window, $rootScope, $scope, $state, $p, Ships, Ship, Components, Serializer, Persist, calcTotalRange, calcSpeed) { angular.module('app').controller('OutfitController', ['$window', '$rootScope', '$scope', '$state', '$stateParams', 'ShipsDB', 'Ship', 'Components', 'Serializer', 'Persist', 'calcTotalRange', 'calcSpeed', function($window, $rootScope, $scope, $state, $p, Ships, Ship, Components, Serializer, Persist, calcTotalRange, calcSpeed) {
var win = angular.element($window); // Angularized window object for event triggering
var data = Ships[$p.shipId]; // Retrieve the basic ship properties, slots and defaults 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 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 var retrofitShip = new Ship($p.shipId, data.properties, data.slots); // Create a new Ship for retrofit comparison
// Update the ship instance with the code (if provided) or the 'factory' defaults. // Update the ship instance with the code (if provided) or the 'factory' defaults.
if ($p.code) { if ($p.code) {
@@ -30,11 +31,27 @@ angular.module('app').controller('OutfitController', ['$window', '$rootScope', '
$scope.selectedSlot = null; $scope.selectedSlot = null;
$scope.savedCode = Persist.getBuild(ship.id, $scope.buildName); $scope.savedCode = Persist.getBuild(ship.id, $scope.buildName);
$scope.canSave = Persist.isEnabled(); $scope.canSave = Persist.isEnabled();
$scope.allBuilds = Persist.builds;
$scope.fuel = 0; $scope.fuel = 0;
$scope.pwrDesc = false; $scope.pwrDesc = false;
$scope.pwrPredicate = 'type'; $scope.pwrPredicate = 'type';
$scope.retroDesc = false;
$scope.retroPredicate = 'netCost';
$scope.costDesc = true; $scope.costDesc = true;
$scope.costPredicate = 'c.cost'; $scope.costPredicate = 'c.cost';
$scope.costTab = 'retrofit';
if ($scope.savedCode) {
Serializer.toShip(retrofitShip, $scope.savedCode); // Populate components from last save
$scope.retrofitBuild = $scope.buildName;
} else {
retrofitShip.buildWith(data.defaults);
$scope.retrofitBuild = null;
}
ship.applyDiscounts($rootScope.discounts.ship, $rootScope.discounts.components);
retrofitShip.applyDiscounts($rootScope.discounts.ship, $rootScope.discounts.components);
updateRetrofitCosts();
$scope.jrSeries = { $scope.jrSeries = {
xMin: 0, xMin: 0,
@@ -187,6 +204,9 @@ angular.module('app').controller('OutfitController', ['$window', '$rootScope', '
if ($scope.code != $scope.savedCode) { if ($scope.code != $scope.savedCode) {
Persist.saveBuild(ship.id, $scope.buildName, $scope.code); Persist.saveBuild(ship.id, $scope.buildName, $scope.code);
$scope.savedCode = $scope.code; $scope.savedCode = $scope.code;
if ($scope.retrofitBuild === $scope.buildName) {
Serializer.toShip(retrofitShip, $scope.code);
}
updateState($scope.code); updateState($scope.code);
} }
}; };
@@ -230,6 +250,11 @@ angular.module('app').controller('OutfitController', ['$window', '$rootScope', '
$scope.pwrPredicate = key; $scope.pwrPredicate = key;
}; };
$scope.sortRetrofit = function(key) {
$scope.retroDesc = $scope.retroPredicate == key ? !$scope.retroDesc : $scope.retroDesc;
$scope.retroPredicate = key;
};
/** /**
* Toggle the power on/off for the selected component * Toggle the power on/off for the selected component
* @param {object} item The component being toggled * @param {object} item The component being toggled
@@ -264,6 +289,15 @@ angular.module('app').controller('OutfitController', ['$window', '$rootScope', '
return ship.getSlotStatus(slot, true); return ship.getSlotStatus(slot, true);
}; };
$scope.setRetrofitBase = function() {
if ($scope.retrofitBuild) {
Serializer.toShip(retrofitShip, Persist.getBuild(ship.id, $scope.retrofitBuild));
} else {
retrofitShip.buildWith(data.defaults);
}
updateRetrofitCosts();
};
// Utilify functions // Utilify functions
function updateState(code) { function updateState(code) {
@@ -272,12 +306,61 @@ angular.module('app').controller('OutfitController', ['$window', '$rootScope', '
$scope.speedSeries.xMax = $scope.trSeries.xMax = $scope.jrSeries.xMax = ship.cargoCapacity; $scope.speedSeries.xMax = $scope.trSeries.xMax = $scope.jrSeries.xMax = ship.cargoCapacity;
$scope.jrSeries.yMax = ship.unladenRange; $scope.jrSeries.yMax = ship.unladenRange;
$scope.trSeries.yMax = ship.unladenTotalRange; $scope.trSeries.yMax = ship.unladenTotalRange;
updateRetrofitCosts();
win.triggerHandler('pwrchange'); win.triggerHandler('pwrchange');
} }
function updateRetrofitCosts() {
var costs = $scope.retrofitList = [];
var cName = $rootScope.cName;
var total = 0, i, l, item;
if (ship.bulkheads.id != retrofitShip.bulkheads.id) {
item = {
buyClassRating: ship.bulkheads.c.class + ship.bulkheads.c.rating,
buyName: cName(ship.bulkheads),
sellClassRating: retrofitShip.bulkheads.c.class + retrofitShip.bulkheads.c.rating,
sellName: cName(retrofitShip.bulkheads),
netCost: ship.bulkheads.discountedCost - retrofitShip.bulkheads.discountedCost
};
costs.push(item);
total += item.netCost;
}
for (var g in { common: 1, internal: 1, hardpoints: 1 }) {
var retroSlotGroup = retrofitShip[g];
var slotGroup = ship[g];
for (i = 0, l = slotGroup.length; i < l; i++) {
if (slotGroup[i].id != retroSlotGroup[i].id) {
item = { netCost: 0 };
if (slotGroup[i].id) {
item.buyName = cName(slotGroup[i]);
item.buyClassRating = slotGroup[i].c.class + slotGroup[i].c.rating;
item.netCost = slotGroup[i].discountedCost;
}
if (retroSlotGroup[i].id) {
item.sellName = cName(retroSlotGroup[i]);
item.sellClassRating = retroSlotGroup[i].c.class + retroSlotGroup[i].c.rating;
item.netCost -= retroSlotGroup[i].discountedCost;
}
costs.push(item);
total += item.netCost;
}
}
}
$scope.retrofitTotal = total;
}
// Hide any open menu/slot/etc if the background is clicked // Hide any open menu/slot/etc if the background is clicked
$scope.$on('close', function() { $scope.$on('close', function() {
$scope.selectedSlot = null; $scope.selectedSlot = null;
}); });
// Hide any open menu/slot/etc if the background is clicked
$scope.$on('discountChange', function() {
ship.applyDiscounts($rootScope.discounts.ship, $rootScope.discounts.components);
retrofitShip.applyDiscounts($rootScope.discounts.ship, $rootScope.discounts.components);
updateRetrofitCosts();
});
}]); }]);

View File

@@ -21,7 +21,7 @@ angular.module('app').directive('componentSelect', function() {
list.push((o.maxmass && mass > o.maxmass) ? ' disabled"' : '" cpid="', id, '">'); list.push((o.maxmass && mass > o.maxmass) ? ' disabled"' : '" cpid="', id, '">');
if (o.mode) { if (o.mode) {
list.push('<svg cpid="', id, '" class="icon lg"><use xlink:href="#mount-', o.mode, '"></use></svg> '); list.push('<svg cpid="', id, '" class="icon lg"><use cpid="', id, '" xlink:href="#mount-', o.mode, '"></use></svg> ');
} }
list.push(o.class, o.rating); list.push(o.class, o.rating);

View File

@@ -13,8 +13,10 @@ angular.module('app').directive('shipyardHeader', ['lodash', '$rootScope', 'Pers
scope.bs = Persist.state; scope.bs = Persist.state;
var insIndex = _.findIndex($rootScope.insurance.opts, 'name', Persist.getInsurance()); var insIndex = _.findIndex($rootScope.insurance.opts, 'name', Persist.getInsurance());
var savedDiscounts = Persist.getDiscount() || [1, 1];
$rootScope.insurance.current = $rootScope.insurance.opts[insIndex != -1 ? insIndex : 0]; $rootScope.insurance.current = $rootScope.insurance.opts[insIndex != -1 ? insIndex : 0];
$rootScope.discounts.current = $rootScope.discounts.opts[Persist.getDiscount() || 0]; $rootScope.discounts.ship = savedDiscounts[0];
$rootScope.discounts.components = savedDiscounts[1];
// Close menus if a navigation change event occurs // Close menus if a navigation change event occurs
$rootScope.$on('$stateChangeStart', function() { $rootScope.$on('$stateChangeStart', function() {
@@ -38,7 +40,8 @@ angular.module('app').directive('shipyardHeader', ['lodash', '$rootScope', 'Pers
* Save selected discount option * Save selected discount option
*/ */
scope.updateDiscount = function() { scope.updateDiscount = function() {
Persist.setDiscount($rootScope.discounts.opts.indexOf($rootScope.discounts.current)); Persist.setDiscount([$rootScope.discounts.ship, $rootScope.discounts.components]);
$rootScope.$broadcast('discountChange');
}; };
scope.openMenu = function(e, menu) { scope.openMenu = function(e, menu) {

View File

@@ -189,7 +189,7 @@ angular.module('app').service('Persist', ['$window', 'lodash', function($window,
*/ */
this.setDiscount = function(val) { this.setDiscount = function(val) {
if (this.lsEnabled) { if (this.lsEnabled) {
return localStorage.setItem('discount', val); return localStorage.setItem('discounts', angular.toJson(val));
} }
}; };
@@ -199,7 +199,7 @@ angular.module('app').service('Persist', ['$window', 'lodash', function($window,
*/ */
this.getDiscount = function() { this.getDiscount = function() {
if (this.lsEnabled) { if (this.lsEnabled) {
return localStorage.getItem('discount'); return angular.fromJson(localStorage.getItem('discounts'));
} }
return null; return null;
}; };

View File

@@ -28,7 +28,7 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
function Ship(id, properties, slots) { function Ship(id, properties, slots) {
this.id = id; this.id = id;
this.cargoScoop = { c: Components.cargoScoop(), type: 'SYS' }; this.cargoScoop = { c: Components.cargoScoop(), type: 'SYS' };
this.bulkheads = { incCost: true, maxClass: 8, discount: 1 }; this.bulkheads = { incCost: true, maxClass: 8 };
for (var p in properties) { this[p] = properties[p]; } // Copy all base properties from shipData for (var p in properties) { this[p] = properties[p]; } // Copy all base properties from shipData
@@ -36,10 +36,11 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
var slotGroup = slots[slotType]; var slotGroup = slots[slotType];
var group = this[slotType] = []; // Initialize Slot group (Common, Hardpoints, Internal) var group = this[slotType] = []; // Initialize Slot group (Common, Hardpoints, Internal)
for (var i = 0; i < slotGroup.length; i++) { for (var i = 0; i < slotGroup.length; i++) {
group.push({ id: null, c: null, incCost: true, maxClass: slotGroup[i], discount: 1 }); group.push({ id: null, c: null, incCost: true, maxClass: slotGroup[i] });
} }
} }
this.c = { incCost: true, discount: 1, c: { name: this.name, cost: this.cost } }; // Make a 'Ship' component similar to other components // Make a Ship 'slot'/item similar to other slots
this.c = { incCost: true, type: 'SHIP', discountedCost: this.cost, c: { name: this.name, cost: this.cost } };
this.costList = _.union(this.internal, this.common, this.hardpoints); this.costList = _.union(this.internal, this.common, this.hardpoints);
this.costList.push(this.bulkheads); // Add The bulkheads this.costList.push(this.bulkheads); // Add The bulkheads
@@ -54,6 +55,9 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
this.powerList.unshift(this.common[2]); // Add FSD this.powerList.unshift(this.common[2]); // Add FSD
this.powerList.unshift(this.common[0]); // Add Power Plant this.powerList.unshift(this.common[0]); // Add Power Plant
this.shipDiscount = 1;
this.componentDiscount = 1;
this.priorityBands = [ this.priorityBands = [
{ deployed: 0, retracted: 0, retOnly: 0 }, { deployed: 0, retracted: 0, retOnly: 0 },
{ deployed: 0, retracted: 0, retOnly: 0 }, { deployed: 0, retracted: 0, retOnly: 0 },
@@ -81,9 +85,10 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
this.ladenMass = 0; this.ladenMass = 0;
this.armourAdded = 0; this.armourAdded = 0;
this.shieldMultiplier = 1; this.shieldMultiplier = 1;
this.totalCost = this.cost; this.totalCost = this.c.incCost ? this.c.discountedCost : 0;
this.unladenMass = this.mass; this.unladenMass = this.mass;
this.armourTotal = this.armour; this.armourTotal = this.armour;
this.totalDps = 0;
this.bulkheads.c = null; this.bulkheads.c = null;
this.useBulkhead(comps.bulkheads || 0, true); this.useBulkhead(comps.bulkheads || 0, true);
@@ -106,6 +111,7 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
common[i].priority = priorities ? priorities[i + 1] * 1 : 0; common[i].priority = priorities ? priorities[i + 1] * 1 : 0;
common[i].type = 'SYS'; common[i].type = 'SYS';
common[i].c = common[i].id = null; // Resetting 'old' component if there was one common[i].c = common[i].id = null; // Resetting 'old' component if there was one
common[i].discountedCost = 0;
this.use(common[i], comps.common[i], Components.common(i, comps.common[i]), true); this.use(common[i], comps.common[i], Components.common(i, comps.common[i]), true);
} }
@@ -119,6 +125,7 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
hps[i].priority = priorities ? priorities[cl + i] * 1 : 0; hps[i].priority = priorities ? priorities[cl + i] * 1 : 0;
hps[i].type = hps[i].maxClass ? 'WEP' : 'SYS'; hps[i].type = hps[i].maxClass ? 'WEP' : 'SYS';
hps[i].c = hps[i].id = null; // Resetting 'old' component if there was one hps[i].c = hps[i].id = null; // Resetting 'old' component if there was one
hps[i].discountedCost = 0;
if (comps.hardpoints[i] !== 0) { if (comps.hardpoints[i] !== 0) {
this.use(hps[i], comps.hardpoints[i], Components.hardpoints(comps.hardpoints[i]), true); this.use(hps[i], comps.hardpoints[i], Components.hardpoints(comps.hardpoints[i]), true);
@@ -133,6 +140,7 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
internal[i].priority = priorities ? priorities[cl + i] * 1 : 0; internal[i].priority = priorities ? priorities[cl + i] * 1 : 0;
internal[i].type = 'SYS'; internal[i].type = 'SYS';
internal[i].id = internal[i].c = null; // Resetting 'old' component if there was one internal[i].id = internal[i].c = null; // Resetting 'old' component if there was one
internal[i].discountedCost = 0;
if (comps.internal[i] !== 0) { if (comps.internal[i] !== 0) {
this.use(internal[i], comps.internal[i], Components.internal(comps.internal[i]), true); this.use(internal[i], comps.internal[i], Components.internal(comps.internal[i]), true);
@@ -149,6 +157,7 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
var oldBulkhead = this.bulkheads.c; var oldBulkhead = this.bulkheads.c;
this.bulkheads.id = index; this.bulkheads.id = index;
this.bulkheads.c = Components.bulkheads(this.id, index); this.bulkheads.c = Components.bulkheads(this.id, index);
this.bulkheads.discountedCost = this.bulkheads.c.cost * this.componentDiscount;
this.updateStats(this.bulkheads, this.bulkheads.c, oldBulkhead, preventUpdate); this.updateStats(this.bulkheads, this.bulkheads.c, oldBulkhead, preventUpdate);
}; };
@@ -171,11 +180,13 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
if (!preventUpdate && similarSlot && similarSlot !== slot) { if (!preventUpdate && similarSlot && similarSlot !== slot) {
this.updateStats(similarSlot, null, similarSlot.c, true); // Update stats but don't trigger a global update this.updateStats(similarSlot, null, similarSlot.c, true); // Update stats but don't trigger a global update
similarSlot.id = similarSlot.c = null; // Empty the slot similarSlot.id = similarSlot.c = null; // Empty the slot
similarSlot.discountedCost = 0;
} }
} }
var oldComponent = slot.c; var oldComponent = slot.c;
slot.id = id; slot.id = id;
slot.c = component; slot.c = component;
slot.discountedCost = (component && component.cost) ? component.cost * this.componentDiscount : 0;
this.updateStats(slot, component, oldComponent, preventUpdate); this.updateStats(slot, component, oldComponent, preventUpdate);
} }
}; };
@@ -232,7 +243,7 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
Ship.prototype.setCostIncluded = function(item, included) { Ship.prototype.setCostIncluded = function(item, included) {
if (item.incCost != included && item.c) { if (item.incCost != included && item.c) {
this.totalCost += included ? item.c.cost : -item.c.cost; this.totalCost += included ? item.discountedCost : -item.discountedCost;
} }
item.incCost = included; item.incCost = included;
}; };
@@ -248,6 +259,8 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
} else if (slot.c.grp == 'sb') { } else if (slot.c.grp == 'sb') {
this.shieldMultiplier += slot.c.shieldmul * (enabled ? 1 : -1); this.shieldMultiplier += slot.c.shieldmul * (enabled ? 1 : -1);
this.updateShieldStrength(); this.updateShieldStrength();
} else if (slot.c.dps) {
this.totalDps += slot.c.dps * (enabled ? 1 : -1);
} }
this.updatePower(); this.updatePower();
@@ -292,12 +305,16 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
} }
if (slot.incCost && old.cost) { if (slot.incCost && old.cost) {
this.totalCost -= old.cost; this.totalCost -= old.cost * this.componentDiscount;
} }
if (old.power && slot.enabled) { if (old.power && slot.enabled) {
this.priorityBands[slot.priority][powerUsageType(slot, old)] -= old.power; this.priorityBands[slot.priority][powerUsageType(slot, old)] -= old.power;
powerChange = true; powerChange = true;
if (old.dps) {
this.totalDps -= old.dps;
}
} }
this.unladenMass -= old.mass || 0; this.unladenMass -= old.mass || 0;
} }
@@ -322,12 +339,16 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
} }
if (slot.incCost && n.cost) { if (slot.incCost && n.cost) {
this.totalCost += n.cost; this.totalCost += n.cost * this.componentDiscount;
} }
if (n.power && slot.enabled) { if (n.power && slot.enabled) {
this.priorityBands[slot.priority][powerUsageType(slot, n)] += n.power; this.priorityBands[slot.priority][powerUsageType(slot, n)] += n.power;
powerChange = true; powerChange = true;
if (n.dps) {
this.totalDps += n.dps;
}
} }
this.unladenMass += n.mass || 0; this.unladenMass += n.mass || 0;
} }
@@ -377,5 +398,28 @@ angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength',
this.maxJumpCount = Math.ceil(this.fuelCapacity / fsd.maxfuel); this.maxJumpCount = Math.ceil(this.fuelCapacity / fsd.maxfuel);
}; };
/**
* Recalculate all item costs and total based on discounts.
* @param {number} shipDiscount Ship cost multiplier discount (e.g. 0.9 === 10% discount)
* @param {number} componentDiscount Component cost multiplier discount (e.g. 0.75 === 25% discount)
*/
Ship.prototype.applyDiscounts = function(shipDiscount, componentDiscount) {
var total = 0;
var costList = this.costList;
for (var i = 0, l = costList.length; i < l; i++) {
var item = costList[i];
if (item.c && item.c.cost) {
item.discountedCost = item.c.cost * (item.type == 'SHIP' ? shipDiscount : componentDiscount);
if (item.incCost) {
total += item.discountedCost;
}
}
}
this.shipDiscount = shipDiscount;
this.componentDiscount = componentDiscount;
this.totalCost = total;
};
return Ship; return Ship;
}]); }]);

View File

@@ -163,8 +163,28 @@ angular.module('shipyard', ['ngLodash'])
lbls: ['Unladen', 'Laden'], lbls: ['Unladen', 'Laden'],
unit: 'LY', unit: 'LY',
fmt: 'fRound' fmt: 'fRound'
},
{ // 11
title: 'DPS',
props: ['totalDps'],
lbls: ['Dps'],
unit: '',
fmt: 'fRound'
} }
]) ])
/**
* Set of all available / theoretical discounts
*
* @type {Object}
*/
.value('Discounts', {
'None': 1,
'5%': 0.95,
'10%': 0.90,
'15%': 0.85,
'20%': 0.80,
'25%': 0.75
})
/** /**
* Calculate the maximum single jump range based on mass and a specific FSD * Calculate the maximum single jump range based on mass and a specific FSD
* *

View File

@@ -22,7 +22,9 @@
html, body { html, body {
height: 100%; height: 100%;
width: 100%;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
overflow-x: hidden;
} }
body { body {
@@ -40,7 +42,9 @@ div, a, li {
#main { #main {
margin: 0; margin: 0;
padding: 0.5em 0; padding: 0.5em 0.5em;
width: 100%;
box-sizing: border-box;
min-height: 90%; min-height: 90%;
clear: both; clear: both;
text-align: center; text-align: center;

View File

@@ -84,6 +84,19 @@ header {
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
max-height: 500px; max-height: 500px;
&::-webkit-scrollbar {
width: 0.5em;
}
&::-webkit-scrollbar-track {
background-color: transparent;
}
&::-webkit-scrollbar-thumb {
background-color: @warning-disabled;
}
.smallTablet({ .smallTablet({
max-height: 400px; max-height: 400px;
left: 0; left: 0;

View File

@@ -59,7 +59,7 @@
input { input {
background: none; background: none;
line-height: 1.3em; line-height: 1.3em;
width: 20em; width: 15em;
font-size: 0.9em; font-size: 0.9em;
box-sizing: border-box; box-sizing: border-box;
display: inline-block; display: inline-block;
@@ -99,6 +99,12 @@
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.optional-hide {
.largePhone({
display: none;
});
}
table.total { table.total {
width: 100%; width: 100%;
@@ -113,6 +119,26 @@ table.total {
} }
} }
.tabs {
width: 100%;
box-sizing: border-box;
margin-bottom: 1px;
&, th {
border-collapse: collapse;
color: @primary-disabled;
background-color: @primary-bg;
border: 1px solid @primary-disabled;
padding-top: 1px;
}
.active {
color: @primary-bg;
background-color: @primary-disabled;
}
}
.group { .group {
width: 25%; width: 25%;
padding: 0.5em 0.2em; padding: 0.5em 0.2em;
@@ -139,13 +165,14 @@ table.total {
font-weight: normal; font-weight: normal;
} }
tbody tr:hover {
background-color: @warning-bg;
}
.smallTablet({ .smallTablet({
width: 50%; width: 50%;
.axis.x {
g.tick:nth-child(2n + 1) text {
display: none;
}
}
}); });
.largePhone({ .largePhone({
@@ -169,14 +196,6 @@ table.total {
&.third { &.third {
width: 33%; width: 33%;
.smallTablet({
.axis.x {
g.tick:nth-child(2n + 1) text {
display: none;
}
}
});
.largePhone({ .largePhone({
width: 100% !important; width: 100% !important;
}); });
@@ -233,9 +252,6 @@ table.total {
&:nth-child(3) { &:nth-child(3) {
display: none; display: none;
} }
&:nth-child(2) {
font-size: 0.7em;
}
} }
}); });

View File

@@ -1,3 +1,27 @@
select {
.border-radius(0);
cursor: pointer;
background: none;
color: @primary-disabled;
font-family: @fStandard;
font-size: 1em;
background-color: transparent;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
padding: 0.1em 0.5em;
outline:none;
border: 0;
&:focus {
outline:none;
}
&::-moz-focus-inner {
border: 0;
}
}
.select { .select {
color: @primary-disabled; color: @primary-disabled;
position: absolute; position: absolute;
@@ -15,6 +39,19 @@
white-space: nowrap; white-space: nowrap;
text-align: center; text-align: center;
&::-webkit-scrollbar {
width: 0.5em;
}
&::-webkit-scrollbar-track {
background-color: transparent;
border-left: 1px solid @primary;
}
&::-webkit-scrollbar-thumb {
background-color: @primary-disabled;
}
.select-group { .select-group {
line-height: 1.5em; line-height: 1.5em;
text-align: left; text-align: left;

View File

@@ -43,7 +43,6 @@ thead {
} }
} }
tbody tr { tbody tr {
&.tr { &.tr {
@@ -55,6 +54,12 @@ tbody tr {
} }
} }
&.highlight {
&:hover {
background-color: @warning-bg;
}
}
td { td {
line-height: 1.4em; line-height: 1.4em;
padding: 0 0.3em; padding: 0 0.3em;

View File

@@ -53,8 +53,12 @@
<li><select ng-model="insurance.current" ng-options="ins.name for (i,ins) in insurance.opts" ng-change="updateInsurance()"></select></li> <li><select ng-model="insurance.current" ng-options="ins.name for (i,ins) in insurance.opts" ng-change="updateInsurance()"></select></li>
</ul><br> </ul><br>
<ul> <ul>
Discount Ship Discount
<li><select ng-model="discounts.current" ng-options="d.name for (i,d) in discounts.opts" ng-change="updateDiscount()"></select></li> <li><select ng-model="discounts.ship" ng-options="i for (i,d) in discounts.opts" ng-change="updateDiscount()"></select></li>
</ul><br>
<ul>
Component Discount
<li><select ng-model="discounts.components" ng-options="i for (i,d) in discounts.opts" ng-change="updateDiscount()"></select></li>
</ul> </ul>
<hr /> <hr />
<ul> <ul>

View File

@@ -4,8 +4,8 @@
{{c.c.class}}{{c.c.rating}} {{c.c.name || lbl}} {{c.c.class}}{{c.c.rating}} {{c.c.name || lbl}}
<div class="r">{{c.c.mass || c.c.capacity || '0'}} <u>T</u></div> <div class="r">{{c.c.mass || c.c.capacity || '0'}} <u>T</u></div>
<div class="cb"></div> <div class="cb"></div>
<div class="l" ng-if="c.c.optmass">Opt: {{c.c.optmass}} <u>T</u></div> <div class="l" ng-if="c.c.optmass">Optimal Mass: {{c.c.optmass}} <u>T</u></div>
<div class="l" ng-if="c.c.maxmass">Max: {{c.c.maxmass}} <u>T</u></div> <div class="l" ng-if="c.c.maxmass">Max Mass: {{c.c.maxmass}} <u>T</u></div>
<div class="l" ng-if="c.c.bins">{{c.c.bins}} <u>Bins</u></div> <div class="l" ng-if="c.c.bins">{{c.c.bins}} <u>Bins</u></div>
<div class="l" ng-if="c.c.rate">Rate: {{c.c.rate}} <u>Kg/s</u>&nbsp;&nbsp;&nbsp;Refuel Time: {{$r.fTime(fuel * 1000 / c.c.rate)}}</div> <div class="l" ng-if="c.c.rate">Rate: {{c.c.rate}} <u>Kg/s</u>&nbsp;&nbsp;&nbsp;Refuel Time: {{$r.fTime(fuel * 1000 / c.c.rate)}}</div>
<div class="l" ng-if="c.c.ammo">Ammo: {{c.c.ammo}}</div> <div class="l" ng-if="c.c.ammo">Ammo: {{c.c.ammo}}</div>

View File

@@ -6,7 +6,7 @@
</div> </div>
<div ng-show="processed"> <div ng-show="processed">
<table class="l" style="overflow:hidden;margin: 1em 0;"> <table class="l" style="overflow:hidden;margin: 1em 0; width: 100%;">
<thead><tr><th>Ship</th><th>Build Name</th><th>Action</th></tr></thead> <thead><tr><th>Ship</th><th>Build Name</th><th>Action</th></tr></thead>
<tbody ng-repeat="(shipId,shipBuilds) in builds"> <tbody ng-repeat="(shipId,shipBuilds) in builds">
<tr class="cb" ng-repeat="(buildName, b) in shipBuilds"> <tr class="cb" ng-repeat="(buildName, b) in shipBuilds">

View File

@@ -30,16 +30,18 @@
<th rowspan="2">Agility</th> <th rowspan="2">Agility</th>
<th rowspan="2">Speed</th> <th rowspan="2">Speed</th>
<th rowspan="2">Boost</th> <th rowspan="2">Boost</th>
<th rowspan="2">DPS</th>
<th rowspan="2">Armour</th> <th rowspan="2">Armour</th>
<th rowspan="2">Shields</th> <th rowspan="2">Shields</th>
<th colspan="2">Mass</th> <th colspan="3">Mass</th>
<th rowspan="2">Cargo</th> <th rowspan="2">Cargo</th>
<th rowspan="2">Fuel</th> <th rowspan="2">Fuel</th>
<th colspan="3">Jump Range</th> <th colspan="3">Jump Range</th>
<th colspan="3">Total Range</th> <th colspan="3">Total Range</th>
</tr> </tr>
<tr> <tr>
<th class="lft">Unladen</th> <th class="lft">Hull</th>
<th>Unladen</th>
<th>Laden</th> <th>Laden</th>
<th class="lft">Max</th> <th class="lft">Max</th>
<th>Full Tank</th> <th>Full Tank</th>
@@ -55,8 +57,10 @@
<td>{{ship.agility}}/10</td> <td>{{ship.agility}}/10</td>
<td>{{fRound(ship.speed)}} <u>m/s</u></td> <td>{{fRound(ship.speed)}} <u>m/s</u></td>
<td>{{fRound(ship.boost)}} <u>m/s</u></td> <td>{{fRound(ship.boost)}} <u>m/s</u></td>
<td>{{fRound(ship.totalDps)}}</td>
<td>{{ship.armourTotal}} <span ng-if="ship.armourAdded">({{ship.armour}} + {{ship.armourAdded}})</span></td> <td>{{ship.armourTotal}} <span ng-if="ship.armourAdded">({{ship.armour}} + {{ship.armourAdded}})</span></td>
<td>{{fRound(ship.shieldStrength)}} <u>MJ</u> <span ng-if="ship.shieldMultiplier > 1 && ship.shieldStrength > 0">({{fRPct(ship.shieldMultiplier)}})</span></td> <td>{{fRound(ship.shieldStrength)}} <u>MJ</u> <span ng-if="ship.shieldMultiplier > 1 && ship.shieldStrength > 0">({{fRPct(ship.shieldMultiplier)}})</span></td>
<td>{{ship.mass}} <u>T</u></td>
<td>{{fRound(ship.unladenMass)}} <u>T</u></td> <td>{{fRound(ship.unladenMass)}} <u>T</u></td>
<td>{{fRound(ship.ladenMass)}} <u>T</u></td> <td>{{fRound(ship.ladenMass)}} <u>T</u></td>
<td>{{fRound(ship.cargoCapacity)}} <u>T</u></td> <td>{{fRound(ship.cargoCapacity)}} <u>T</u></td>
@@ -72,6 +76,26 @@
</table> </table>
</div> </div>
<div id="hardpoints" class="group">
<h1>HardPoints</h1>
<div class="slot" ng-repeat="h in ship.hardpoints | filter:{maxClass: '!0'}" ng-click="selectSlot($event, h)" context-menu="select('h', h, $event, 'empty')" ng-class="{selected: selectedSlot==h}">
<div slot-hardpoint class="details" hp="h" size="HPC[h.maxClass]" lbl="GMAP[h.c.grp]"></div>
<div class="select" ng-class="{hardpoint: h.maxClass > 0}" ng-if="selectedSlot==h" ng-click="select('h',h,$event)">
<div component-select s="h" groups="availCS.getHps(h.maxClass)"></div>
</div>
</div>
</div>
<div id="utility" class="group">
<h1>Utility Mounts</h1>
<div class="slot" ng-repeat="h in ship.hardpoints | filter:{maxClass: '0'}" ng-click="selectSlot($event, h)" context-menu="select('h', h, $event, 'empty')" ng-class="{selected: selectedSlot==h}">
<div slot-hardpoint class="details" hp="h" size="HPC[h.maxClass]" lbl="GMAP[h.c.grp]"></div>
<div class="select" ng-class="{hardpoint: h.maxClass > 0}" ng-if="selectedSlot==h" ng-click="select('h',h,$event)">
<div component-select s="h" groups="availCS.getHps(h.maxClass)"></div>
</div>
</div>
</div>
<div id="standard" class="group"> <div id="standard" class="group">
<h1>Standard</h1> <h1>Standard</h1>
<div class="slot" ng-click="selectSlot($event, ship.bulkheads)" ng-class="{selected: selectedSlot==ship.bulkheads}"> <div class="slot" ng-click="selectSlot($event, ship.bulkheads)" ng-class="{selected: selectedSlot==ship.bulkheads}">
@@ -176,26 +200,6 @@
</div> </div>
</div> </div>
<div id="hardpoints" class="group">
<h1>HardPoints</h1>
<div class="slot" ng-repeat="h in ship.hardpoints | filter:{maxClass: '!0'}" ng-click="selectSlot($event, h)" context-menu="select('h', h, $event, 'empty')" ng-class="{selected: selectedSlot==h}">
<div slot-hardpoint class="details" hp="h" size="HPC[h.maxClass]" lbl="GMAP[h.c.grp]"></div>
<div class="select" ng-class="{hardpoint: h.maxClass > 0}" ng-if="selectedSlot==h" ng-click="select('h',h,$event)">
<div component-select s="h" groups="availCS.getHps(h.maxClass)"></div>
</div>
</div>
</div>
<div id="utility" class="group">
<h1>Utility Mounts</h1>
<div class="slot" ng-repeat="h in ship.hardpoints | filter:{maxClass: '0'}" ng-click="selectSlot($event, h)" context-menu="select('h', h, $event, 'empty')" ng-class="{selected: selectedSlot==h}">
<div slot-hardpoint class="details" hp="h" size="HPC[h.maxClass]" lbl="GMAP[h.c.grp]"></div>
<div class="select" ng-class="{hardpoint: h.maxClass > 0}" ng-if="selectedSlot==h" ng-click="select('h',h,$event)">
<div component-select s="h" groups="availCS.getHps(h.maxClass)"></div>
</div>
</div>
</div>
<div class="group half" id="componentPriority"> <div class="group half" id="componentPriority">
<table style="width:100%"> <table style="width:100%">
<thead> <thead>
@@ -210,7 +214,7 @@
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td ng-click="togglePwr(c)">{{pp.c.class}}{{pp.c.rating}}</td> <td>{{pp.c.class}}{{pp.c.rating}}</td>
<td class="le shorten">Power Plant</td> <td class="le shorten">Power Plant</td>
<td><u>SYS</u></td> <td><u>SYS</u></td>
<td>1</td> <td>1</td>
@@ -220,7 +224,7 @@
<td></td> <td></td>
</tr> </tr>
<tr><td style="line-height:0;" colspan="8"><hr style="margin: 0 0 3px;background: #ff8c0d;border: 0;height: 1px;" /></td></tr> <tr><td style="line-height:0;" colspan="8"><hr style="margin: 0 0 3px;background: #ff8c0d;border: 0;height: 1px;" /></td></tr>
<tr ng-repeat="c in powerList | orderBy:pwrPredicate:pwrDesc" ng-if="c.c.power" ng-class="{disabled:!c.enabled}"> <tr class="highlight" ng-repeat="c in powerList | orderBy:pwrPredicate:pwrDesc" ng-if="c.c.power" ng-class="{disabled:!c.enabled}">
<td style="width:1em;" ng-click="togglePwr(c)">{{c.c.class}}{{c.c.rating}}</td> <td style="width:1em;" ng-click="togglePwr(c)">{{c.c.class}}{{c.c.rating}}</td>
<td class="le shorten" ng-click="togglePwr(c)" ng-bind="cName(c)"></td> <td class="le shorten" ng-click="togglePwr(c)" ng-bind="cName(c)"></td>
<td ng-click="togglePwr(c)"><u ng-bind="c.type"></u></td> <td ng-click="togglePwr(c)"><u ng-bind="c.type"></u></td>
@@ -237,31 +241,90 @@
</div> </div>
<div class="group half"> <div class="group half">
<table style="width:100%"> <table class="tabs">
<thead> <thead>
<tr class="main"> <tr>
<th colspan="2" class="sortable le" ng-click="sortCost(cName)">Component</th> <th style="width:50%" ng-class="{active: costTab == 'retrofit'}" ng-click="costTab = 'retrofit'">Retrofit Costs</th>
<th class="sortable le" ng-click="sortCost('c.cost')">Credits</th> <th style="width:50%" ng-class="{active: costTab == 'costs'}" ng-click="costTab = 'costs'">Costs</th>
</tr> </tr>
</thead> </thead>
<tbody>
<tr ng-repeat="c in costList | orderBy:costPredicate:costDesc" ng-if="c.c.cost > 0" ng-class="{disabled:!c.incCost}">
<td class="toggleable" style="width:1em;" ng-click="toggleCost(c)">{{c.c.class}}{{c.c.rating}}</td>
<td class="le toggleable shorten" ng-bind="cName(c)" ng-click="toggleCost(c)"></td>
<td class="ri toggleable" ng-click="toggleCost(c)">{{fCrd(discounts.current.pct * c.c.cost)}} <u>CR</u></td>
</tr>
</tbody>
</table> </table>
<table class="total">
<div ng-if="costTab == 'costs'">
<table style="width:100%">
<thead>
<tr class="main">
<th colspan="2" class="sortable le" ng-click="sortCost(cName)">
Component
<u class="optional-hide" ng-if="discounts.ship < 1">[Ship {{fRPct(1 - discounts.ship)}} off]</u>
<u class="optional-hide" ng-if="discounts.components < 1">[Components {{fRPct(1 - discounts.components)}} off]</u>
</th>
<th class="sortable le" ng-click="sortCost('discountedCost')">Credits</th>
</tr>
</thead>
<tbody>
<tr class="highlight" ng-repeat="item in costList | orderBy:costPredicate:costDesc" ng-if="item.c.cost > 0" ng-class="{disabled:!item.incCost}">
<td class="toggleable" style="width:1em;" ng-click="toggleCost(item)">{{item.c.class}}{{item.c.rating}}</td>
<td class="le toggleable shorten" ng-click="toggleCost(item)">{{cName(item)}}</td>
<td class="ri toggleable" ng-click="toggleCost(item)">{{fCrd(item.discountedCost)}} <u>CR</u></td>
</tr>
</tbody>
</table>
<table class="total">
<tr class="ri"> <tr class="ri">
<td class="lbl">Total</td> <td class="lbl">Total</td>
<td>{{fCrd(ship.totalCost * discounts.current.pct)}} <u>CR</u></td> <td>{{fCrd(ship.totalCost)}} <u>CR</u></td>
</tr> </tr>
<tr class="ri"> <tr class="ri">
<td class="lbl">Insurance</td> <td class="lbl">Insurance</td>
<td>{{fCrd(ship.totalCost * discounts.current.pct * insurance.current.pct)}} <u>CR</u></td> <td>{{fCrd(ship.totalCost * insurance.current.pct)}} <u>CR</u></td>
</tr> </tr>
</table> </table>
</div>
<div ng-if="costTab == 'retrofit'">
<div class="scroll-x">
<table style="width:100%">
<thead>
<tr class="main">
<th colspan="2" class="sortable le" ng-click="sortRetrofit('sellName')">Sell</th>
<th colspan="2" class="sortable le" ng-click="sortRetrofit('buyName')">Buy</th>
<th class="sortable le" ng-click="sortRetrofit('netCost')">
Net Cost <u class="optional-hide" ng-if="discounts.components < 1">[{{fRPct(1 - discounts.components)}} off]</u>
</th>
</tr>
</thead>
<tbody>
<tr ng-if="!retrofitList || retrofitList.length == 0">
<td colspan="5">No Retrofitting changes</td>
</tr>
<tr class="highlight" ng-repeat="item in retrofitList | orderBy:retroPredicate:retroDesc">
<td style="width:1em;">{{item.sellClassRating}}</td>
<td class="le shorten">{{item.sellName}}</td>
<td style="width:1em;">{{item.buyClassRating}}</td>
<td class="le shorten">{{item.buyName}}</td>
<td class="ri" ng-class="item.netCost > 0 ? 'warning' : 'secondary-disabled'">{{ fCrd(item.netCost)}} <u>CR</u></td>
</tr>
</tbody>
</table>
</div>
<table class="total">
<tr class="ri">
<td class="lbl">Cost</td>
<td colspan="2" ng-class="retrofitTotal > 0 ? 'warning' : 'secondary-disabled'">{{fCrd(retrofitTotal)}} <u>CR</u></td>
</tr>
<tr class="ri">
<td class="lbl">Retrofitting from</td>
<td class="cen" style="border-right:none;width: 1em;"><u class="primary-disabled">&#9662;</u></td>
<td style="border-left:none;padding:0;">
<select style="width: 100%;padding: 0" ng-model="$parent.retrofitBuild" ng-change="setRetrofitBase()" ng-options="name as name for (name, build) in allBuilds[ship.id]">
<option value="">Stock / Standard</option>
</select>
</td>
</tr>
</table>
</div>
</div> </div>
<div class="group half"> <div class="group half">

View File

@@ -21,7 +21,7 @@
"dependencies": { "dependencies": {
"d3": "~3.5.5", "d3": "~3.5.5",
"ng-lodash": "~0.2.0", "ng-lodash": "~0.2.0",
"ui-router-extras": "~0.0.13", "ui-router-extras": "0.0.13",
"angular-ui-router": "^0.2.15", "angular-ui-router": "^0.2.15",
"d3-tip": "~0.6.7", "d3-tip": "~0.6.7",
"ng-sortable": "~1.2.1", "ng-sortable": "~1.2.1",

View File

@@ -1,27 +1,27 @@
{ {
"Fuel Transfer Limpet Ctrl": [ "Fuel Transfer Limpet Ctrl": [
{ "id": "Ff", "grp":"fx", "class":7, "rating":"E", "cost": 437400, "mass": 80.0, "power":-0.55, "range":1.02, "maximum": 8 }, { "id": "Ff", "grp":"fx", "class":7, "rating":"E", "cost": 437400, "mass": 80.0, "power":0.55, "range":1.02, "maximum": 8 },
{ "id": "Fg", "grp":"fx", "class":7, "rating":"D", "cost": 874800, "mass": 32.0, "power":-0.41, "range":1.36, "maximum": 8 }, { "id": "Fg", "grp":"fx", "class":7, "rating":"D", "cost": 874800, "mass": 32.0, "power":0.41, "range":1.36, "maximum": 8 },
{ "id": "Fh", "grp":"fx", "class":7, "rating":"C", "cost":1749600, "mass": 80.0, "power":-0.69, "range":1.70, "maximum": 8 }, { "id": "Fh", "grp":"fx", "class":7, "rating":"C", "cost":1749600, "mass": 80.0, "power":0.69, "range":1.70, "maximum": 8 },
{ "id": "Fi", "grp":"fx", "class":7, "rating":"B", "cost":3499200, "mass":128.0, "power":-0.97, "range":2.04, "maximum": 8 }, { "id": "Fi", "grp":"fx", "class":7, "rating":"B", "cost":3499200, "mass":128.0, "power":0.97, "range":2.04, "maximum": 8 },
{ "id": "Fj", "grp":"fx", "class":7, "rating":"A", "cost":6998400, "mass": 80.0, "power":-0.83, "range":2.38, "maximum": 8 }, { "id": "Fj", "grp":"fx", "class":7, "rating":"A", "cost":6998400, "mass": 80.0, "power":0.83, "range":2.38, "maximum": 8 },
{ "id": "Fa", "grp":"fx", "class":5, "rating":"E", "cost": 48600, "mass": 20.0, "power":-0.40, "range":0.78, "maximum": 4 }, { "id": "Fa", "grp":"fx", "class":5, "rating":"E", "cost": 48600, "mass": 20.0, "power":0.40, "range":0.78, "maximum": 4 },
{ "id": "Fb", "grp":"fx", "class":5, "rating":"D", "cost": 97200, "mass": 8.0, "power":-0.30, "range":1.04, "maximum": 4 }, { "id": "Fb", "grp":"fx", "class":5, "rating":"D", "cost": 97200, "mass": 8.0, "power":0.30, "range":1.04, "maximum": 4 },
{ "id": "Fc", "grp":"fx", "class":5, "rating":"C", "cost": 194400, "mass": 20.0, "power":-0.50, "range":1.30, "maximum": 4 }, { "id": "Fc", "grp":"fx", "class":5, "rating":"C", "cost": 194400, "mass": 20.0, "power":0.50, "range":1.30, "maximum": 4 },
{ "id": "Fd", "grp":"fx", "class":5, "rating":"B", "cost": 388800, "mass": 32.0, "power":-0.97, "range":1.56, "maximum": 4 }, { "id": "Fd", "grp":"fx", "class":5, "rating":"B", "cost": 388800, "mass": 32.0, "power":0.97, "range":1.56, "maximum": 4 },
{ "id": "Fe", "grp":"fx", "class":5, "rating":"A", "cost": 777600, "mass": 20.0, "power":-0.60, "range":1.82, "maximum": 4 }, { "id": "Fe", "grp":"fx", "class":5, "rating":"A", "cost": 777600, "mass": 20.0, "power":0.60, "range":1.82, "maximum": 4 },
{ "id": "F5", "grp":"fx", "class":3, "rating":"E", "cost": 5400, "mass": 5.0, "power":-0.27, "range":0.66, "maximum": 2 }, { "id": "F5", "grp":"fx", "class":3, "rating":"E", "cost": 5400, "mass": 5.0, "power":0.27, "range":0.66, "maximum": 2 },
{ "id": "F6", "grp":"fx", "class":3, "rating":"D", "cost": 10800, "mass": 2.0, "power":-0.20, "range":0.88, "maximum": 2 }, { "id": "F6", "grp":"fx", "class":3, "rating":"D", "cost": 10800, "mass": 2.0, "power":0.20, "range":0.88, "maximum": 2 },
{ "id": "F7", "grp":"fx", "class":3, "rating":"C", "cost": 21600, "mass": 5.0, "power":-0.34, "range":1.10, "maximum": 2 }, { "id": "F7", "grp":"fx", "class":3, "rating":"C", "cost": 21600, "mass": 5.0, "power":0.34, "range":1.10, "maximum": 2 },
{ "id": "F8", "grp":"fx", "class":3, "rating":"B", "cost": 43200, "mass": 8.0, "power":-0.48, "range":1.32, "maximum": 2 }, { "id": "F8", "grp":"fx", "class":3, "rating":"B", "cost": 43200, "mass": 8.0, "power":0.48, "range":1.32, "maximum": 2 },
{ "id": "F9", "grp":"fx", "class":3, "rating":"A", "cost": 86400, "mass": 5.0, "power":-0.41, "range":1.54, "maximum": 2 }, { "id": "F9", "grp":"fx", "class":3, "rating":"A", "cost": 86400, "mass": 5.0, "power":0.41, "range":1.54, "maximum": 2 },
{ "id": "F0", "grp":"fx", "class":1, "rating":"E", "cost": 600, "mass": 1.3, "power":-0.18, "range":0.60, "maximum": 1 }, { "id": "F0", "grp":"fx", "class":1, "rating":"E", "cost": 600, "mass": 1.3, "power":0.18, "range":0.60, "maximum": 1 },
{ "id": "F1", "grp":"fx", "class":1, "rating":"D", "cost": 1200, "mass": 0.5, "power":-0.14, "range":0.80, "maximum": 1 }, { "id": "F1", "grp":"fx", "class":1, "rating":"D", "cost": 1200, "mass": 0.5, "power":0.14, "range":0.80, "maximum": 1 },
{ "id": "F2", "grp":"fx", "class":1, "rating":"C", "cost": 2400, "mass": 1.3, "power":-0.23, "range":1.00, "maximum": 1 }, { "id": "F2", "grp":"fx", "class":1, "rating":"C", "cost": 2400, "mass": 1.3, "power":0.23, "range":1.00, "maximum": 1 },
{ "id": "F3", "grp":"fx", "class":1, "rating":"B", "cost": 4800, "mass": 2.0, "power":-0.32, "range":1.20, "maximum": 1 }, { "id": "F3", "grp":"fx", "class":1, "rating":"B", "cost": 4800, "mass": 2.0, "power":0.32, "range":1.20, "maximum": 1 },
{ "id": "F4", "grp":"fx", "class":1, "rating":"A", "cost": 9600, "mass": 1.3, "power":-0.28, "range":1.40, "maximum": 1 } { "id": "F4", "grp":"fx", "class":1, "rating":"A", "cost": 9600, "mass": 1.3, "power":0.28, "range":1.40, "maximum": 1 }
] ]
} }

View File

@@ -1,52 +1,12 @@
{ {
"Fuel Tank": [ "Fuel Tank": [
{ { "id": "f1", "grp": "ft", "class": 1, "rating": "C", "cost": 1000, "capacity": 2 },
"id": "f1", { "id": "f2", "grp": "ft", "class": 2, "rating": "C", "cost": 3750, "capacity": 4 },
"grp": "ft", { "id": "f3", "grp": "ft", "class": 3, "rating": "C", "cost": 7063, "capacity": 8 },
"class": 1, { "id": "f4", "grp": "ft", "class": 4, "rating": "C", "cost": 24734, "capacity": 16 },
"rating": "C", { "id": "f5", "grp": "ft", "class": 5, "rating": "C", "cost": 97754, "capacity": 32 },
"cost": 1000, { "id": "f6", "grp": "ft", "class": 6, "rating": "C", "cost": 341577, "capacity": 64 },
"capacity": 2 { "id": "f7", "grp": "ft", "class": 7, "rating": "C", "cost": 1780900, "capacity": 128 },
}, { "id": "f8", "grp": "ft", "class": 8, "rating": "C", "cost": 5428400, "capacity": 256 }
{
"id": "f2",
"grp": "ft",
"class": 2,
"rating": "C",
"cost": 3750,
"capacity": 4
},
{
"id": "f3",
"grp": "ft",
"class": 3,
"rating": "C",
"cost": 7063,
"capacity": 8
},
{
"id": "f4",
"grp": "ft",
"class": 4,
"rating": "C",
"cost": 24734,
"capacity": 16
},
{
"id": "f5",
"grp": "ft",
"class": 5,
"rating": "C",
"cost": 97754,
"capacity": 32
},
{
"id": "f6",
"grp": "ft",
"class": 6,
"rating": "C",
"cost": 341577,
"capacity": 64
}
] ]
} }

View File

@@ -1,27 +1,27 @@
{ {
"Prospector Limpet Ctrl": [ "Prospector Limpet Ctrl": [
{ "id": "Pf", "grp":"pc", "class":7, "rating":"E", "cost": 437400, "mass": 80.0, "power":-0.55, "range": 5.10, "maximum": 8 }, { "id": "Pf", "grp":"pc", "class":7, "rating":"E", "cost": 437400, "mass": 80.0, "power":0.55, "range": 5.10, "maximum": 8 },
{ "id": "Pg", "grp":"pc", "class":7, "rating":"D", "cost": 874800, "mass": 32.0, "power":-0.41, "range": 6.80, "maximum": 8 }, { "id": "Pg", "grp":"pc", "class":7, "rating":"D", "cost": 874800, "mass": 32.0, "power":0.41, "range": 6.80, "maximum": 8 },
{ "id": "Ph", "grp":"pc", "class":7, "rating":"C", "cost":1749600, "mass": 80.0, "power":-0.69, "range": 8.50, "maximum": 8 }, { "id": "Ph", "grp":"pc", "class":7, "rating":"C", "cost":1749600, "mass": 80.0, "power":0.69, "range": 8.50, "maximum": 8 },
{ "id": "Pi", "grp":"pc", "class":7, "rating":"B", "cost":3499200, "mass":128.0, "power":-0.97, "range":10.20, "maximum": 8 }, { "id": "Pi", "grp":"pc", "class":7, "rating":"B", "cost":3499200, "mass":128.0, "power":0.97, "range":10.20, "maximum": 8 },
{ "id": "Pj", "grp":"pc", "class":7, "rating":"A", "cost":6998400, "mass": 80.0, "power":-0.83, "range":11.90, "maximum": 8 }, { "id": "Pj", "grp":"pc", "class":7, "rating":"A", "cost":6998400, "mass": 80.0, "power":0.83, "range":11.90, "maximum": 8 },
{ "id": "Pa", "grp":"pc", "class":5, "rating":"E", "cost": 48600, "mass": 20.0, "power":-0.40, "range": 3.90, "maximum": 4 }, { "id": "Pa", "grp":"pc", "class":5, "rating":"E", "cost": 48600, "mass": 20.0, "power":0.40, "range": 3.90, "maximum": 4 },
{ "id": "Pb", "grp":"pc", "class":5, "rating":"D", "cost": 97200, "mass": 8.0, "power":-0.30, "range": 5.20, "maximum": 4 }, { "id": "Pb", "grp":"pc", "class":5, "rating":"D", "cost": 97200, "mass": 8.0, "power":0.30, "range": 5.20, "maximum": 4 },
{ "id": "Pc", "grp":"pc", "class":5, "rating":"C", "cost": 194400, "mass": 20.0, "power":-0.50, "range": 6.50, "maximum": 4 }, { "id": "Pc", "grp":"pc", "class":5, "rating":"C", "cost": 194400, "mass": 20.0, "power":0.50, "range": 6.50, "maximum": 4 },
{ "id": "Pd", "grp":"pc", "class":5, "rating":"B", "cost": 388800, "mass": 32.0, "power":-0.97, "range": 7.80, "maximum": 4 }, { "id": "Pd", "grp":"pc", "class":5, "rating":"B", "cost": 388800, "mass": 32.0, "power":0.97, "range": 7.80, "maximum": 4 },
{ "id": "Pe", "grp":"pc", "class":5, "rating":"A", "cost": 777600, "mass": 20.0, "power":-0.60, "range": 9.10, "maximum": 4 }, { "id": "Pe", "grp":"pc", "class":5, "rating":"A", "cost": 777600, "mass": 20.0, "power":0.60, "range": 9.10, "maximum": 4 },
{ "id": "P5", "grp":"pc", "class":3, "rating":"E", "cost": 5400, "mass": 5.0, "power":-0.27, "range": 3.30, "maximum": 2 }, { "id": "P5", "grp":"pc", "class":3, "rating":"E", "cost": 5400, "mass": 5.0, "power":0.27, "range": 3.30, "maximum": 2 },
{ "id": "P6", "grp":"pc", "class":3, "rating":"D", "cost": 10800, "mass": 2.0, "power":-0.20, "range": 4.40, "maximum": 2 }, { "id": "P6", "grp":"pc", "class":3, "rating":"D", "cost": 10800, "mass": 2.0, "power":0.20, "range": 4.40, "maximum": 2 },
{ "id": "P7", "grp":"pc", "class":3, "rating":"C", "cost": 21600, "mass": 5.0, "power":-0.34, "range": 5.50, "maximum": 2 }, { "id": "P7", "grp":"pc", "class":3, "rating":"C", "cost": 21600, "mass": 5.0, "power":0.34, "range": 5.50, "maximum": 2 },
{ "id": "P8", "grp":"pc", "class":3, "rating":"B", "cost": 43200, "mass": 8.0, "power":-0.48, "range": 6.60, "maximum": 2 }, { "id": "P8", "grp":"pc", "class":3, "rating":"B", "cost": 43200, "mass": 8.0, "power":0.48, "range": 6.60, "maximum": 2 },
{ "id": "P9", "grp":"pc", "class":3, "rating":"A", "cost": 86400, "mass": 5.0, "power":-0.41, "range": 7.70, "maximum": 2 }, { "id": "P9", "grp":"pc", "class":3, "rating":"A", "cost": 86400, "mass": 5.0, "power":0.41, "range": 7.70, "maximum": 2 },
{ "id": "P0", "grp":"pc", "class":1, "rating":"E", "cost": 600, "mass": 1.3, "power":-0.18, "range": 3.00, "maximum": 1 }, { "id": "P0", "grp":"pc", "class":1, "rating":"E", "cost": 600, "mass": 1.3, "power":0.18, "range": 3.00, "maximum": 1 },
{ "id": "P1", "grp":"pc", "class":1, "rating":"D", "cost": 1200, "mass": 0.5, "power":-0.14, "range": 4.00, "maximum": 1 }, { "id": "P1", "grp":"pc", "class":1, "rating":"D", "cost": 1200, "mass": 0.5, "power":0.14, "range": 4.00, "maximum": 1 },
{ "id": "P2", "grp":"pc", "class":1, "rating":"C", "cost": 2400, "mass": 1.3, "power":-0.23, "range": 5.00, "maximum": 1 }, { "id": "P2", "grp":"pc", "class":1, "rating":"C", "cost": 2400, "mass": 1.3, "power":0.23, "range": 5.00, "maximum": 1 },
{ "id": "P3", "grp":"pc", "class":1, "rating":"B", "cost": 4800, "mass": 2.0, "power":-0.32, "range": 6.00, "maximum": 1 }, { "id": "P3", "grp":"pc", "class":1, "rating":"B", "cost": 4800, "mass": 2.0, "power":0.32, "range": 6.00, "maximum": 1 },
{ "id": "P4", "grp":"pc", "class":1, "rating":"A", "cost": 9600, "mass": 1.3, "power":-0.28, "range": 7.00, "maximum": 1 } { "id": "P4", "grp":"pc", "class":1, "rating":"A", "cost": 9600, "mass": 1.3, "power":0.28, "range": 7.00, "maximum": 1 }
] ]
} }

View File

@@ -8,7 +8,7 @@
"cost": 18969990, "cost": 18969990,
"speed": 180, "speed": 180,
"boost": 300, "boost": 300,
"agility": 0, "agility": 2,
"shields": 200, "shields": 200,
"armour": 540, "armour": 540,
"fuelcost": 50, "fuelcost": 50,

View File

@@ -140,7 +140,7 @@ gulp.task('generateIndexHTML', function(done) {
gulp.src('app/index.html') gulp.src('app/index.html')
.pipe(template({ .pipe(template({
version: pkg.version, version: pkg.version,
date : (new Date()).toLocaleDateString(), date : new Date().toISOString().slice(0, 10),
uaTracking: process.env.CORIOLIS_UA_TRACKING || false, uaTracking: process.env.CORIOLIS_UA_TRACKING || false,
svgContent: svgIconsContent, svgContent: svgIconsContent,
gapiKey: process.env.CORIOLIS_GAPI_KEY gapiKey: process.env.CORIOLIS_GAPI_KEY

View File

@@ -1,6 +1,6 @@
{ {
"name": "coriolis_shipyard", "name": "coriolis_shipyard",
"version": "0.13.4", "version": "1.0.2",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/cmmcleod/coriolis" "url": "https://github.com/cmmcleod/coriolis"

View File

@@ -0,0 +1,50 @@
describe("Outfit Controller", function() {
beforeEach(module('app'));
var outfitController, scope;
var eventStub = {
preventDefault: function(){ },
stopPropagation: function(){ }
};
beforeEach(inject(function(_$rootScope_, $controller) {
$rootScope = _$rootScope_;
$rootScope.discounts = { ship: 1, components: 1};
$stateParams = { shipId: 'anaconda'};
scope = $rootScope.$new();
outfitController = $controller('OutfitController', { $rootScope: $rootScope, $scope: scope, $stateParams: $stateParams });
}));
describe("Retrofitting Costs", function() {
it("are empty by default", function() {
expect(scope.retrofitTotal).toEqual(0);
expect(scope.retrofitList.length).toEqual(0);
});
it("updates on bulkheads change", function() {
scope.select('b', scope.ship.bulkheads, eventStub, "1"); // Use Reinforced Alloy Bulkheads
expect(scope.retrofitTotal).toEqual(58787780);
expect(scope.retrofitList.length).toEqual(1);
scope.select('b', scope.ship.bulkheads, eventStub, "0"); // Use Reinforced Alloy Bulkheads
expect(scope.retrofitTotal).toEqual(0);
expect(scope.retrofitList.length).toEqual(0);
});
it("updates on component change", function() {
scope.select('h', scope.ship.hardpoints[0], eventStub, "0u"); // 3C/F Beam Laser
expect(scope.retrofitTotal).toEqual(1177600);
expect(scope.retrofitList.length).toEqual(1);
scope.select('h', scope.ship.hardpoints[6], eventStub, "empty"); // Remove default pulse laser
scope.select('h', scope.ship.hardpoints[7], eventStub, "empty"); // Remove default pulse laser
expect(scope.retrofitTotal).toEqual(1173200);
expect(scope.retrofitList.length).toEqual(3);
scope.select('i', scope.ship.internal[3], eventStub, "11"); // Use 6A Auto field maintenance unit
expect(scope.retrofitTotal).toEqual(16478701);
expect(scope.retrofitList.length).toEqual(4);
});
});
});

View File

@@ -68,6 +68,37 @@ describe("Ship Factory", function() {
} }
}); });
it("discounts hull and components properly", function() {
var id = 'cobra_mk_iii';
var cobra = DB.ships[id];
var testShip = new Ship(id, cobra.properties, cobra.slots);
testShip.buildWith(cobra.defaults);
var originalHullCost = testShip.cost;
var originalTotalCost = testShip.totalCost;
var discount = 0.9;
expect(testShip.c.discountedCost).toEqual(originalHullCost, 'Hull cost does not match');
testShip.applyDiscounts(discount, discount);
// Floating point errors cause miniscule decimal places which are handled in the app by rounding/formatting
expect(Math.floor(testShip.c.discountedCost)).toEqual(Math.floor(originalHullCost * discount), 'Discounted Hull cost does not match');
expect(Math.floor(testShip.totalCost)).toEqual(Math.floor(originalTotalCost * discount), 'Discounted Total cost does not match');
testShip.applyDiscounts(1, 1); // No discount, 100% of cost
expect(testShip.c.discountedCost).toEqual(originalHullCost, 'Hull cost does not match');
expect(testShip.totalCost).toEqual(originalTotalCost, 'Total cost does not match');
testShip.applyDiscounts(discount, 1); // Only discount hull
expect(Math.floor(testShip.c.discountedCost)).toEqual(Math.round(originalHullCost * discount), 'Discounted Hull cost does not match');
expect(testShip.totalCost).toEqual(originalTotalCost - originalHullCost + testShip.c.discountedCost, 'Total cost does not match');
});
it("enforces a single shield generator", function() { it("enforces a single shield generator", function() {
var id = 'anaconda'; var id = 'anaconda';
var anacondaData = DB.ships[id]; var anacondaData = DB.ships[id];