mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-10 07:05:35 +00:00
UI improvements, save build feature partial implementation
This commit is contained in:
@@ -1,44 +1,9 @@
|
||||
angular.module('app', ['ui.router', 'shipyard', 'ngLodash', 'app.templates'])
|
||||
.config(['$stateProvider', '$urlRouterProvider', '$locationProvider', function($stateProvider, $urlRouterProvider, $locationProvider) {
|
||||
$locationProvider.html5Mode(true);
|
||||
$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' });
|
||||
|
||||
}])
|
||||
.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
|
||||
$state.go('error', error, {location:false, reload:true}); // Go to error state, reload the controller, keep the current URL
|
||||
});
|
||||
|
||||
// Global Reference variables
|
||||
@@ -47,7 +12,7 @@ angular.module('app', ['ui.router', 'shipyard', 'ngLodash', 'app.templates'])
|
||||
$rootScope.SZ = sz;
|
||||
$rootScope.HPC = hpc;
|
||||
$rootScope.igMap = igMap;
|
||||
window.hgmap = $rootScope.hgMap = hgMap;
|
||||
$rootScope.hgMap = hgMap;
|
||||
$rootScope.ships = DB.ships;
|
||||
$rootScope.title = 'Coriolis';
|
||||
|
||||
@@ -61,11 +26,16 @@ angular.module('app', ['ui.router', 'shipyard', 'ngLodash', 'app.templates'])
|
||||
|
||||
// Global Event Listeners
|
||||
$doc.bind('keyup', function (e) {
|
||||
$rootScope.$broadcast('keyup', e);
|
||||
if(e.keyCode == 27) { // Escape Key
|
||||
$rootScope.$broadcast('close', e);
|
||||
$rootScope.$apply();
|
||||
} else {
|
||||
$rootScope.$broadcast('keyup', e);
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.bgClicked = function (e) {
|
||||
$rootScope.$broadcast('bgClicked', e);
|
||||
$rootScope.$broadcast('close', e);
|
||||
}
|
||||
|
||||
}]);
|
||||
|
||||
56
app/js/config.js
Normal file
56
app/js/config.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Sets up the routes and handlers before the Angular app is kicked off.
|
||||
*/
|
||||
angular.module('app').config(['$provide','$stateProvider', '$urlRouterProvider', '$locationProvider', function ($provide, $stateProvider, $urlRouterProvider, $locationProvider) {
|
||||
// Use HTML5 push and replace state if possible
|
||||
$locationProvider.html5Mode(true);
|
||||
/**
|
||||
* Set up all states and their routes.
|
||||
*/
|
||||
$stateProvider
|
||||
.state('outfit', {
|
||||
url: '/outfit/:shipId/:code?bn',
|
||||
params: {
|
||||
// TODO: Squash:false not working due to UI-router issue
|
||||
shipId: { value: 'sidewinder', squash: false}, // Allow 'shipId' parameter to default to
|
||||
code: { value: undefined, 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: 'no-ship', message: $p.shipId };
|
||||
}
|
||||
}]
|
||||
}
|
||||
})
|
||||
.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' })
|
||||
|
||||
// Redirects
|
||||
$urlRouterProvider.when('/outfit','/outfit/sidewinder/');
|
||||
|
||||
/**
|
||||
* 404 Handler - Keep current URL/ do not redirect, change to error state.
|
||||
*/
|
||||
$urlRouterProvider.otherwise(function ($injector, $location) {
|
||||
// Go to error state, reload the controller, keep the current URL
|
||||
$injector.get('$state').go('error', { type: 404, message: null, details: null }, {location:false, reload:true});
|
||||
return $location.path;
|
||||
});
|
||||
|
||||
/**
|
||||
* Global Error Handler. Decorates the existing error handler such that it
|
||||
* redirects uncaught errors to the error page.
|
||||
*
|
||||
*/
|
||||
$provide.decorator('$exceptionHandler', ['$delegate', '$injector', function ($delegate, $injector) {
|
||||
return function(exception, cause) {
|
||||
// Go to error state, reload the controller, keep the current URL
|
||||
$injector.get('$state').go('error', { details: exception }, {location:false, reload:true});
|
||||
$delegate(exception, cause);
|
||||
};
|
||||
}]);
|
||||
|
||||
}]);
|
||||
@@ -1,15 +1,29 @@
|
||||
angular.module('app')
|
||||
.controller('ErrorController', ['$rootScope','$scope','$stateParams', '$location', function ($rootScope, $scope, $p, $location) {
|
||||
$rootScope.title = 'Error';
|
||||
$scope.path = $location.path();
|
||||
$scope.type = $p.type || 'unknown';
|
||||
|
||||
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();
|
||||
switch ($scope.type) {
|
||||
case 404:
|
||||
$scope.msgPre = 'Page';
|
||||
$scope.msgHighlight = $scope.path;
|
||||
$scope.msgPost = 'Not Found';
|
||||
$scope.image = 'deep-space';
|
||||
break;
|
||||
case 'no-ship':
|
||||
$scope.msgPre = 'Ship';
|
||||
$scope.msgHighlight = $p.message;
|
||||
$scope.msgPost = 'does not exist';
|
||||
$scope.image = 'thargoid';
|
||||
break;
|
||||
case 'build-fail':
|
||||
$scope.msgPre = 'Build Failure!';
|
||||
$scope.image = 'ship-explode';
|
||||
break;
|
||||
default:
|
||||
$scope.msgPre = "Uh, this is bad..";
|
||||
$scope.image = 'thargoid';
|
||||
}
|
||||
|
||||
}]);
|
||||
@@ -1,7 +1,8 @@
|
||||
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 data = DB.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
|
||||
|
||||
// Update the ship instance with the code (if provided) or the 'factory' defaults.
|
||||
if ($p.code) {
|
||||
Serializer.toShip(ship, $p.code); // Populate components from 'code' URL param
|
||||
$scope.code = $p.code;
|
||||
@@ -10,7 +11,7 @@ angular.module('app').controller('OutfitController', ['$rootScope','$scope', '$s
|
||||
}
|
||||
|
||||
$scope.buildName = $p.bn;
|
||||
$rootScope.title = ship.name + $scope.buildName? ' - ' + $scope.buildName: '';
|
||||
$rootScope.title = ship.name + ($scope.buildName? ' - ' + $scope.buildName: '');
|
||||
$scope.ship = ship;
|
||||
$scope.pp = ship.common[0]; // Power Plant
|
||||
$scope.th = ship.common[1]; // Thruster
|
||||
@@ -23,11 +24,17 @@ angular.module('app').controller('OutfitController', ['$rootScope','$scope', '$s
|
||||
$scope.internal = ship.internal;
|
||||
$scope.availCS = Components.forShip(ship.id);
|
||||
$scope.selectedSlot = null;
|
||||
$scope.lastSaveCode = Persist.getBuild(ship.id, $scope.buildName);
|
||||
$scope.savedCode = Persist.getBuild(ship.id, $scope.buildName);
|
||||
|
||||
// for debugging
|
||||
window.myScope = $scope;
|
||||
|
||||
/**
|
||||
* 'Opens' a select for component selection.
|
||||
*
|
||||
* @param {[type]} e The event object
|
||||
* @param {[type]} slot The slot that is being 'opened' for selection
|
||||
*/
|
||||
$scope.selectSlot = function(e, slot) {
|
||||
e.stopPropagation();
|
||||
if ($scope.selectedSlot == slot) {
|
||||
@@ -37,6 +44,14 @@ angular.module('app').controller('OutfitController', ['$rootScope','$scope', '$s
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the ships build with the selected component for the
|
||||
* specified slot. Prevents the click event from propagation.
|
||||
*
|
||||
* @param {string} type Shorthand key/string for identifying the slot & component type
|
||||
* @param {[type]} slot The slot object belonging to the ship instance
|
||||
* @param {[type]} e The event object
|
||||
*/
|
||||
$scope.select = function(type, slot, e) {
|
||||
e.stopPropagation();
|
||||
if (e.srcElement.id) {
|
||||
@@ -54,7 +69,6 @@ angular.module('app').controller('OutfitController', ['$rootScope','$scope', '$s
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,21 +76,28 @@ angular.module('app').controller('OutfitController', ['$rootScope','$scope', '$s
|
||||
* 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});
|
||||
if ($scope.buildName && $scope.savedCode) {
|
||||
Serializer.toShip(ship, $scope.savedCode); // Repopulate with components from last save
|
||||
$scope.code = $scope.savedCode;
|
||||
$state.go('outfit', {shipId: ship.id, code: $scope.savedCode, bn: $scope.buildName}, {location:'replace', notify:false});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Save the current build. Will replace the saved build if there is one
|
||||
* for this ship & with the exact name.
|
||||
*/
|
||||
$scope.saveBuild = function() {
|
||||
if ($scope.code && $scope.code != $scope.lastSaveCode) {
|
||||
if($scope.code != $scope.savedCode) {
|
||||
Persist.saveBuild(ship.id, $scope.buildName, $scope.code);
|
||||
$scope.lastSaveCode = $scope.code;
|
||||
$rootScope.$broadcast('buildSaved', ship.id, $scope.buildName, $scope.code);
|
||||
$scope.savedCode = $scope.code;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Permanently delete the current build and redirect/reload this controller
|
||||
* with the 'factory' build of the current ship.
|
||||
*/
|
||||
$scope.deleteBuild = function() {
|
||||
Persist.deleteBuild(ship.id, $scope.buildName);
|
||||
$rootScope.$broadcast('buildDeleted', $scope.saveName, ship.id);
|
||||
@@ -84,17 +105,20 @@ angular.module('app').controller('OutfitController', ['$rootScope','$scope', '$s
|
||||
}
|
||||
|
||||
$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
|
||||
// CTRL + S or CMD + S will override the default and save the build is possible
|
||||
if (keyEvent.keycode == 83 && keyEvent.ctrlKey) {
|
||||
e.preventDefault();
|
||||
$scope.saveBuild();
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on('bgClicked', function (e, keyEvent) {
|
||||
// Hide any open menu/slot/etc if escape key is pressed
|
||||
$rootScope.$on('escape', function (e, keyEvent) {
|
||||
$scope.selectedSlot = null;
|
||||
$scope.$apply();
|
||||
});
|
||||
// Hide any open menu/slot/etc if the background is clicked
|
||||
$rootScope.$on('close', function (e, keyEvent) {
|
||||
$scope.selectedSlot = null;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
angular.module('app')
|
||||
.controller('ShipyardController', ['$rootScope', function ($rootScope) {
|
||||
angular.module('app').controller('ShipyardController', ['$rootScope', function ($rootScope) {
|
||||
$rootScope.title = 'Coriolis - Shipyard';
|
||||
}]);
|
||||
37
app/js/directives/directive-header.js
Normal file
37
app/js/directives/directive-header.js
Normal file
@@ -0,0 +1,37 @@
|
||||
angular.module('app').directive('shipyardHeader', ['$rootScope', 'Persist', function ($rootScope, Persist) {
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: 'views/_header.html',
|
||||
scope: true,
|
||||
link: function (scope) {
|
||||
scope.openedMenu = null;
|
||||
scope.ships = DB.ships;
|
||||
scope.allBuilds = Persist.builds;
|
||||
scope.bs = Persist.state;
|
||||
console.log(scope);
|
||||
|
||||
$rootScope.$on('$stateChangeStart',function(){
|
||||
scope.openedMenu = null;
|
||||
});
|
||||
|
||||
$rootScope.$on('close', function (e, keyEvent) {
|
||||
scope.openedMenu = null;
|
||||
});
|
||||
|
||||
scope.openMenu = function (menu) {
|
||||
if(menu == scope.openedMenu) {
|
||||
scope.openedMenu = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (menu == 'b' && !scope.bs.hasBuilds) {
|
||||
scope.openedMenu = null;
|
||||
return;
|
||||
}
|
||||
scope.openedMenu = menu;
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
}]);
|
||||
@@ -1,13 +0,0 @@
|
||||
angular.module('app').directive('shipyardMenu', function () {
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: 'views/menu.html',
|
||||
link: function () {
|
||||
|
||||
// TODO: Saved Ships: load, save, save as, delete, export
|
||||
// TODO: Links: github, forum, etc
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,3 +1,6 @@
|
||||
/**
|
||||
* [description]
|
||||
*/
|
||||
angular.module('app').service('Persist', ['lodash', function (_) {
|
||||
var LS_KEY = 'builds';
|
||||
|
||||
@@ -9,6 +12,9 @@ angular.module('app').service('Persist', ['lodash', function (_) {
|
||||
this.builds = {};
|
||||
}
|
||||
|
||||
this.state = {
|
||||
hasBuilds: Object.keys(this.builds).length > 0
|
||||
}
|
||||
/**
|
||||
* Persist a ship build in local storage.
|
||||
*
|
||||
@@ -22,7 +28,9 @@ angular.module('app').service('Persist', ['lodash', function (_) {
|
||||
}
|
||||
|
||||
this.builds[shipId][name] = code;
|
||||
localStorage.setItem(LS_KEY, angular.toJson(this.builds)); // Persist updated build collection to localstorage
|
||||
this.state.hasBuilds = true;
|
||||
// Persist updated build collection to localstorage
|
||||
localStorage.setItem(LS_KEY, angular.toJson(this.builds));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,11 +56,15 @@ angular.module('app').service('Persist', ['lodash', function (_) {
|
||||
* @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];
|
||||
if(this.builds[shipId][name]) {
|
||||
delete this.builds[shipId][name];
|
||||
if (Object.keys(this.builds[shipId]).length == 0) {
|
||||
delete this.builds[shipId];
|
||||
this.state.hasBuilds = Object.keys(this.builds).length > 0;
|
||||
}
|
||||
// Persist updated build collection to localstorage
|
||||
localStorage.setItem(LS_KEY, angular.toJson(this.builds));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}]);
|
||||
}]);
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
* information or behavoir in Elite Dangerous.
|
||||
*
|
||||
* This file contains values and functions that can be reused across the app.
|
||||
*
|
||||
* @requires ngLodash is a dependency of this module.
|
||||
*/
|
||||
angular.module('shipyard', [])
|
||||
angular.module('shipyard', ['ngLodash'])
|
||||
.value('commonArray', [
|
||||
'Power Plant',
|
||||
'Thrusters',
|
||||
@@ -67,42 +69,39 @@ angular.module('shipyard', [])
|
||||
'Large',
|
||||
'Huge'
|
||||
])
|
||||
.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
|
||||
* @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass
|
||||
* @param {number} fuel Optional - The fuel consumed during the jump (must be less than the drives max fuel per jump)
|
||||
* @return {number} Distance in Light Years
|
||||
*/
|
||||
return function(mass, fsd, fuel) {
|
||||
/**
|
||||
* 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
|
||||
* @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass
|
||||
* @param {number} fuel Optional - The fuel consumed during the jump (must be less than the drives max fuel per jump)
|
||||
* @return {number} Distance in Light Years
|
||||
*/
|
||||
.value('calcJumpRange', function(mass, fsd, fuel) {
|
||||
return Math.pow(Math.min(fuel || Infinity, fsd.maxfuel) / fsd.fuelmul, 1 / fsd.fuelpower ) * fsd.optmass / mass;
|
||||
};
|
||||
})
|
||||
.factory('calcShieldStrength', function() {
|
||||
/**
|
||||
* Calculate the a ships shield strength based on mass, shield generator and shield boosters used.
|
||||
*
|
||||
* @private
|
||||
* @param {number} mass Current mass of the ship
|
||||
* @param {number} shields Base Shield strength MJ for ship
|
||||
* @param {object} sg The shield generator used
|
||||
* @param {number} multiplier Shield multiplier for ship (1 + shield boosters if any)
|
||||
* @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;
|
||||
}
|
||||
if (mass < sg.optmass) {
|
||||
return shields * multiplier * (sg.minmul + (mass - sg.minmass) / (sg.optmass - sg.minmass) * (sg.optmul - sg.minmul));
|
||||
}
|
||||
if (mass < sg.maxmass) {
|
||||
return shields * multiplier * (sg.optmul + (mass - sg.optmass) / (sg.maxmass - sg.optmass) * (sg.maxmul - sg.optmul));
|
||||
}
|
||||
return shields * multiplier * sg.maxmul;
|
||||
/**
|
||||
* Calculate the a ships shield strength based on mass, shield generator and shield boosters used.
|
||||
*
|
||||
* @private
|
||||
* @param {number} mass Current mass of the ship
|
||||
* @param {number} shields Base Shield strength MJ for ship
|
||||
* @param {object} sg The shield generator used
|
||||
* @param {number} multiplier Shield multiplier for ship (1 + shield boosters if any)
|
||||
* @return {number} Approximate shield strengh in MJ
|
||||
*/
|
||||
.value('calcShieldStrength', function (mass, shields, sg, multiplier) {
|
||||
if (!sg) {
|
||||
return 0;
|
||||
}
|
||||
if (mass <= sg.minmass) {
|
||||
return shields * multiplier * sg.minmul;
|
||||
}
|
||||
if (mass < sg.optmass) {
|
||||
return shields * multiplier * (sg.minmul + (mass - sg.minmass) / (sg.optmass - sg.minmass) * (sg.optmul - sg.minmul));
|
||||
}
|
||||
if (mass < sg.maxmass) {
|
||||
return shields * multiplier * (sg.optmul + (mass - sg.optmass) / (sg.maxmass - sg.optmass) * (sg.maxmul - sg.optmul));
|
||||
}
|
||||
return shields * multiplier * sg.maxmul;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user