mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 22:55:35 +00:00
UI changes, added utilitiy services, persistance, serialization
This commit is contained in:
@@ -1,25 +1,45 @@
|
||||
angular.module('app', ['ngRoute', 'shipyard', 'ngLodash', 'n3-line-chart', 'app.templates'])
|
||||
.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
|
||||
angular.module('app', ['ui.router', 'shipyard', 'ngLodash', 'app.templates'])
|
||||
.config(['$stateProvider', '$urlRouterProvider', '$locationProvider', function($stateProvider, $urlRouterProvider, $locationProvider) {
|
||||
$locationProvider.html5Mode(true);
|
||||
$routeProvider
|
||||
.when('/:ship', { templateUrl: 'views/ship.html', controller: 'ShipController' })
|
||||
.when('/:ship/:code', { templateUrl: 'views/ship.html', controller: 'ShipController' })
|
||||
.when('/', { templateUrl: 'views/ships.html', controller: 'ShipyardController' });
|
||||
$stateProvider
|
||||
.state('outfit', {
|
||||
url: '/outfit/:shipId/:code?bn',
|
||||
params: {
|
||||
// TODO: fix below, default, squash false not working
|
||||
//shipId: { value: 'sidewinder', squash: false }, // Allow 'shipId' parameter to default to
|
||||
code: { value: null, squash: true } // Allow 'code' parameter to be empty/optional
|
||||
},
|
||||
templateUrl: 'views/page-outfit.html',
|
||||
controller: 'OutfitController',
|
||||
resolve: {
|
||||
shipId: ['$stateParams',function ($p) { // Ensure ship exists before loading controller
|
||||
if (!DB.ships[$p.shipId]) {
|
||||
throw { type: 404, message: 'Ship "' + $p.shipId + '" does not exist'};
|
||||
}
|
||||
}]
|
||||
}
|
||||
})
|
||||
.state('shipyard', { url: '/', templateUrl: 'views/page-shipyard.html', controller: 'ShipyardController' })
|
||||
.state('error', { params: {type:null, message:null, details: null }, templateUrl: 'views/page-error.html', controller: 'ErrorController' })
|
||||
.state('notfound', { url: '*path', templateUrl: 'views/page-error.html', controller: 'ErrorController' });
|
||||
|
||||
}])
|
||||
.run(['$rootScope','$document','$location','$route','commonArray','shipPurpose','shipSize','hardPointClass','internalGroupMap', function ($rootScope, $doc, $loc, $route, CArr, shipPurpose, sz, hpc, igMap) {
|
||||
// Allow URL changes without reloading controllers/view
|
||||
var original = $loc.path;
|
||||
$loc.path = function (path, reload) {
|
||||
if (reload === false) {
|
||||
var lastRoute = $route.current;
|
||||
var un = $rootScope.$on('$locationChangeSuccess', function () {
|
||||
$route.current = lastRoute;
|
||||
un();
|
||||
});
|
||||
}
|
||||
return original.apply($loc, [path]);
|
||||
};
|
||||
.config(['$provide',function($provide) {
|
||||
// Global Error Handler, redirects uncaught errors to the error page
|
||||
$provide.decorator('$exceptionHandler', ['$delegate', '$injector', function ($delegate, $injector) {
|
||||
return function(exception, cause) {
|
||||
$injector.get('$state').go('error', { details: exception }, {location:false, reload:true}); // Go to error state, reload the controller, keep the current URL
|
||||
$delegate(exception, cause);
|
||||
};
|
||||
}]);
|
||||
}])
|
||||
.run(['$rootScope','$document','$state','commonArray','shipPurpose','shipSize','hardPointClass','internalGroupMap','hardpointsGroupMap', function ($rootScope, $doc, $state, CArr, shipPurpose, sz, hpc, igMap, hgMap) {
|
||||
|
||||
// Redirect any state transition errors to the error controller/state
|
||||
$rootScope.$on('$stateChangeError', function(e, toState, toParams, fromState, fromParams, error){
|
||||
e.preventDefault();
|
||||
$state.go('error',error, {location:false, reload:true}); // Go to error state, reload the controller, keep the current URL
|
||||
});
|
||||
|
||||
// Global Reference variables
|
||||
$rootScope.CArr = CArr;
|
||||
@@ -27,7 +47,9 @@ angular.module('app', ['ngRoute', 'shipyard', 'ngLodash', 'n3-line-chart', 'app.
|
||||
$rootScope.SZ = sz;
|
||||
$rootScope.HPC = hpc;
|
||||
$rootScope.igMap = igMap;
|
||||
window.hgmap = $rootScope.hgMap = hgMap;
|
||||
$rootScope.ships = DB.ships;
|
||||
$rootScope.title = 'Coriolis';
|
||||
|
||||
// Formatters
|
||||
$rootScope.fCrd = d3.format(',.0f');
|
||||
|
||||
15
app/js/controllers/controller-error.js
Normal file
15
app/js/controllers/controller-error.js
Normal file
@@ -0,0 +1,15 @@
|
||||
angular.module('app')
|
||||
.controller('ErrorController', ['$rootScope','$scope','$stateParams', '$location', function ($rootScope, $scope, $p, $location) {
|
||||
$rootScope.title = 'Error';
|
||||
|
||||
if ($p.path) { // If path is specified, 404
|
||||
$scope.type = 404; // Deep Space Image...
|
||||
$scope.message = ""
|
||||
$scope.path = $p.path;
|
||||
} else {
|
||||
$scope.type = $p.type || 'unknown';
|
||||
$scope.message = $p.message || "Uh, this is bad..";
|
||||
$scope.path = $location.path();
|
||||
}
|
||||
|
||||
}]);
|
||||
101
app/js/controllers/controller-outfit.js
Normal file
101
app/js/controllers/controller-outfit.js
Normal file
@@ -0,0 +1,101 @@
|
||||
angular.module('app').controller('OutfitController', ['$rootScope','$scope', '$state', '$stateParams', 'Ship', 'Components', 'Serializer', 'Persist', function ($rootScope, $scope, $state, $p, Ship, Components, Serializer, Persist) {
|
||||
var data = DB.ships[$p.shipId];
|
||||
var ship = new Ship($p.shipId, data.properties, data.slots); // Create a new Ship instance
|
||||
|
||||
if ($p.code) {
|
||||
Serializer.toShip(ship, $p.code); // Populate components from 'code' URL param
|
||||
$scope.code = $p.code;
|
||||
} else {
|
||||
ship.buildWith(data.defaults); // Populate with default components
|
||||
}
|
||||
|
||||
$scope.buildName = $p.bn;
|
||||
$rootScope.title = ship.name + $scope.buildName? ' - ' + $scope.buildName: '';
|
||||
$scope.ship = ship;
|
||||
$scope.pp = ship.common[0]; // Power Plant
|
||||
$scope.th = ship.common[1]; // Thruster
|
||||
$scope.fsd = ship.common[2]; // Frame Shrift Drive
|
||||
$scope.ls = ship.common[3]; // Life Support
|
||||
$scope.pd = ship.common[4]; // Power Distributor
|
||||
$scope.ss = ship.common[5]; // Sensors
|
||||
$scope.ft = ship.common[6]; // Fuel Tank
|
||||
$scope.hps = ship.hardpoints;
|
||||
$scope.internal = ship.internal;
|
||||
$scope.availCS = Components.forShip(ship.id);
|
||||
$scope.selectedSlot = null;
|
||||
$scope.lastSaveCode = Persist.getBuild(ship.id, $scope.buildName);
|
||||
|
||||
// for debugging
|
||||
window.myScope = $scope;
|
||||
|
||||
$scope.selectSlot = function(e, slot) {
|
||||
e.stopPropagation();
|
||||
if ($scope.selectedSlot == slot) {
|
||||
$scope.selectedSlot = null;
|
||||
} else {
|
||||
$scope.selectedSlot = slot;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.select = function(type, slot, e) {
|
||||
e.stopPropagation();
|
||||
if (e.srcElement.id) {
|
||||
if(type == 'h') {
|
||||
ship.use(slot, e.srcElement.id, Components.hardpoints(e.srcElement.id));
|
||||
} else if (type == 'c') {
|
||||
ship.use(slot, e.srcElement.id, Components.common(ship.common.indexOf(slot), e.srcElement.id));
|
||||
} else if (type == 'i') {
|
||||
ship.use(slot, e.srcElement.id, Components.internal(e.srcElement.id));
|
||||
} else if (type == 'b') {
|
||||
ship.useBulkhead(slot, e.srcElement.id);
|
||||
} else {
|
||||
ship.use(slot, null, null);
|
||||
}
|
||||
$scope.selectedSlot = null;
|
||||
$scope.code = Serializer.fromShip(ship);
|
||||
$state.go('outfit', {shipId: ship.id, code: $scope.code, bn: $scope.buildName}, {location:'replace', notify:false});
|
||||
$scope.canSave = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the build from the last save.
|
||||
*/
|
||||
$scope.reloadBuild = function() {
|
||||
if ($scope.buildName && $scope.lastSaveCode) {
|
||||
Serializer.toShip(ship, $scope.lastSaveCode); // Repopulate with components from last save
|
||||
$scope.code = $scope.lastSaveCode;
|
||||
$state.go('outfit', {shipId: ship.id, code: $scope.lastSaveCode, bn: $scope.buildName}, {location:'replace', notify:false});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.saveBuild = function() {
|
||||
if ($scope.code && $scope.code != $scope.lastSaveCode) {
|
||||
Persist.saveBuild(ship.id, $scope.buildName, $scope.code);
|
||||
$scope.lastSaveCode = $scope.code;
|
||||
$rootScope.$broadcast('buildSaved', ship.id, $scope.buildName, $scope.code);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.deleteBuild = function() {
|
||||
Persist.deleteBuild(ship.id, $scope.buildName);
|
||||
$rootScope.$broadcast('buildDeleted', $scope.saveName, ship.id);
|
||||
$state.go('outfit', {shipId: ship.id, code: null, bn: null}, {location:'replace', reload:true});
|
||||
}
|
||||
|
||||
$rootScope.$on('keyup', function (e, keyEvent) {
|
||||
if(keyEvent.keyCode == 27) { // on Escape
|
||||
$scope.selectedSlot = null;
|
||||
$scope.$apply();
|
||||
}
|
||||
else if(keyEvent.keycode == 83 && keyEvent.ctrlKey){ // CTRL + S
|
||||
e.preventDefault();
|
||||
$scope.saveBuild();
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on('bgClicked', function (e, keyEvent) {
|
||||
$scope.selectedSlot = null;
|
||||
});
|
||||
|
||||
}]);
|
||||
@@ -1,61 +0,0 @@
|
||||
angular.module('app')
|
||||
.controller('ShipController', ['$rootScope','$scope', '$routeParams', '$location', 'ShipFactory', 'components', function ($rootScope, $scope, $p, $loc, ShipFactory, Components) {
|
||||
$scope.shipId = $p.ship;
|
||||
|
||||
// TODO: show 404 if ship not found.
|
||||
var ship = ShipFactory($scope.shipId, DB.ships[$scope.shipId], $p.code);
|
||||
$scope.ship = ship;
|
||||
$scope.pp = ship.common[0]; // Power Plant
|
||||
$scope.th = ship.common[1]; // Thruster
|
||||
$scope.fsd = ship.common[2]; // Frame Shrift Drive
|
||||
$scope.ls = ship.common[3]; // Life Support
|
||||
$scope.pd = ship.common[4]; // Power Distributor
|
||||
$scope.ss = ship.common[5]; // Sensors
|
||||
$scope.ft = ship.common[6]; // Fuel Tank
|
||||
$scope.hps = ship.hardpoints;
|
||||
$scope.internal = ship.internal;
|
||||
$scope.availCS = Components.forShip($scope.shipId);
|
||||
$scope.selectedSlot = null;
|
||||
// for debugging
|
||||
window.ship = ship;
|
||||
window.availcs = $scope.availCS;
|
||||
|
||||
$scope.selectSlot = function(e, slot) {
|
||||
e.stopPropagation();
|
||||
if ($scope.selectedSlot == slot) {
|
||||
$scope.selectedSlot = null;
|
||||
} else {
|
||||
$scope.selectedSlot = slot;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.selectComponent = function(slot, id, component) {
|
||||
ship.use(slot, id, component);
|
||||
$scope.selectedSlot = null;
|
||||
$loc.path(ship.id + '/' + ship.code, false).replace();
|
||||
}
|
||||
|
||||
$scope.hideMenus = function() {
|
||||
$scope.selectedSlot = null;
|
||||
}
|
||||
|
||||
$rootScope.$on('keyup', function (e, keyEvent) {
|
||||
if(keyEvent.keyCode == 27) { // on Escape
|
||||
$scope.hideMenus();
|
||||
$scope.$apply();
|
||||
}
|
||||
// TODO: CTRL+S -> Save
|
||||
});
|
||||
|
||||
$rootScope.$on('bgClicked', function (e, keyEvent) {
|
||||
$scope.hideMenus();
|
||||
});
|
||||
|
||||
// TODO: Save build
|
||||
// TODO: name build + save
|
||||
// Push new url history in this case
|
||||
// TODO: delete build
|
||||
// TODO: reset to ship defaults
|
||||
// TODO: revert to last save
|
||||
|
||||
}]);
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module('app')
|
||||
.controller('ShipyardController', function () {
|
||||
|
||||
});
|
||||
.controller('ShipyardController', ['$rootScope', function ($rootScope) {
|
||||
$rootScope.title = 'Coriolis - Shipyard';
|
||||
}]);
|
||||
@@ -2,15 +2,39 @@ angular.module('app').directive('componentSelect', [ function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope:{
|
||||
opts: '=', // Component Options object
|
||||
slot: '=', // Slot Object
|
||||
selectComponent: '&sc' // Select Component function
|
||||
opts: '=', // Component Options object
|
||||
},
|
||||
templateUrl: 'views/component_select.html',
|
||||
link: function (scope) {
|
||||
scope.use = function(id, component) {
|
||||
scope.selectComponent({s: scope.slot, id: id, c: component});
|
||||
};
|
||||
link: function(scope, element) {
|
||||
var list = [], o, id;
|
||||
var opts = scope.opts;
|
||||
//TODO: take current ship mass into account if provided
|
||||
// Generting the HTML in this manner is MUCH faster than using an angular template.
|
||||
for (id in opts) {
|
||||
o = opts[id];
|
||||
list.push('<li class="');
|
||||
list.push(o.name? 'lc' : 'c');
|
||||
if (false) { // Omit id if mass is exceeded making it 'disabled'
|
||||
list.push(' disabled"');
|
||||
} else {
|
||||
list.push('" id="');
|
||||
}
|
||||
list.push(id);
|
||||
list.push('">');
|
||||
list.push(o.class);
|
||||
list.push(o.rating);
|
||||
if(o.mode) {
|
||||
list.push('/' + o.mode);
|
||||
if(o.missile) {
|
||||
list.push(o.missile);
|
||||
}
|
||||
}
|
||||
if(o.name) {
|
||||
list.push(' ' + o.name);
|
||||
}
|
||||
list.push('</li>');
|
||||
}
|
||||
|
||||
element.html('<ul>' + list.join('') + '</ul>');
|
||||
}
|
||||
};
|
||||
}]);
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module('app').directive('shipRange', ['$rootScope','CalcJumpRange', function ($r, calcJumpRange) {
|
||||
angular.module('app').directive('shipRange', ['$rootScope','calcJumpRange', function ($r, calcJumpRange) {
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
angular.module('app').directive('hardpoint', ['$rootScope', function ($r) {
|
||||
angular.module('app').directive('slotHardpoint', ['$rootScope', function ($r) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope:{
|
||||
hp: '=',
|
||||
size: '=',
|
||||
opts: '='
|
||||
lbl: '=',
|
||||
},
|
||||
templateUrl: 'views/hardpoint.html',
|
||||
templateUrl: 'views/slot-hardpoint.html',
|
||||
link: function (scope) {
|
||||
scope.$r = $r;
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
angular.module('app').directive('slotDetails', ['$rootScope', function ($r) {
|
||||
angular.module('app').directive('slotInternal', ['$rootScope', function ($r) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope:{
|
||||
c: '=',
|
||||
c: '=slot',
|
||||
lbl: '=',
|
||||
opts: '='
|
||||
},
|
||||
templateUrl: 'views/slot.html',
|
||||
templateUrl: 'views/slot-internal.html',
|
||||
link: function(scope) {
|
||||
scope.$r = $r;
|
||||
}
|
||||
58
app/js/service-persist.js
Normal file
58
app/js/service-persist.js
Normal file
@@ -0,0 +1,58 @@
|
||||
angular.module('app').service('Persist', ['lodash', function (_) {
|
||||
var LS_KEY = 'builds';
|
||||
|
||||
var buildJson = localStorage.getItem(LS_KEY);
|
||||
|
||||
if (buildJson) {
|
||||
this.builds = angular.fromJson(localStorage.getItem(LS_KEY));
|
||||
} else {
|
||||
this.builds = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist a ship build in local storage.
|
||||
*
|
||||
* @param {string} shipId The unique id for a model of ship
|
||||
* @param {string} name The name of the build
|
||||
* @param {string} code The serialized code
|
||||
*/
|
||||
this.saveBuild = function (shipId, name, code) {
|
||||
if (!this.builds[shipId]) {
|
||||
this.builds[shipId] = {};
|
||||
}
|
||||
|
||||
this.builds[shipId][name] = code;
|
||||
localStorage.setItem(LS_KEY, angular.toJson(this.builds)); // Persist updated build collection to localstorage
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the serialized code/string for a build. Returns null if a
|
||||
* build is not found.
|
||||
*
|
||||
* @param {string} shipId The unique id for a model of ship
|
||||
* @param {string} name The name of the build
|
||||
* @return {string} The serialized build string.
|
||||
*/
|
||||
this.getBuild = function (shipId, name) {
|
||||
if (this.builds[shipId]) {
|
||||
return this.builds[shipId][name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a build from local storage. It will also delete the ship build collection if
|
||||
* it becomes empty
|
||||
*
|
||||
* @param {string} shipId The unique id for a model of ship
|
||||
* @param {string} name The name of the build
|
||||
*/
|
||||
this.deleteBuild = function (shipId, name) {
|
||||
delete build[shipId][name];
|
||||
if (Object.keys(build[shipId]).length == 0) {
|
||||
delete build[shipId];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}]);
|
||||
69
app/js/service-serializer.js
Normal file
69
app/js/service-serializer.js
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Service managing seralization and deserialization of models for use in URLs and persistene.
|
||||
*/
|
||||
angular.module('app').service('Serializer', ['lodash', function (_) {
|
||||
|
||||
/**
|
||||
* Serializes the ships selected components for all slots to a URL friendly string.
|
||||
* @param {Ship} ship The ship to be serialized.
|
||||
* @return {string} Encoded string of components
|
||||
*/
|
||||
this.fromShip = function(ship) {
|
||||
var data = [
|
||||
ship.bulkheads.id,
|
||||
_.map(ship.common, idToStr),
|
||||
_.map(ship.hardpoints, idToStr),
|
||||
_.map(ship.internal, idToStr),
|
||||
];
|
||||
|
||||
return _.flatten(data).join('');
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates an existing ship instance's slots with components determined by the
|
||||
* code.
|
||||
*
|
||||
* @param {Ship} ship The ship instance to be updated
|
||||
* @param {string} code The string to deserialize
|
||||
*/
|
||||
this.toShip = function (ship, code) {
|
||||
var commonCount = ship.common.length;
|
||||
var hpCount = commonCount + ship.hardpoints.length;
|
||||
var comps = {
|
||||
bulkheads: code.charAt(0) * 1,
|
||||
common: new Array(ship.common.length),
|
||||
hardpoints: new Array(ship.hardpoints.length),
|
||||
internal: new Array(ship.internal.length)
|
||||
};
|
||||
|
||||
// TODO: improve...
|
||||
for (var i = 1, c = 0, l = code.length; i < l; i++) {
|
||||
var empty = code.charAt(i) == '-';
|
||||
if (c < commonCount) {
|
||||
comps.common[c] = empty? 0 : code.substring(i, i + 2);
|
||||
} else if (c < hpCount) {
|
||||
comps.hardpoints[c - commonCount] = empty? 0 : code.substring(i, i + 2);
|
||||
} else {
|
||||
comps.internal[c - hpCount] = empty? 0 : code.substring(i, i + 2);
|
||||
}
|
||||
if (!empty) {
|
||||
i++;
|
||||
}
|
||||
c++;
|
||||
}
|
||||
ship.buildWith(comps);
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility function to retrieve a safe string for selected component for a slot.
|
||||
* Used for serialization to code only.
|
||||
*
|
||||
* @private
|
||||
* @param {object} slot The slot object.
|
||||
* @return {string} The id of the selected component or '-' if none selected
|
||||
*/
|
||||
function idToStr(slot) {
|
||||
return (slot.id === null)? '-' : slot.id;
|
||||
}
|
||||
|
||||
}]);
|
||||
@@ -1,40 +1,45 @@
|
||||
angular.module('shipyard').factory('components', ['lodash', function (_) {
|
||||
var C = DB.components;
|
||||
angular.module('shipyard').factory('ComponentSet', ['lodash', function (_) {
|
||||
|
||||
function ComponentSet(shipId) {
|
||||
var ship = DB.ships[shipId];
|
||||
var maxInternal = ship.slotCap.internal[0];
|
||||
|
||||
this.mass = ship.mass;
|
||||
function ComponentSet(components, mass, maxCommonArr, maxInternal, maxHardPoint) {
|
||||
this.mass = mass;
|
||||
this.common = {};
|
||||
this.internal = {};
|
||||
this.hardpoints = filter(C.hardpoints, ship.slotCap.hardpoints[0], 0, ship.mass);
|
||||
this.bulkheads = C.bulkheads[shipId];
|
||||
this.hardpoints = {};
|
||||
this.hpClass = {};
|
||||
this.intClass = {};
|
||||
|
||||
for (var i = 0; i < C.common.length; i ++) {
|
||||
var max = ship.slotCap.common[i];
|
||||
for (var i = 0; i < components.common.length; i ++) {
|
||||
var max = maxCommonArr[i];
|
||||
switch (i) {
|
||||
// Slots where component class must be equal to slot class
|
||||
case 3: // Life Support
|
||||
case 5: // Sensors
|
||||
this.common[i] = filter(C.common[i], max, max, ship.mass);
|
||||
this.common[i] = filter(components.common[i], max, max, this.mass);
|
||||
break;
|
||||
// Other slots can have a component of class lower than the slot class
|
||||
default:
|
||||
this.common[i] = filter(C.common[i], max, 0, ship.mass);
|
||||
this.common[i] = filter(components.common[i], max, 0, this.mass);
|
||||
}
|
||||
}
|
||||
|
||||
for(var g in C.internal) {
|
||||
this.internal[g] = filter(C.internal[g], maxInternal, 0, ship.mass);
|
||||
for(var h in components.hardpoints) {
|
||||
this.hardpoints[h] = filter(components.hardpoints[h], maxHardPoint, 0, this.mass);
|
||||
}
|
||||
|
||||
for(var g in components.internal) {
|
||||
this.internal[g] = filter(components.internal[g], maxInternal, 0, this.mass);
|
||||
}
|
||||
}
|
||||
|
||||
ComponentSet.prototype.getHps = function(c) {
|
||||
if(!this.hpClass[c]) {
|
||||
this.hpClass[c] = filter(this.hardpoints, c, c? 1 : 0, this.mass);
|
||||
var o = this.hpClass[c] = {};
|
||||
for(var key in this.hardpoints) {
|
||||
var data = filter(this.hardpoints[key], c, c? 1 : 0, this.mass);
|
||||
if(Object.keys(data).length) { // If group is not empty
|
||||
o[key] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.hpClass[c];
|
||||
};
|
||||
@@ -44,7 +49,7 @@ angular.module('shipyard').factory('components', ['lodash', function (_) {
|
||||
var o = this.intClass[c] = {};
|
||||
for(var key in this.internal) {
|
||||
var data = filter(this.internal[key], c, 0, this.mass);
|
||||
if(Object.keys(data).length) {
|
||||
if(Object.keys(data).length) { // If group is not empty
|
||||
o[key] = data;
|
||||
}
|
||||
}
|
||||
@@ -62,16 +67,6 @@ angular.module('shipyard').factory('components', ['lodash', function (_) {
|
||||
return set;
|
||||
}
|
||||
|
||||
return {
|
||||
forShip: function (shipId) {
|
||||
return new ComponentSet(shipId);
|
||||
},
|
||||
findInternal: function(id) {
|
||||
var c = _.find(C.internal, function(o) {
|
||||
return o[id];
|
||||
})
|
||||
return c[id];
|
||||
}
|
||||
};
|
||||
return ComponentSet;
|
||||
|
||||
}]);
|
||||
@@ -1,47 +1,30 @@
|
||||
angular.module('shipyard').factory('ShipFactory', ['components', 'CalcShieldStrength', 'CalcJumpRange', 'lodash', function (Components, calcShieldStrength, calcJumpRange, _) {
|
||||
angular.module('shipyard').factory('Ship', ['Components', 'calcShieldStrength', 'calcJumpRange', 'lodash', function (Components, calcShieldStrength, calcJumpRange, _) {
|
||||
|
||||
/**
|
||||
* Ship model used to track all ship components and properties.
|
||||
*
|
||||
* @param {string} id Unique ship Id / Key
|
||||
* @param {object} shipData Data/defaults from the Ship database.
|
||||
* @param {string} id Unique ship Id / Key
|
||||
* @param {object} properties Basic ship properties such as name, manufacturer, mass, etc
|
||||
* @param {object} slots Collection of slot groups (standard/common, internal, hardpoints) with their max class size.
|
||||
*/
|
||||
function Ship(id, shipData) {
|
||||
function Ship(id, properties, slots) {
|
||||
this.id = id;
|
||||
this.defaults = shipData.defaultComponents;
|
||||
this.incCost = true;
|
||||
this.cargoScoop = { enabled: true, c: { name: 'Cargo Scoop', class: 1, rating: 'H', power: 0.6} };
|
||||
this.sgSI = null; // Shield Generator Slot Index
|
||||
this.cargoScoop = { enabled: true, c: Components.cargoScoop() };
|
||||
this.bulkheads = { incCost: true, maxClass: 8 };
|
||||
this.sgSI = null; // Shield Generator Index
|
||||
|
||||
// Copy all base properties from shipData
|
||||
angular.forEach(shipData,function(o,k){
|
||||
if(typeof o != 'object') {
|
||||
this[k] = o;
|
||||
}
|
||||
}.bind(this));
|
||||
for (p in properties) { this[p] = properties[p]; } // Copy all base properties from shipData
|
||||
|
||||
angular.forEach(shipData.slotCap, function (slots, slotGroup) { // Initialize all slots
|
||||
this[slotGroup] = []; // Initialize Slot group (Common, Hardpoints, Internal)
|
||||
for(var i = 0; i < slots.length; i++){
|
||||
this[slotGroup].push({id: null, c: null, enabled: true, incCost: true, maxClass: slots[i]});
|
||||
for (groupName in slots) { // Initialize all slots
|
||||
var slotGroup = slots[groupName];
|
||||
var group = this[groupName] = []; // Initialize Slot group (Common, Hardpoints, Internal)
|
||||
for(var i = 0; i < slotGroup.length; i++){
|
||||
group.push({id: null, c: null, enabled: true, incCost: true, maxClass: slotGroup[i]});
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the ship to the original 'manufacturer' defaults.
|
||||
*/
|
||||
Ship.prototype.clear = function() {
|
||||
this.buildWith(DB.ships[this.id].defaultComponents);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset the current build to the previously used default
|
||||
*/
|
||||
Ship.prototype.reset = function() {
|
||||
this.buildWith(this.defaults);
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds/Updates the ship instance with the components[comps] passed in.
|
||||
* @param {object} comps Collection of components used to build the ship
|
||||
@@ -50,98 +33,37 @@ angular.module('shipyard').factory('ShipFactory', ['components', 'CalcShieldStre
|
||||
var internal = this.internal;
|
||||
var common = this.common;
|
||||
var hps = this.hardpoints;
|
||||
var availCommon = DB.components.common;
|
||||
var availHardPoints = DB.components.hardpoints;
|
||||
var availInternal = DB.components.internal;
|
||||
var i,l;
|
||||
|
||||
this.bulkheads = { incCost: true, maxClass: 8, id: comps.bulkheads || 0, c: DB.components.bulkheads[this.id][comps.bulkheads || 0] };
|
||||
this.bulkheads.id = comps.bulkheads || 0;
|
||||
this.bulkheads.c = Components.bulkheads(this.id, this.bulkheads.id);
|
||||
|
||||
for(i = 0, l = comps.common.length; i < l; i++) {
|
||||
common[i].id = comps.common[i];
|
||||
common[i].c = availCommon[i][comps.common[i]];
|
||||
common[i].c = Components.common(i, comps.common[i]);
|
||||
}
|
||||
|
||||
for(i = 0, l = comps.hardpoints.length; i < l; i++) {
|
||||
if(comps.hardpoints[i] !== 0) {
|
||||
if (comps.hardpoints[i] !== 0) {
|
||||
hps[i].id = comps.hardpoints[i];
|
||||
hps[i].c = availHardPoints[comps.hardpoints[i]];
|
||||
hps[i].c = Components.hardpoints(comps.hardpoints[i]);
|
||||
} else {
|
||||
hps[i].c = hps[i].id = null;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0, l = comps.internal.length; i < l; i++) {
|
||||
if(comps.internal[i] !== 0) {
|
||||
if (comps.internal[i] !== 0) {
|
||||
internal[i].id = comps.internal[i];
|
||||
internal[i].c = Components.findInternal(comps.internal[i]);
|
||||
if(internal[i].c.group == 'sg') {
|
||||
internal[i].c = Components.internal(comps.internal[i]);
|
||||
if (internal[i].c.group == 'sg') {
|
||||
this.sgSI = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.code = this.toCode();
|
||||
this.updateTotals();
|
||||
};
|
||||
|
||||
/**
|
||||
* Serializes the selected components for all slots to a URL friendly string.
|
||||
* @return {string} Encoded string of components
|
||||
*/
|
||||
Ship.prototype.toCode = function() {
|
||||
var data = [
|
||||
this.bulkheads.id,
|
||||
_.map(this.common, idToStr),
|
||||
_.map(this.hardpoints, idToStr),
|
||||
_.map(this.internal, idToStr),
|
||||
];
|
||||
|
||||
return _.flatten(data).join('');
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility function to retrieve a safe string for selected component for a slot.
|
||||
* Used for serialization to code only.
|
||||
*
|
||||
* @private
|
||||
* @param {object} slot The slot object.
|
||||
* @return {string} The id of the selected component or '-' if none selected
|
||||
*/
|
||||
function idToStr(slot) {
|
||||
return (slot.id === null)? '-' : slot.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current ship instance's slots with components determined by the
|
||||
* code.
|
||||
*
|
||||
* @param {string} code [description]
|
||||
*/
|
||||
Ship.prototype.buildFromCode = function (code) {
|
||||
var commonCount = this.common.length;
|
||||
var hpCount = commonCount + this.hardpoints.length;
|
||||
var comps = {
|
||||
bulkheads: code.charAt(0) * 1,
|
||||
common: new Array(this.common.length),
|
||||
hardpoints: new Array(this.hardpoints.length),
|
||||
internal: new Array(this.internal.length)
|
||||
};
|
||||
|
||||
// TODO: improve...
|
||||
for (var i = 1, c = 0, l = code.length; i < l; i++) {
|
||||
var isNull = code.charAt(i) == '-';
|
||||
if (c < commonCount) {
|
||||
comps.common[c] = isNull? 0 : code.substring(i, i + 2);
|
||||
} else if (c < hpCount) {
|
||||
comps.hardpoints[c - commonCount] = isNull? 0 : code.substring(i, i + 2);
|
||||
} else {
|
||||
comps.internal[c - hpCount] = isNull? 0 : code.substring(i, i + 2);
|
||||
internal[i].id = internal[i].c = null;
|
||||
}
|
||||
if (!isNull) {
|
||||
i++;
|
||||
}
|
||||
c++;
|
||||
}
|
||||
this.defaults = comps;
|
||||
this.buildWith(comps);
|
||||
this.updateTotals();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -171,7 +93,6 @@ angular.module('shipyard').factory('ShipFactory', ['components', 'CalcShieldStre
|
||||
// TODO: shield recharge rate
|
||||
// TODO: armor bonus / damage reduction for bulkheads
|
||||
// TODO: thermal load and weapon recharge rate
|
||||
this.code = this.toCode();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -195,6 +116,10 @@ angular.module('shipyard').factory('ShipFactory', ['components', 'CalcShieldStre
|
||||
return sum;
|
||||
}
|
||||
|
||||
function findInternal(slots, group) {
|
||||
|
||||
}
|
||||
|
||||
Ship.prototype.useBulkhead = function(index) {
|
||||
this.bulkheads.id = index;
|
||||
this.bulkheads.c = DB.components.bulkheads[this.id][index];
|
||||
@@ -238,21 +163,5 @@ angular.module('shipyard').factory('ShipFactory', ['components', 'CalcShieldStre
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Ship Factory function. Created a new instance of a ship based on the ship type.
|
||||
*
|
||||
* @param {string} id Id/Key for the Ship type
|
||||
* @param {object} shipData [description]
|
||||
* @param {string} code [optional] Code to build the ship with
|
||||
* @return {Ship} A new Ship instance
|
||||
*/
|
||||
return function (id, shipData, code) {
|
||||
var s = new Ship(id, shipData);
|
||||
if (code) {
|
||||
s.buildFromCode(code);
|
||||
} else {
|
||||
s.clear();
|
||||
}
|
||||
return s;
|
||||
};
|
||||
return Ship;
|
||||
}]);
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/**
|
||||
* This module contains all of the logic and models corresponding to
|
||||
* information or behavoir in Elite Dangerous.
|
||||
*
|
||||
* This file contains values and functions that can be reused across the app.
|
||||
*/
|
||||
angular.module('shipyard', [])
|
||||
.value('commonArray', [
|
||||
'Power Plant',
|
||||
@@ -13,14 +19,33 @@ angular.module('shipyard', [])
|
||||
sc:'Scanners',
|
||||
am:'Auto Field-Maintenance Unit',
|
||||
cr:'Cargo Racks',
|
||||
fi:'Frame Shift Drive Interdictor',
|
||||
hb:'Hatch Breaker Limpet Controller',
|
||||
fi:'FSD Interdictor',
|
||||
hb:'Hatch Breaker Limpet Ctrl',
|
||||
hr:'Hull Reinforcement Package',
|
||||
rf:'Refinery',
|
||||
sb:'Shield Cell Bank',
|
||||
sg:'Shield Generator',
|
||||
dc:'Docking Computer'
|
||||
})
|
||||
.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"
|
||||
})
|
||||
.value('shipPurpose', {
|
||||
mp: 'Multi Purpose',
|
||||
fr: 'Freighter',
|
||||
@@ -35,13 +60,6 @@ angular.module('shipyard', [])
|
||||
'Large',
|
||||
'Capital',
|
||||
])
|
||||
.factory('commonMap', ['commonArray', function (commonArray) {
|
||||
var commonMap = {};
|
||||
for(var i = 0; i < commonArray.length; i++) {
|
||||
commonMap[commonArray[i]] = i;
|
||||
}
|
||||
return commonMap;
|
||||
}])
|
||||
.value('hardPointClass', [
|
||||
'Utility',
|
||||
'Small',
|
||||
@@ -49,53 +67,7 @@ angular.module('shipyard', [])
|
||||
'Large',
|
||||
'Huge'
|
||||
])
|
||||
.factory('hardpointGroup', function () {
|
||||
function groupToLabel (grp) {
|
||||
var a = grp.toLowerCase().split('');
|
||||
var l = [];
|
||||
switch(a[0]) {
|
||||
case 's':
|
||||
l.push('Small');
|
||||
break;
|
||||
case 'm':
|
||||
l.push('Medium');
|
||||
break;
|
||||
case 'l':
|
||||
l.push('Large');
|
||||
break;
|
||||
case 'h':
|
||||
l.push('Huge');
|
||||
break;
|
||||
case 'u':
|
||||
l.push('Utility');
|
||||
break;
|
||||
}
|
||||
switch(a[1]) {
|
||||
case 'o':
|
||||
l.push('Other');
|
||||
break;
|
||||
case 'k':
|
||||
l.push('Kinetic');
|
||||
break;
|
||||
case 't':
|
||||
l.push('Thermal');
|
||||
break;
|
||||
case 's':
|
||||
l.push('Scanner');
|
||||
break;
|
||||
case 'b':
|
||||
l.push('Booster');
|
||||
break;
|
||||
case 'm':
|
||||
l.push('Mount');
|
||||
break;
|
||||
}
|
||||
return l.join(' ');
|
||||
}
|
||||
|
||||
return groupToLabel;
|
||||
})
|
||||
.factory('CalcJumpRange', function() {
|
||||
.factory('calcJumpRange', function() {
|
||||
/**
|
||||
* Calculate the maximum single jump range based on mass and a specific FSD
|
||||
* @param {number} mass Mass of a ship: laden, unlanden, partially laden, etc
|
||||
@@ -107,7 +79,7 @@ angular.module('shipyard', [])
|
||||
return Math.pow(Math.min(fuel || Infinity, fsd.maxfuel) / fsd.fuelmul, 1 / fsd.fuelpower ) * fsd.optmass / mass;
|
||||
};
|
||||
})
|
||||
.factory('CalcShieldStrength', function() {
|
||||
.factory('calcShieldStrength', function() {
|
||||
/**
|
||||
* Calculate the a ships shield strength based on mass, shield generator and shield boosters used.
|
||||
*
|
||||
@@ -119,6 +91,9 @@ angular.module('shipyard', [])
|
||||
* @return {number} Approximate shield strengh in MJ
|
||||
*/
|
||||
return function (mass, shields, sg, multiplier) {
|
||||
if (!sg) {
|
||||
return 0;
|
||||
}
|
||||
if (mass <= sg.minmass) {
|
||||
return shields * multiplier * sg.minmul;
|
||||
}
|
||||
|
||||
35
app/js/shipyard/service-components.js
Normal file
35
app/js/shipyard/service-components.js
Normal file
@@ -0,0 +1,35 @@
|
||||
angular.module('shipyard').service('Components', ['lodash', 'ComponentSet', function (_, ComponentSet) {
|
||||
var C = DB.components;
|
||||
|
||||
this.cargoScoop = function() {
|
||||
return { name: 'Cargo Scoop', class: 1, rating: 'H', power: 0.6};
|
||||
}
|
||||
|
||||
this.common = function (typeIndex, componentId) {
|
||||
return C.common[typeIndex][componentId];
|
||||
};
|
||||
|
||||
this.hardpoints = function(id) {
|
||||
var c = _.find(C.hardpoints, function(o) {
|
||||
return o[id];
|
||||
})
|
||||
return c[id];
|
||||
};
|
||||
|
||||
this.internal = function(id) {
|
||||
var c = _.find(C.internal, function(o) {
|
||||
return o[id];
|
||||
})
|
||||
return c[id];
|
||||
};
|
||||
|
||||
this.bulkheads = function(shipId, bulkheadsId) {
|
||||
return C.bulkheads[shipId][bulkheadsId];
|
||||
};
|
||||
|
||||
this.forShip = function (shipId) {
|
||||
var ship = DB.ships[shipId];
|
||||
return new ComponentSet(C, ship.properties.mass, ship.slots.common, ship.slots.internal[0], ship.slots.hardpoints[0]);
|
||||
};
|
||||
|
||||
}]);
|
||||
Reference in New Issue
Block a user