mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-08 14:33:22 +00:00
Merge branch 'release/2.9.18'
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -9,4 +9,5 @@ env
|
||||
*.swp
|
||||
.project
|
||||
.vscode/
|
||||
docs/
|
||||
docs/
|
||||
package-lock.json
|
||||
|
||||
13790
package-lock.json
generated
13790
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "coriolis_shipyard",
|
||||
"version": "2.9.17",
|
||||
"version": "2.9.18",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EDCD/coriolis"
|
||||
|
||||
@@ -134,9 +134,9 @@ export default class Coriolis extends React.Component {
|
||||
console && console.error && console.error(arguments); // eslint-disable-line no-console
|
||||
if (errObj) {
|
||||
if (errObj instanceof Error) {
|
||||
bugsnagClient.notify(errObj) // eslint-disable-line
|
||||
bugsnagClient.notify(errObj); // eslint-disable-line
|
||||
} else if (errObj instanceof String) {
|
||||
bugsnagClient.notify(msg, errObj) // eslint-disable-line
|
||||
bugsnagClient.notify(msg, errObj); // eslint-disable-line
|
||||
}
|
||||
}
|
||||
this.setState({
|
||||
@@ -180,13 +180,13 @@ export default class Coriolis extends React.Component {
|
||||
case 72: // 'h'
|
||||
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + h
|
||||
e.preventDefault();
|
||||
this._showModal(<ModalHelp />);
|
||||
this._showModal(<ModalHelp/>);
|
||||
}
|
||||
break;
|
||||
case 73: // 'i'
|
||||
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + i
|
||||
e.preventDefault();
|
||||
this._showModal(<ModalImport />);
|
||||
this._showModal(<ModalImport/>);
|
||||
}
|
||||
break;
|
||||
case 79: // 'o'
|
||||
@@ -208,7 +208,7 @@ export default class Coriolis extends React.Component {
|
||||
* @param {React.Component} content Modal Content
|
||||
*/
|
||||
_showModal(content) {
|
||||
let modal = <div className='modal-bg' onClick={(e) => this._hideModal() }>{content}</div>;
|
||||
let modal = <div className='modal-bg' onClick={(e) => this._hideModal()}>{content}</div>;
|
||||
this.setState({ modal });
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ export default class Coriolis extends React.Component {
|
||||
return this.emitter.addListener('windowResize', listener);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Add a listener to global commands such as save,
|
||||
* @param {Function} listener Listener callback
|
||||
* @return {Object} Subscription token
|
||||
@@ -323,45 +323,57 @@ export default class Coriolis extends React.Component {
|
||||
componentWillMount() {
|
||||
// Listen for appcache updated event, present refresh to update view
|
||||
// Check that service workers are registered
|
||||
if (navigator.storage && navigator.storage.persist) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.storage.persist().then(granted => {
|
||||
if (granted)
|
||||
console.log('Storage will not be cleared except by explicit user action');
|
||||
else
|
||||
console.log('Storage may be cleared by the UA under storage pressure.');
|
||||
});
|
||||
});
|
||||
}
|
||||
if ('serviceWorker' in navigator) {
|
||||
// Your service-worker.js *must* be located at the top-level directory relative to your site.
|
||||
// It won't be able to control pages unless it's located at the same level or higher than them.
|
||||
// *Don't* register service worker file in, e.g., a scripts/ sub-directory!
|
||||
// See https://github.com/slightlyoff/ServiceWorker/issues/468
|
||||
const self = this;
|
||||
navigator.serviceWorker.register('/service-worker.js').then(function(reg) {
|
||||
// updatefound is fired if service-worker.js changes.
|
||||
reg.onupdatefound = function() {
|
||||
// The updatefound event implies that reg.installing is set; see
|
||||
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
|
||||
var installingWorker = reg.installing;
|
||||
window.addEventListener('load', () => {
|
||||
// Your service-worker.js *must* be located at the top-level directory relative to your site.
|
||||
// It won't be able to control pages unless it's located at the same level or higher than them.
|
||||
// *Don't* register service worker file in, e.g., a scripts/ sub-directory!
|
||||
// See https://github.com/slightlyoff/ServiceWorker/issues/468
|
||||
const self = this;
|
||||
navigator.serviceWorker.register('/service-worker.js').then(function(reg) {
|
||||
// updatefound is fired if service-worker.js changes.
|
||||
reg.onupdatefound = function() {
|
||||
// The updatefound event implies that reg.installing is set; see
|
||||
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
|
||||
var installingWorker = reg.installing;
|
||||
|
||||
installingWorker.onstatechange = function() {
|
||||
switch (installingWorker.state) {
|
||||
case 'installed':
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the old content will have been purged and the fresh content will
|
||||
// have been added to the cache.
|
||||
// It's the perfect time to display a "New content is available; please refresh."
|
||||
// message in the page's interface.
|
||||
console.log('New or updated content is available.');
|
||||
self.setState({ appCacheUpdate: true }); // Browser downloaded a new app cache.
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a "Content is cached for offline use." message.
|
||||
console.log('Content is now available offline!');
|
||||
self.setState({ appCacheUpdate: true }); // Browser downloaded a new app cache.
|
||||
}
|
||||
break;
|
||||
installingWorker.onstatechange = function() {
|
||||
switch (installingWorker.state) {
|
||||
case 'installed':
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the old content will have been purged and the fresh content will
|
||||
// have been added to the cache.
|
||||
// It's the perfect time to display a "New content is available; please refresh."
|
||||
// message in the page's interface.
|
||||
console.log('New or updated content is available.');
|
||||
self.setState({ appCacheUpdate: true }); // Browser downloaded a new app cache.
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a "Content is cached for offline use." message.
|
||||
console.log('Content is now available offline!');
|
||||
self.setState({ appCacheUpdate: true }); // Browser downloaded a new app cache.
|
||||
}
|
||||
break;
|
||||
|
||||
case 'redundant':
|
||||
console.error('The installing service worker became redundant.');
|
||||
break;
|
||||
}
|
||||
case 'redundant':
|
||||
console.error('The installing service worker became redundant.');
|
||||
break;
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
}).catch(function(e) {
|
||||
console.error('Error during service worker registration:', e);
|
||||
}).catch(function(e) {
|
||||
console.error('Error during service worker registration:', e);
|
||||
});
|
||||
});
|
||||
}
|
||||
window.onerror = this._onError.bind(this);
|
||||
@@ -381,16 +393,22 @@ export default class Coriolis extends React.Component {
|
||||
render() {
|
||||
let currentMenu = this.state.currentMenu;
|
||||
|
||||
return <div style={{ minHeight: '100%' }} onClick={this._closeMenu} className={ this.state.noTouch ? 'no-touch' : null }>
|
||||
<Header appCacheUpdate={this.state.appCacheUpdate} currentMenu={currentMenu} />
|
||||
{ this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) : <NotFoundPage/> }
|
||||
{ this.state.modal }
|
||||
{ this.state.tooltip }
|
||||
return <div style={{ minHeight: '100%' }} onClick={this._closeMenu}
|
||||
className={this.state.noTouch ? 'no-touch' : null}>
|
||||
<Header appCacheUpdate={this.state.appCacheUpdate} currentMenu={currentMenu}/>
|
||||
{this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) :
|
||||
<NotFoundPage/>}
|
||||
{this.state.modal}
|
||||
{this.state.tooltip}
|
||||
<footer>
|
||||
<div className="right cap">
|
||||
<a href="https://github.com/EDCD/coriolis" target="_blank" title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a>
|
||||
<a href="https://github.com/EDCD/coriolis" target="_blank"
|
||||
title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a>
|
||||
<br/>
|
||||
<a href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'} target="_blank" title={'Coriolis Commits since' + window.CORIOLIS_DATE}>Commits since last release ({window.CORIOLIS_DATE})</a>
|
||||
<a
|
||||
href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'}
|
||||
target="_blank" title={'Coriolis Commits since' + window.CORIOLIS_DATE}>Commits since last release
|
||||
({window.CORIOLIS_DATE})</a>
|
||||
</div>
|
||||
</footer>
|
||||
</div>;
|
||||
|
||||
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import cn from 'classnames';
|
||||
import NumberEditor from 'react-number-editor';
|
||||
import { isValueBeneficial } from '../utils/BlueprintFunctions';
|
||||
|
||||
/**
|
||||
* Modification
|
||||
@@ -38,30 +39,17 @@ export default class Modification extends TranslatedComponent {
|
||||
* in a value by hand
|
||||
*/
|
||||
_updateValue(value) {
|
||||
const name = this.props.name;
|
||||
let scaledValue = Math.round(Number(value) * 100);
|
||||
// Limit to +1000% / -99.99%
|
||||
if (scaledValue > 100000) {
|
||||
scaledValue = 100000;
|
||||
value = 1000;
|
||||
}
|
||||
if (scaledValue < -9999) {
|
||||
scaledValue = -9999;
|
||||
value = -99.99;
|
||||
}
|
||||
|
||||
let m = this.props.m;
|
||||
let ship = this.props.ship;
|
||||
ship.setModification(m, name, scaledValue, true);
|
||||
|
||||
let { m, name, ship } = this.props;
|
||||
value = Math.max(Math.min(value, 50000), -50000);
|
||||
ship.setModification(m, name, value, true, true);
|
||||
this.setState({ value });
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when an update to slider value is finished i.e. when losing focus
|
||||
*
|
||||
* pnellesen (24/05/2018): added value check below - this should prevent experimental effects from being recalculated
|
||||
* with each onBlur event, even when no change has actually been made to the field.
|
||||
*
|
||||
* pnellesen (24/05/2018): added value check below - this should prevent experimental effects from being recalculated
|
||||
* with each onBlur event, even when no change has actually been made to the field.
|
||||
*/
|
||||
_updateFinished() {
|
||||
if (this.props.value != this.state.value) {
|
||||
@@ -75,28 +63,50 @@ export default class Modification extends TranslatedComponent {
|
||||
* @return {React.Component} modification
|
||||
*/
|
||||
render() {
|
||||
let translate = this.context.language.translate;
|
||||
let { translate, formats, units } = this.context.language;
|
||||
let { m, name } = this.props;
|
||||
let modValue = m.getChange(name);
|
||||
|
||||
if (name === 'damagedist') {
|
||||
// We don't show damage distribution
|
||||
return null;
|
||||
}
|
||||
|
||||
let symbol;
|
||||
if (name === 'jitter') {
|
||||
symbol = '°';
|
||||
} else if (name !== 'burst' && name != 'burstrof') {
|
||||
symbol = '%';
|
||||
}
|
||||
if (symbol) {
|
||||
symbol = ' (' + symbol + ')';
|
||||
}
|
||||
|
||||
return (
|
||||
<div onBlur={this._updateFinished.bind(this)} className={'cb'} key={name} ref={ modItem => this.props.modItems[name] = modItem }>
|
||||
<div className={'cb'}>{translate(name, m.grp)}{symbol}</div>
|
||||
<NumberEditor className={'cb'} style={{ width: '90%', textAlign: 'center' }} step={0.01} stepModifier={1} decimals={2} value={this.state.value} onValueChange={this._updateValue.bind(this)} onKeyDown={ this.props.onKeyDown } />
|
||||
<div onBlur={this._updateFinished.bind(this)} key={name}
|
||||
className={cn('cb', 'modification-container')}
|
||||
ref={ modItem => this.props.modItems[name] = modItem }>
|
||||
<span className={'cb'}>{translate(name, m.grp)}</span>
|
||||
<span className={'header-adjuster'}></span>
|
||||
<table style={{ width: '100%' }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className={'input-container'}>
|
||||
<span>
|
||||
{this.props.editable ?
|
||||
<NumberEditor className={'cb'} value={this.state.value}
|
||||
decimals={2} style={{ textAlign: 'right' }} step={0.01}
|
||||
stepModifier={1} onKeyDown={ this.props.onKeyDown }
|
||||
onValueChange={this._updateValue.bind(this)} /> :
|
||||
<input type="text" value={formats.f2(this.state.value)}
|
||||
disabled className={'number-editor'}
|
||||
style={{ textAlign: 'right', cursor: 'inherit' }}/>
|
||||
}
|
||||
<span className={'unit-container'}>
|
||||
{units[m.getStoredUnitFor(name)]}
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td style={{ textAlign: 'center' }} className={
|
||||
modValue ?
|
||||
isValueBeneficial(name, modValue) ? 'secondary': 'warning':
|
||||
''
|
||||
}>
|
||||
{formats.f2(modValue / 100) || 0}%
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ import {
|
||||
specialToolTip
|
||||
} from '../utils/BlueprintFunctions';
|
||||
|
||||
const MODIFICATIONS_COMPARATOR = (mod1, mod2) => {
|
||||
return mod1.props.name.localeCompare(mod2.props.name);
|
||||
};
|
||||
|
||||
/**
|
||||
* Modifications menu
|
||||
*/
|
||||
@@ -124,7 +128,7 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
// Initial modification menu
|
||||
event.preventDefault();
|
||||
this.modItems[this.lastModId].focus();
|
||||
return;
|
||||
return;
|
||||
} else if (event.currentTarget.className.indexOf('button-inline-menu') >= 0 && event.currentTarget.previousElementSibling == null && this.lastNeId != null && this.modItems[this.lastNeId] != null) {
|
||||
// shift-tab on first element in modifications menu. set focus to last number editor field if open
|
||||
event.preventDefault();
|
||||
@@ -205,15 +209,26 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
*/
|
||||
_renderModifications(props) {
|
||||
const { m, onChange, ship } = props;
|
||||
const modifiableModifications = [];
|
||||
const modifications = [];
|
||||
for (const modName of Modifications.modules[m.grp].modifications) {
|
||||
if (!Modifications.modifications[modName].hidden) {
|
||||
const key = modName + (m.getModValue(modName) / 100 || 0);
|
||||
const editable = modName !== 'fallofffromrange' &&
|
||||
m.blueprint.grades[m.blueprint.grade].features[modName];
|
||||
this.lastNeId = modName;
|
||||
modifications.push(<Modification key={ key } ship={ ship } m={ m } name={ modName } value={ m.getModValue(modName) / 100 || 0 } onChange={ onChange } onKeyDown={ this._keyDown } modItems={ this.modItems } handleModChange = {this._handleModChange} />);
|
||||
(editable ? modifiableModifications : modifications).push(
|
||||
<Modification key={ key } ship={ ship } m={ m }
|
||||
value={m.getPretty(modName) || 0} modItems={this.modItems}
|
||||
onChange={onChange} onKeyDown={this._keyDown} name={modName}
|
||||
editable={editable} handleModChange = {this._handleModChange} />
|
||||
);
|
||||
}
|
||||
}
|
||||
return modifications;
|
||||
|
||||
modifiableModifications.sort(MODIFICATIONS_COMPARATOR);
|
||||
modifications.sort(MODIFICATIONS_COMPARATOR);
|
||||
return modifiableModifications.concat(modifications);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -60,17 +60,20 @@ export function getLanguage(langCode) {
|
||||
},
|
||||
translate,
|
||||
units: {
|
||||
ang: '°', // Angle
|
||||
CR: <u>{translate('CR')}</u>, // Credits
|
||||
kg: <u>{translate('kg')}</u>, // Kilograms
|
||||
kgs: <u>{translate('kg/s')}</u>, // Kilograms per second
|
||||
km: <u>{translate('km')}</u>, // Kilometers
|
||||
Ls: <u>{translate('Ls')}</u>, // Light Seconds
|
||||
LY: <u>{translate('LY')}</u>, // Light Years
|
||||
m: <u>{translate('m')}</u>, // Meters
|
||||
MJ: <u>{translate('MJ')}</u>, // Mega Joules
|
||||
'm/s': <u>{translate('m/s')}</u>, // Meters per second
|
||||
'°/s': <u>{translate('°/s')}</u>, // Degrees per second
|
||||
MW: <u>{translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
|
||||
mps: <u>{translate('m/s')}</u>, // Metres per second
|
||||
pct: '%', // Percent
|
||||
ps: <u>{translate('/s')}</u>, // per second
|
||||
pm: <u>{translate('/min')}</u>, // per minute
|
||||
s: <u>{translate('secs')}</u>, // Seconds
|
||||
|
||||
@@ -178,6 +178,7 @@
|
||||
"dpssdps": "Damage per second (sustained damage per second)",
|
||||
"eps": "Energy per second",
|
||||
"epsseps": "Energy per second (sustained energy per second)",
|
||||
"fallofffromrange": "Falloff",
|
||||
"hps": "Heat per second",
|
||||
"hpsshps": "Heat per second (sustained heat per second)",
|
||||
"damage by": "Damage by",
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
"PHRASE_BLUEPRINT_BEST": "Лучшие основные значения для чертежа",
|
||||
"PHRASE_BLUEPRINT_EXTREME": "Лучшие положительные и худшие отрицательные основные значения для чертежа",
|
||||
"PHRASE_BLUEPRINT_RESET": "Убрать все изменения и чертёж",
|
||||
"PHRASE_SELECT_SPECIAL": "Нажмите чтобы выбрать экспериментальный эффект",
|
||||
"PHRASE_SELECT_SPECIAL": "Нажмите, чтобы выбрать экспериментальный эффект",
|
||||
"PHRASE_NO_SPECIAL": "Без экспериментального эффекта",
|
||||
"PHRASE_SHOPPING_LIST": "Станции что продают эту сборку",
|
||||
"PHRASE_REFIT_SHOPPING_LIST": "Станции что продают необходимые модули",
|
||||
"PHRASE_TOTAL_EFFECTIVE_SHIELD": "Общий урон что может быть нанесён в каждым типе, если используются все щитонакопители",
|
||||
"PHRASE_SHOPPING_LIST": "Станции, что продают эту сборку",
|
||||
"PHRASE_REFIT_SHOPPING_LIST": "Станции, что продают необходимые модули",
|
||||
"PHRASE_TOTAL_EFFECTIVE_SHIELD": "Общий урон, что может быть нанесён в каждым типе, если используются все щитонакопители",
|
||||
"PHRASE_TIME_TO_LOSE_SHIELDS": "Щиты продержатся",
|
||||
"PHRASE_TIME_TO_RECOVER_SHIELDS": "Щиты восстановятся за",
|
||||
"PHRASE_TIME_TO_RECHARGE_SHIELDS": "Щиты будут заряжены за",
|
||||
@@ -43,12 +43,12 @@
|
||||
"PHRASE_TIME_TO_REMOVE_ARMOUR": "Снимет броню за",
|
||||
"TT_TIME_TO_REMOVE_ARMOUR": "Непрерывным огнём из всех орудий",
|
||||
"PHRASE_TIME_TO_DRAIN_WEP": "Опустошит ОРУЖ за",
|
||||
"TT_TIME_TO_DRAIN_WEP": "Время за которое опустошится аккумулятор ОРУЖ при стрельбе из всех орудий",
|
||||
"TT_TIME_TO_DRAIN_WEP": "Время, за которое опустошится аккумулятор ОРУЖ при стрельбе из всех орудий",
|
||||
"TT_TIME_TO_LOSE_SHIELDS": "Против поддерживаемой стрельбы из всех орудий противника",
|
||||
"TT_TIME_TO_LOSE_ARMOUR": "Против поддерживаемой стрельбы из всех орудий противника",
|
||||
"TT_MODULE_ARMOUR": "Броня защищаюшае модули от урона",
|
||||
"TT_MODULE_PROTECTION_EXTERNAL": "Процент урона перенаправленного от гнёзд на наборы для усиления модулей",
|
||||
"TT_MODULE_PROTECTION_INTERNAL": "Процент урона перенаправленного от модулей вне гнёзд на наборы для усиления модулей",
|
||||
"TT_MODULE_ARMOUR": "Броня, защищающая модули от урона",
|
||||
"TT_MODULE_PROTECTION_EXTERNAL": "Процент урона, перенаправленного от гнёзд на наборы для усиления модулей",
|
||||
"TT_MODULE_PROTECTION_INTERNAL": "Процент урона, перенаправленного от модулей вне гнёзд на наборы для усиления модулей",
|
||||
"TT_EFFECTIVE_SDPS_SHIELDS": "Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст",
|
||||
"TT_EFFECTIVENESS_SHIELDS": "Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью без пунктов в СИС на 0 метрах",
|
||||
"TT_EFFECTIVE_SDPS_ARMOUR": "Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст",
|
||||
@@ -56,7 +56,7 @@
|
||||
"PHRASE_EFFECTIVE_SDPS_SHIELDS": "ПДПС против щитов",
|
||||
"PHRASE_EFFECTIVE_SDPS_ARMOUR": "ПДПС против брони",
|
||||
"TT_SUMMARY_SPEED": "С полным топливным баком и 4 пунктами в ДВИ",
|
||||
"TT_SUMMARY_SPEED_NONFUNCTIONAL": "маневровые двигатели выключены или превышена максимальная масса с топливом и грузом",
|
||||
"TT_SUMMARY_SPEED_NONFUNCTIONAL": "Маневровые двигатели выключены или превышена максимальная масса с топливом и грузом",
|
||||
"TT_SUMMARY_BOOST": "С полным топливным баком и 4 пунктами в ДВИ",
|
||||
"TT_SUMMARY_BOOST_NONFUNCTIONAL": "Распределитель питания не может обеспечить достаточно энергии для форсажа",
|
||||
"TT_SUMMARY_SHIELDS": "Чистая сила щита, включая усилители",
|
||||
@@ -68,16 +68,16 @@
|
||||
"TT_SUMMARY_DPS": "Урон в секунду при стрельбе из всех орудий",
|
||||
"TT_SUMMARY_EPS": "Расход аккумулятора ОРУЖ в секунду при стрельбе из всех орудий",
|
||||
"TT_SUMMARY_TTD": "Время расхода аккумулятора ОРУЖ при стрельбе из всех орудий и с 4 пунктами в ОРУЖ",
|
||||
"TT_SUMMARY_MAX_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с топливом достаточным только на сам прыжок",
|
||||
"TT_SUMMARY_MAX_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с топливом, достаточным только на сам прыжок",
|
||||
"TT_SUMMARY_UNLADEN_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с полным топливным баком",
|
||||
"TT_SUMMARY_LADEN_SINGLE_JUMP": "Самый дальний возможный прыжок с полным грузовым отсеком и с полным топливным баком",
|
||||
"TT_SUMMARY_UNLADEN_TOTAL_JUMP": "Самая дальняя общая дистанция без груза, с полным топливным баком и при прыжках на максимальное расстояние",
|
||||
"TT_SUMMARY_LADEN_TOTAL_JUMP": "Самая дальняя общая дистанция с полным грузовым отсеком, с полным топливным баком и при прыжках на максимальное расстояние",
|
||||
"HELP_MODIFICATIONS_MENU": "Ткните на номер чтобы ввести новое значение, или потяните вдоль полосы для малых изменений",
|
||||
"HELP_MODIFICATIONS_MENU": "Нажмите на номер, чтобы ввести новое значение, или потяните вдоль полосы для малых изменений",
|
||||
"am": "Блок Автом. Полевого Ремонта",
|
||||
"bh": "Переборки",
|
||||
"bl": "Пучковый Лазер",
|
||||
"bsg": "Двухпоточный Щитогенератор",
|
||||
"bl": "Пучковый лазер",
|
||||
"bsg": "Двухпоточный щитогенератор",
|
||||
"c": "Орудие",
|
||||
"cc": "Контроллер магнитного снаряда для сбора",
|
||||
"ch": "Разбрасыватель дипольных отражателей",
|
||||
@@ -89,7 +89,7 @@
|
||||
"fh": "Ангар для истребителя",
|
||||
"fi": "FSD-перехватчик",
|
||||
"fs": "Топливозаборник",
|
||||
"fsd": "Рамочно Сместительный двигатель",
|
||||
"fsd": "Рамочно-сместительный двигатель",
|
||||
"ft": "Топливный бак",
|
||||
"fx": "Контроллер магнитного снаряда для топлива",
|
||||
"hb": "Контроллер магнитного снаряда для взлома трюма",
|
||||
@@ -110,7 +110,7 @@
|
||||
"pcm": "Каюта пассажира первого класса",
|
||||
"pcq": "Каюта пассажира класса люкс",
|
||||
"pd": "Распределитель питания",
|
||||
"pl": "Ипмульсный лазер",
|
||||
"pl": "Импульсный лазер",
|
||||
"po": "Точечная оборона",
|
||||
"pp": "Силовая установка",
|
||||
"psg": "Призматический щитогенератор",
|
||||
@@ -122,7 +122,7 @@
|
||||
"sc": "Сканер обнаружения",
|
||||
"scb": "Щитонакопитель",
|
||||
"sg": "Щитогенератор",
|
||||
"ss": "Сканер Поверхностей",
|
||||
"ss": "Сканер поверхностей",
|
||||
"t": "Маневровые двигатели",
|
||||
"tp": "Торпедная стойка",
|
||||
"ul": "Пульсирующие лазеры",
|
||||
@@ -130,7 +130,7 @@
|
||||
"emptyrestricted": "пусто (ограниченно)",
|
||||
"damage dealt to": "Урон нанесён",
|
||||
"damage received from": "Урон получен от",
|
||||
"against shields": "Против шитов",
|
||||
"against shields": "Против щитов",
|
||||
"against hull": "Против корпуса",
|
||||
"total effective shield": "Общие эффективные щиты",
|
||||
"ammunition": "Припасы",
|
||||
@@ -149,7 +149,7 @@
|
||||
"eps": "Энергия в секунду",
|
||||
"epsseps": "Энергия в секунду (поддерживаемая энергия в секунду)",
|
||||
"hps": "Нагрев в секунду",
|
||||
"hpsshps": "Heat per second (sustained heat per second)",
|
||||
"hpsshps": "Нагрев в секунду (поддерживаемый нагрев в секунду)",
|
||||
"damage by": "Урон",
|
||||
"damage from": "Урон от",
|
||||
"shield cells": "Щитонакопители",
|
||||
@@ -183,7 +183,7 @@
|
||||
"hullreinforcement": "Укрепление корпуса",
|
||||
"integrity": "Целостность",
|
||||
"jitter": "Дрожание",
|
||||
"kinres": "Сопротивление китетическому урону",
|
||||
"kinres": "Сопротивление кинетическому урону",
|
||||
"maxfuel": "Макс. топлива на прыжок",
|
||||
"mass": "Масса",
|
||||
"optmass": "Оптимизированная масса",
|
||||
@@ -381,4 +381,4 @@
|
||||
"URL": "Ссылка",
|
||||
"WEP": "ОРУЖ",
|
||||
"yes": "Да"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,7 +544,7 @@ export function calcBoost(ship) {
|
||||
if (!ship.boostEnergy || !ship.standard[4] || !ship.standard[4].m) {
|
||||
return undefined;
|
||||
}
|
||||
return ship.boostEnergy / ship.standard[4].m.engrate;
|
||||
return ship.boostEnergy / ship.standard[4].m.getEnginesRechargeRate();
|
||||
}
|
||||
|
||||
|
||||
@@ -585,7 +585,7 @@ export function armourMetrics(ship) {
|
||||
hullThermDmg = hullThermDmg * (1 - slot.m.getThermalResistance());
|
||||
hullCausDmg = hullCausDmg * (1 - slot.m.getCausticResistance());
|
||||
}
|
||||
if (slot.m && slot.m.grp == 'mrp') {
|
||||
if (slot.m && (slot.m.grp == 'mrp' || slot.m.grp == 'gmrp')) {
|
||||
moduleArmour += slot.m.getIntegrity();
|
||||
moduleProtection = moduleProtection * (1 - slot.m.getProtection());
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ import LZString from 'lz-string';
|
||||
import * as _ from 'lodash';
|
||||
import isEqual from 'lodash/lang';
|
||||
import { Ships, Modifications } from 'coriolis-data/dist';
|
||||
import { chain } from 'lodash';
|
||||
const zlib = require('zlib');
|
||||
|
||||
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh', 'gfsb'];
|
||||
@@ -493,68 +494,62 @@ export default class Ship {
|
||||
* @param {Object} name The name of the modification to change
|
||||
* @param {Number} value The new value of the modification. The value of the modification is scaled to provide two decimal places of precision in an integer. For example 1.23% is stored as 123
|
||||
* @param {bool} sentfromui True if this update was sent from the UI
|
||||
* @param {bool} isAbsolute True if value is an absolute value and not a
|
||||
* modification value
|
||||
*/
|
||||
setModification(m, name, value, sentfromui) {
|
||||
setModification(m, name, value, sentfromui, isAbsolute) {
|
||||
if (isNaN(value)) {
|
||||
// Value passed is invalid; reset it to 0
|
||||
value = 0;
|
||||
}
|
||||
|
||||
if (isAbsolute) {
|
||||
m.setPretty(name, value, sentfromui);
|
||||
} else {
|
||||
m.setModValue(name, value, sentfromui);
|
||||
}
|
||||
|
||||
// Handle special cases
|
||||
if (name === 'pgen') {
|
||||
// Power generation
|
||||
m.setModValue(name, value, sentfromui);
|
||||
this.updatePowerGenerated();
|
||||
} else if (name === 'power') {
|
||||
// Power usage
|
||||
m.setModValue(name, value, sentfromui);
|
||||
this.updatePowerUsed();
|
||||
} else if (name === 'mass') {
|
||||
// Mass
|
||||
m.setModValue(name, value, sentfromui);
|
||||
this.recalculateMass();
|
||||
this.updateMovement();
|
||||
this.updateJumpStats();
|
||||
} else if (name === 'maxfuel') {
|
||||
m.setModValue(name, value, sentfromui);
|
||||
this.updateJumpStats();
|
||||
} else if (name === 'optmass') {
|
||||
m.setModValue(name, value, sentfromui);
|
||||
// Could be for any of thrusters, FSD or shield
|
||||
this.updateMovement();
|
||||
this.updateJumpStats();
|
||||
this.recalculateShield();
|
||||
} else if (name === 'optmul') {
|
||||
m.setModValue(name, value, sentfromui);
|
||||
// Could be for any of thrusters, FSD or shield
|
||||
this.updateMovement();
|
||||
this.updateJumpStats();
|
||||
this.recalculateShield();
|
||||
} else if (name === 'shieldboost') {
|
||||
m.setModValue(name, value, sentfromui);
|
||||
this.recalculateShield();
|
||||
} else if (name === 'hullboost' || name === 'hullreinforcement' || name === 'modulereinforcement') {
|
||||
m.setModValue(name, value, sentfromui);
|
||||
this.recalculateArmour();
|
||||
} else if (name === 'shieldreinforcement') {
|
||||
m.setModValue(name, value, sentfromui);
|
||||
this.recalculateShieldCells();
|
||||
} else if (name === 'burst' || name == 'burstrof' || name === 'clip' || name === 'damage' || name === 'distdraw' || name === 'jitter' || name === 'piercing' || name === 'range' || name === 'reload' || name === 'rof' || name === 'thermload') {
|
||||
m.setModValue(name, value, sentfromui);
|
||||
this.recalculateDps();
|
||||
this.recalculateHps();
|
||||
this.recalculateEps();
|
||||
} else if (name === 'explres' || name === 'kinres' || name === 'thermres') {
|
||||
m.setModValue(name, value, sentfromui);
|
||||
// Could be for shields or armour
|
||||
this.recalculateArmour();
|
||||
this.recalculateShield();
|
||||
} else if (name === 'engcap') {
|
||||
m.setModValue(name, value, sentfromui);
|
||||
// Might have resulted in a change in boostability
|
||||
this.updateMovement();
|
||||
} else {
|
||||
// Generic
|
||||
m.setModValue(name, value, sentfromui);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -937,7 +932,12 @@ export default class Ship {
|
||||
let epsChanged = n && n.getEps() || old && old.getEps();
|
||||
let hpsChanged = n && n.getHps() || old && old.getHps();
|
||||
|
||||
let armourChange = (slot === this.bulkheads) || (n && n.grp === 'hr') || (n && n.grp === 'ghrp') || (old && old.grp === 'hr') || (old && old.grp === 'ghrp') || (n && n.grp === 'mrp') || (old && old.grp === 'mrp') || (n && n.grp == 'mahr') || (old && old.grp == 'mahr');
|
||||
let armourChange = (slot === this.bulkheads) ||
|
||||
(n && n.grp === 'hr') || (old && old.grp === 'hr') ||
|
||||
(n && n.grp === 'ghrp') || (old && old.grp === 'ghrp') ||
|
||||
(n && n.grp == 'mahr') || (old && old.grp == 'mahr') ||
|
||||
(n && n.grp === 'mrp') || (old && old.grp === 'mrp') ||
|
||||
(n && n.grp === 'gmrp') || (old && old.grp == 'gmrp');
|
||||
|
||||
let shieldChange = (n && n.grp === 'bsg') || (old && old.grp === 'bsg') || (n && n.grp === 'psg') || (old && old.grp === 'psg') || (n && n.grp === 'sg') || (old && old.grp === 'sg') || (n && n.grp === 'sb') || (old && old.grp === 'sb') || (old && old.grp === 'gsrp') || (n && n.grp === 'gsrp');
|
||||
|
||||
@@ -1182,38 +1182,35 @@ export default class Ship {
|
||||
|
||||
unladenMass += this.bulkheads.m.getMass();
|
||||
|
||||
for (let slotNum in this.standard) {
|
||||
const slot = this.standard[slotNum];
|
||||
if (slot.m) {
|
||||
unladenMass += slot.m.getMass();
|
||||
if (slot.m.grp === 'ft') {
|
||||
fuelCapacity += slot.m.fuel;
|
||||
}
|
||||
}
|
||||
}
|
||||
let slots = this.standard.concat(this.internal, this.hardpoints);
|
||||
// TODO: create class for slot and also add slot.get
|
||||
// handle unladen mass
|
||||
unladenMass += chain(slots)
|
||||
.map(slot => slot.m ? slot.m.get('mass') : null)
|
||||
.filter()
|
||||
.reduce((sum, mass) => sum + mass)
|
||||
.value();
|
||||
|
||||
for (let slotNum in this.internal) {
|
||||
const slot = this.internal[slotNum];
|
||||
if (slot.m) {
|
||||
unladenMass += slot.m.getMass();
|
||||
if (slot.m.grp === 'ft') {
|
||||
fuelCapacity += slot.m.fuel;
|
||||
} else if (slot.m.grp === 'cr') {
|
||||
cargoCapacity += slot.m.cargo;
|
||||
} else if (slot.m.grp.slice(0,2) === 'pc') {
|
||||
if (slot.m.passengers) {
|
||||
passengerCapacity += slot.m.passengers;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// handle fuel capacity
|
||||
fuelCapacity += chain(slots)
|
||||
.map(slot => slot.m ? slot.m.get('fuel') : null)
|
||||
.filter()
|
||||
.reduce((sum, fuel) => sum + fuel)
|
||||
.value();
|
||||
|
||||
for (let slotNum in this.hardpoints) {
|
||||
const slot = this.hardpoints[slotNum];
|
||||
if (slot.m) {
|
||||
unladenMass += slot.m.getMass();
|
||||
}
|
||||
}
|
||||
// handle cargo capacity
|
||||
cargoCapacity += chain(slots)
|
||||
.map(slot => slot.m ? slot.m.get('cargo') : null)
|
||||
.filter()
|
||||
.reduce((sum, cargo) => sum + cargo)
|
||||
.value();
|
||||
|
||||
// handle passenger capacity
|
||||
passengerCapacity += chain(slots)
|
||||
.map(slot => slot.m ? slot.m.get('passengers') : null)
|
||||
.filter()
|
||||
.reduce((sum, passengers) => sum + passengers)
|
||||
.value();
|
||||
|
||||
// Update global stats
|
||||
this.unladenMass = unladenMass;
|
||||
|
||||
82
src/app/shipyard/StatsFormatting.js
Normal file
82
src/app/shipyard/StatsFormatting.js
Normal file
@@ -0,0 +1,82 @@
|
||||
export const SI_PREFIXES = {
|
||||
'Y': 1e+24, // Yotta
|
||||
'Z': 1e+21, // Zetta
|
||||
'E': 1e+18, // Peta
|
||||
'P': 1e+15, // Peta
|
||||
'T': 1e+12, // Tera
|
||||
'G': 1e+9, // Giga
|
||||
'M': 1e+6, // Mega
|
||||
'k': 1e+3, // Kilo
|
||||
'h': 1e+2, // Hekto
|
||||
'da': 1e+1, // Deka
|
||||
'': 1,
|
||||
'd': 1e-1, // Dezi
|
||||
'c': 1e-2, // Zenti
|
||||
'm': 1e-3, // Milli
|
||||
'μ': 1e-6, // mikro not supported due to charset
|
||||
'n': 10e-9, // Nano
|
||||
'p': 1e-12, // Nano
|
||||
'f': 1e-15, // Femto
|
||||
'a': 1e-18, // Atto
|
||||
'z': 1e-21, // Zepto
|
||||
'y': 1e-24 // Yokto
|
||||
};
|
||||
|
||||
export const STATS_FORMATTING = {
|
||||
'ammo': { 'format': 'int', },
|
||||
'boot': { 'format': 'int', 'unit': 'secs' },
|
||||
'brokenregen': { 'format': 'round1', 'unit': 'ps' },
|
||||
'burst': { 'format': 'int' },
|
||||
'burstrof': { 'format': 'round1', 'unit': 'ps' },
|
||||
'causres': { 'format': 'pct' },
|
||||
'clip': { 'format': 'int' },
|
||||
'damage': { 'format': 'round' },
|
||||
'dps': { 'format': 'round', 'units': 'ps', 'synthetic': 'getDps' },
|
||||
'dpe': { 'format': 'round', 'units': 'ps', 'synthetic': 'getDpe' },
|
||||
'distdraw': { 'format': 'round', 'unit': 'MW' },
|
||||
'duration': { 'format': 'round1', 'unit': 's' },
|
||||
'eff': { 'format': 'round2' },
|
||||
'engcap': { 'format': 'round1', 'unit': 'MJ' },
|
||||
'engrate': { 'format': 'round1', 'unit': 'MW' },
|
||||
'eps': { 'format': 'round', 'units': 'ps', 'synthetic': 'getEps' },
|
||||
'explres': { 'format': 'pct' },
|
||||
'facinglimit': { 'format': 'round1', 'unit': 'ang' },
|
||||
'falloff': { 'format': 'round', 'unit': 'km', 'storedUnit': 'm' },
|
||||
'fallofffromrange': { 'format': 'round', 'unit': 'km', 'storedUnit': 'm', 'synthetic': 'getFalloff' },
|
||||
'hps': { 'format': 'round', 'units': 'ps', 'synthetic': 'getHps' },
|
||||
'hullboost': { 'format': 'pct1', 'change': 'additive' },
|
||||
'hullreinforcement': { 'format': 'int' },
|
||||
'integrity': { 'format': 'round1' },
|
||||
'jitter': { 'format': 'round', 'unit': 'ang' },
|
||||
'kinres': { 'format': 'pct' },
|
||||
'mass': { 'format': 'round1', 'unit': 'T' },
|
||||
'maxfuel': { 'format': 'round1', 'unit': 'T' },
|
||||
'optmass': { 'format': 'int', 'unit': 'T' },
|
||||
'optmul': { 'format': 'pct', 'change': 'additive' },
|
||||
'pgen': { 'format': 'round1', 'unit': 'MW' },
|
||||
'piercing': { 'format': 'int' },
|
||||
'power': { 'format': 'round', 'unit': 'MW' },
|
||||
'protection': { 'format': 'pct' },
|
||||
'range': { 'format': 'f2', 'unit': 'km', 'storedUnit': 'm' },
|
||||
'ranget': { 'format': 'f1', 'unit': 's' },
|
||||
'regen': { 'format': 'round1', 'unit': 'ps' },
|
||||
'reload': { 'format': 'int', 'unit': 's' },
|
||||
'rof': { 'format': 'round1', 'unit': 'ps' },
|
||||
'angle': { 'format': 'round1', 'unit': 'ang' },
|
||||
'scanrate': { 'format': 'int' },
|
||||
'scantime': { 'format': 'round1', 'unit': 's' },
|
||||
'sdps': { 'format': 'round1', 'units': 'ps', 'synthetic': 'getSDps' },
|
||||
'shield': { 'format': 'int', 'unit': 'MJ' },
|
||||
'shieldaddition': { 'format': 'round1', 'unit': 'MJ' },
|
||||
'shieldboost': { 'format': 'pct1', 'change': 'additive' },
|
||||
'shieldreinforcement': { 'format': 'round1', 'unit': 'MJ' },
|
||||
'shotspeed': { 'format': 'int', 'unit': 'm/s' },
|
||||
'spinup': { 'format': 'round1', 'unit': 's' },
|
||||
'syscap': { 'format': 'round1', 'unit': 'MJ' },
|
||||
'sysrate': { 'format': 'round1', 'unit': 'MW' },
|
||||
'thermload': { 'format': 'round1' },
|
||||
'thermres': { 'format': 'pct' },
|
||||
'wepcap': { 'format': 'round1', 'unit': 'MJ' },
|
||||
'weprate': { 'format': 'round1', 'unit': 'MW' },
|
||||
'jumpboost': { 'format': 'round1', 'unit': 'LY' }
|
||||
};
|
||||
@@ -281,7 +281,7 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat
|
||||
if (modifiers[i].Label.search('Resistance') >= 0) {
|
||||
value = (modifiers[i].Value * 100) - (modifiers[i].OriginalValue * 100);
|
||||
}
|
||||
if (modifiers[i].Label.search('ShieldMultiplier') >= 0) {
|
||||
if (modifiers[i].Label.search('ShieldMultiplier') >= 0 || modifiers[i].Label.search('DefenceModifierHealthMultiplier') >= 0) {
|
||||
value = ((100 + modifiers[i].Value) / (100 + modifiers[i].OriginalValue) * 100 - 100) * 100;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,35 @@
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.modification-container {
|
||||
@input-container-width: 75%;
|
||||
td {
|
||||
width: 100% - @input-container-width;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
width: @input-container-width;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.unit-container {
|
||||
width: 30px;
|
||||
padding: 3px;
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.header-adjuster {
|
||||
width: 100% - @input-container-width;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.cb {
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -45,6 +74,11 @@
|
||||
border-color:#fff;
|
||||
}
|
||||
|
||||
input:disabled {
|
||||
border-color: #888;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.l {
|
||||
text-transform: capitalize;
|
||||
margin-right: 0.8em;
|
||||
|
||||
Reference in New Issue
Block a user