mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-10 07:05:35 +00:00
First steps towards ed-forge rewrite
This commit is contained in:
committed by
felixlinker
parent
cee4c32551
commit
9797a8d781
@@ -128,6 +128,7 @@
|
||||
"coriolis-data": "../coriolis-data",
|
||||
"d3": "^5.7.0",
|
||||
"detect-browser": "^3.0.1",
|
||||
"ed-forge": "github:EDCD/ed-forge",
|
||||
"fbemitter": "^2.1.1",
|
||||
"lodash": "^4.17.11",
|
||||
"lz-string": "^1.4.4",
|
||||
|
||||
@@ -5,16 +5,14 @@ import { register } from 'register-service-worker';
|
||||
import { EventEmitter } from 'fbemitter';
|
||||
import { getLanguage } from './i18n/Language';
|
||||
import Persist from './stores/Persist';
|
||||
import { Ship } from 'ed-forge';
|
||||
|
||||
import Announcement from './components/Announcement';
|
||||
import Header from './components/Header';
|
||||
import Tooltip from './components/Tooltip';
|
||||
import ModalExport from './components/ModalExport';
|
||||
import ModalHelp from './components/ModalHelp';
|
||||
import ModalImport from './components/ModalImport';
|
||||
import ModalPermalink from './components/ModalPermalink';
|
||||
import * as CompanionApiUtils from './utils/CompanionApiUtils';
|
||||
import * as JournalUtils from './utils/JournalUtils';
|
||||
import AboutPage from './pages/AboutPage';
|
||||
import NotFoundPage from './pages/NotFoundPage';
|
||||
import OutfittingPage from './pages/OutfittingPage';
|
||||
@@ -22,7 +20,6 @@ import ComparisonPage from './pages/ComparisonPage';
|
||||
import ShipyardPage from './pages/ShipyardPage';
|
||||
import ErrorDetails from './pages/ErrorDetails';
|
||||
|
||||
const zlib = require('pako');
|
||||
const request = require('superagent');
|
||||
|
||||
/**
|
||||
@@ -61,7 +58,6 @@ export default class Coriolis extends React.Component {
|
||||
this._onLanguageChange = this._onLanguageChange.bind(this);
|
||||
this._onSizeRatioChange = this._onSizeRatioChange.bind(this);
|
||||
this._keyDown = this._keyDown.bind(this);
|
||||
this._importBuild = this._importBuild.bind(this);
|
||||
|
||||
this.emitter = new EventEmitter();
|
||||
this.state = {
|
||||
@@ -93,19 +89,10 @@ export default class Coriolis extends React.Component {
|
||||
_importBuild(r) {
|
||||
try {
|
||||
// Need to decode and gunzip the data, then build the ship
|
||||
const data = zlib.inflate(new Buffer(r.params.data, 'base64'), { to: 'string' });
|
||||
const json = JSON.parse(data);
|
||||
console.info('Ship import data: ');
|
||||
console.info(json);
|
||||
let ship;
|
||||
if (json && json.modules) {
|
||||
ship = CompanionApiUtils.shipFromJson(json);
|
||||
} else if (json && json.Modules) {
|
||||
ship = JournalUtils.shipFromLoadoutJSON(json);
|
||||
}
|
||||
r.params.ship = ship.id;
|
||||
r.params.code = ship.toString();
|
||||
this._setPage(OutfittingPage, r)
|
||||
let ship = new Ship(r.params.data);
|
||||
r.params.ship = ship.getShipType();
|
||||
r.params.code = ship.compress();
|
||||
this._setPage(OutfittingPage, r);
|
||||
} catch (err) {
|
||||
this._onError('Failed to import ship', r.path, 0, 0, err);
|
||||
}
|
||||
|
||||
@@ -122,7 +122,6 @@ const CATEGORIES = {
|
||||
*/
|
||||
export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
static propTypes = {
|
||||
modules: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired,
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
diffDetails: PropTypes.func,
|
||||
m: PropTypes.object,
|
||||
@@ -155,7 +154,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
*/
|
||||
_initState(props, context) {
|
||||
let translate = context.language.translate;
|
||||
let { m, warning, onSelect, modules, ship } = props;
|
||||
let { m, warning, onSelect, ship } = props;
|
||||
let list, currentGroup;
|
||||
|
||||
let buildGroup = this._buildGroup.bind(
|
||||
@@ -170,6 +169,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
}
|
||||
);
|
||||
let fuzzy = [];
|
||||
let modules = m.getApplicableItems();
|
||||
if (modules instanceof Array) {
|
||||
list = buildGroup(modules[0].grp, modules);
|
||||
} else {
|
||||
|
||||
@@ -21,7 +21,6 @@ import { blueprintTooltip } from '../utils/BlueprintFunctions';
|
||||
* Hardpoint / Utility Slot
|
||||
*/
|
||||
export default class HardpointSlot extends Slot {
|
||||
|
||||
/**
|
||||
* Get the CSS class name for the slot.
|
||||
* @return {string} CSS Class name
|
||||
@@ -42,40 +41,38 @@ export default class HardpointSlot extends Slot {
|
||||
/**
|
||||
* Generate the slot contents
|
||||
* @param {Object} m Mounted Module
|
||||
* @param {Boolean} enabled Slot enabled
|
||||
* @param {Function} translate Translate function
|
||||
* @param {Object} formats Localized Formats map
|
||||
* @param {Object} u Localized Units Map
|
||||
* @return {React.Component} Slot contents
|
||||
*/
|
||||
_getSlotDetails(m, enabled, translate, formats, u) {
|
||||
_getSlotDetails(m, translate, formats, u) {
|
||||
if (m) {
|
||||
let classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
|
||||
let { drag, drop } = this.props;
|
||||
let { termtip, tooltip } = this.context;
|
||||
let validMods = Modifications.modules[m.grp].modifications || [];
|
||||
let showModuleResistances = Persist.showModuleResistances();
|
||||
|
||||
// Modifications tooltip shows blueprint and grade, if available
|
||||
let modTT = translate('modified');
|
||||
if (m && m.blueprint && m.blueprint.name) {
|
||||
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||
if (m.blueprint.special && m.blueprint.special.id >= 0) {
|
||||
modTT += ', ' + translate(m.blueprint.special.name);
|
||||
}
|
||||
modTT = (
|
||||
<div>
|
||||
<div>{modTT}</div>
|
||||
{blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], null, m.grp, m)}
|
||||
</div>
|
||||
);
|
||||
// modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||
// if (m.blueprint.special && m.blueprint.special.id >= 0) {
|
||||
// modTT += ', ' + translate(m.blueprint.special.name);
|
||||
// }
|
||||
// modTT = (
|
||||
// <div>
|
||||
// <div>{modTT}</div>
|
||||
// {blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], m)}
|
||||
// </div>
|
||||
// );
|
||||
}
|
||||
|
||||
const className = cn('details', enabled ? '' : 'disabled');
|
||||
const className = cn('details', m.isEnabled() ? '' : 'disabled');
|
||||
return <div className={className} draggable='true' onDragStart={drag} onDragEnd={drop}>
|
||||
<div className={'cb'}>
|
||||
<div className={'l'}>
|
||||
{m.mount && m.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')}
|
||||
{/* {m.mount && m.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')}
|
||||
onMouseOut={tooltip.bind(null, null)}><MountFixed/></span> : ''}
|
||||
{m.mount && m.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')}
|
||||
onMouseOut={tooltip.bind(null, null)}><MountGimballed/></span> : ''}
|
||||
@@ -91,13 +88,13 @@ export default class HardpointSlot extends Slot {
|
||||
onMouseOut={tooltip.bind(null, null)}><DamageAbsolute/></span> : ''}
|
||||
{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span className='r'
|
||||
onMouseOver={termtip.bind(null, modTT)}
|
||||
onMouseOut={tooltip.bind(null, null)}><Modified/></span> : null}
|
||||
onMouseOut={tooltip.bind(null, null)}><Modified/></span> : null} */}
|
||||
</div>
|
||||
|
||||
<div className={'r'}>{formats.round(m.getMass())}{u.T}</div>
|
||||
<div className={'r'}>{formats.round(m.get('mass'))}{u.T}</div>
|
||||
</div>
|
||||
<div className={'cb'}>
|
||||
{m.getDps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'dpssdps' : 'dps')}
|
||||
{/* {m.getDps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'dpssdps' : 'dps')}
|
||||
onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}: {formats.round1(m.getDps())} {m.getClip() ?
|
||||
<span>({formats.round1(m.getSDps())})</span> : null}</div> : null}
|
||||
{m.getDamage() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getDamage() ? 'shotdmg' : 'shotdmg')}
|
||||
@@ -140,12 +137,11 @@ export default class HardpointSlot extends Slot {
|
||||
<button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation}
|
||||
onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}>
|
||||
<ListModifications/></button>
|
||||
</div> : null}
|
||||
</div> : null} */}
|
||||
</div>
|
||||
</div>;
|
||||
} else {
|
||||
return <div className={'empty'}>{translate('empty')}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -68,16 +68,11 @@ export default class HardpointSlotSection extends SlotSection {
|
||||
let { ship, currentMenu } = this.props;
|
||||
let { originSlot, targetSlot } = this.state;
|
||||
let slots = [];
|
||||
let hardpoints = ship.hardpoints;
|
||||
let availableModules = ship.getAvailableModules();
|
||||
|
||||
for (let i = 0, l = hardpoints.length; i < l; i++) {
|
||||
let h = hardpoints[i];
|
||||
if (h.maxClass) {
|
||||
for (let h of ship.getHardpoints(undefined, true)) {
|
||||
slots.push(<HardpointSlot
|
||||
key={i}
|
||||
maxClass={h.maxClass}
|
||||
availableModules={() => availableModules.getHps(h.maxClass)}
|
||||
key={h.object.Slot}
|
||||
maxClass={h.getSize()}
|
||||
onOpen={this._openMenu.bind(this, h)}
|
||||
onSelect={this._selectModule.bind(this, h)}
|
||||
onChange={this.props.onChange}
|
||||
@@ -87,11 +82,10 @@ export default class HardpointSlotSection extends SlotSection {
|
||||
drop={this._drop}
|
||||
dropClass={this._dropClass(h, originSlot, targetSlot)}
|
||||
ship={ship}
|
||||
m={h.m}
|
||||
slot={h}
|
||||
enabled={h.enabled ? true : false}
|
||||
/>);
|
||||
}
|
||||
}
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
@@ -15,36 +15,39 @@ export default class InternalSlot extends Slot {
|
||||
/**
|
||||
* Generate the slot contents
|
||||
* @param {Object} m Mounted Module
|
||||
* @param {Boolean} enabled Slot enabled
|
||||
* @param {Function} translate Translate function
|
||||
* @param {Object} formats Localized Formats map
|
||||
* @param {Object} u Localized Units Map
|
||||
* @return {React.Component} Slot contents
|
||||
*/
|
||||
_getSlotDetails(m, enabled, translate, formats, u) {
|
||||
_getSlotDetails(m, translate, formats, u) {
|
||||
if (m) {
|
||||
let classRating = m.class + m.rating;
|
||||
let classRating = String(m.getSize()) + m.getRating();
|
||||
let { drag, drop, ship } = this.props;
|
||||
let { termtip, tooltip } = this.context;
|
||||
let validMods = (Modifications.modules[m.grp] ? Modifications.modules[m.grp].modifications : []);
|
||||
let validMods = m.getApplicableBlueprints();
|
||||
let showModuleResistances = Persist.showModuleResistances();
|
||||
|
||||
// Modifications tooltip shows blueprint and grade, if available
|
||||
let modTT = translate('modified');
|
||||
if (m && m.blueprint && m.blueprint.name) {
|
||||
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||
if (m.blueprint.special && m.blueprint.special.id >= 0) {
|
||||
modTT += ', ' + translate(m.blueprint.special.name);
|
||||
}
|
||||
modTT = (
|
||||
<div>
|
||||
<div>{modTT}</div>
|
||||
{blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], null, m.grp, m)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// let modTT = translate('modified');
|
||||
// const blueprint = m.getBlueprint();
|
||||
// const experimental = m.getExperimental();
|
||||
// const grade = m.getGrade();
|
||||
// if (blueprint) {
|
||||
// modTT = translate(blueprint) + ' ' + translate('grade') + ' ' + grade;
|
||||
// if (experimental) {
|
||||
// modTT += ', ' + translate(experimental);
|
||||
// }
|
||||
// modTT = (
|
||||
// <div>
|
||||
// <div>{modTT}</div>
|
||||
// {blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], m)}
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
let mass = m.getMass() || m.cargo || m.fuel || 0;
|
||||
let mass = m.get('mass') || m.get('cargo') || m.get('fuel') || 0;
|
||||
const enabled = m.isEnabled();
|
||||
|
||||
const className = cn('details', enabled ? '' : 'disabled');
|
||||
return <div className={className} draggable='true' onDragStart={drag} onDragEnd={drop}>
|
||||
@@ -53,7 +56,7 @@ export default class InternalSlot extends Slot {
|
||||
<div className={'r'}>{formats.round(mass)}{u.T}</div>
|
||||
</div>
|
||||
<div className={'cb'}>
|
||||
{ m.getOptMass() ? <div className={'l'}>{translate('optmass', 'sg')}: {formats.int(m.getOptMass())}{u.T}</div> : null }
|
||||
{/* { m.getOptMass() ? <div className={'l'}>{translate('optmass', 'sg')}: {formats.int(m.getOptMass())}{u.T}</div> : null }
|
||||
{ m.getMaxMass() ? <div className={'l'}>{translate('maxmass', 'sg')}: {formats.int(m.getMaxMass())}{u.T}</div> : null }
|
||||
{ m.bins ? <div className={'l'}>{m.bins} <u>{translate('bins')}</u></div> : null }
|
||||
{ m.bays ? <div className={'l'}>{translate('bays')}: {m.bays}</div> : null }
|
||||
@@ -88,7 +91,7 @@ export default class InternalSlot extends Slot {
|
||||
{ m.getHullReinforcement() ? <div className='l'>{translate('armour')}: {formats.int(m.getHullReinforcement() + ship.baseArmour * m.getModValue('hullboost') / 10000)}</div> : null }
|
||||
{ m.getProtection() ? <div className='l'>{translate('protection')}: {formats.rPct(m.getProtection())}</div> : null }
|
||||
{ m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null }
|
||||
{ m && validMods.length > 0 ? <div className='r' tabIndex="0" ref={ modButton => this.modButton = modButton }><button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
|
||||
{ m && validMods.length > 0 ? <div className='r' tabIndex="0" ref={ modButton => this.modButton = modButton }><button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null } */}
|
||||
</div>
|
||||
</div>;
|
||||
} else {
|
||||
|
||||
@@ -9,7 +9,6 @@ import { canMount } from '../utils/SlotFunctions';
|
||||
* Internal slot section
|
||||
*/
|
||||
export default class InternalSlotSection extends SlotSection {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
@@ -221,29 +220,24 @@ export default class InternalSlotSection extends SlotSection {
|
||||
let slots = [];
|
||||
let { currentMenu, ship } = this.props;
|
||||
let { originSlot, targetSlot } = this.state;
|
||||
let { internal, fuelCapacity } = ship;
|
||||
let availableModules = ship.getAvailableModules();
|
||||
|
||||
for (let i = 0, l = internal.length; i < l; i++) {
|
||||
let s = internal[i];
|
||||
let { fuelCapacity } = ship;
|
||||
|
||||
for (const slot of ship.getInternals(undefined, true)) {
|
||||
slots.push(<InternalSlot
|
||||
key={i}
|
||||
maxClass={s.maxClass}
|
||||
availableModules={() => availableModules.getInts(ship, s.maxClass, s.eligible)}
|
||||
onOpen={this._openMenu.bind(this,s)}
|
||||
key={slot.object.Slot}
|
||||
maxClass={slot.getSize()}
|
||||
onOpen={this._openMenu.bind(this, slot)}
|
||||
onChange={this.props.onChange}
|
||||
onSelect={this._selectModule.bind(this, s)}
|
||||
selected={currentMenu == s}
|
||||
eligible={s.eligible}
|
||||
m={s.m}
|
||||
drag={this._drag.bind(this, s)}
|
||||
dragOver={this._dragOverSlot.bind(this, s)}
|
||||
onSelect={this._selectModule.bind(this, slot)}
|
||||
selected={currentMenu == slot}
|
||||
slot={slot}
|
||||
drag={this._drag.bind(this, slot)}
|
||||
dragOver={this._dragOverSlot.bind(this, slot)}
|
||||
drop={this._drop}
|
||||
dropClass={this._dropClass(s, originSlot, targetSlot)}
|
||||
dropClass={this._dropClass(slot, originSlot, targetSlot)}
|
||||
fuel={fuelCapacity}
|
||||
ship={ship}
|
||||
enabled={s.enabled ? true : false}
|
||||
enabled={slot.isEnabled()}
|
||||
/>);
|
||||
}
|
||||
|
||||
@@ -273,5 +267,4 @@ export default class InternalSlotSection extends SlotSection {
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
});
|
||||
const close = this._blueprintSelected.bind(this, blueprintName, grade);
|
||||
const key = blueprintName + ':' + grade;
|
||||
const tooltipContent = blueprintTooltip(translate, blueprint.grades[grade], Modifications.modules[m.grp].blueprints[blueprintName].grades[grade].engineers, m.grp);
|
||||
const tooltipContent = blueprintTooltip(translate, blueprint.grades[grade]);
|
||||
if (classes.indexOf('active') >= 0) this.selectedModId = key;
|
||||
blueprintGrades.unshift(<li key={key} tabIndex="0" data-id={key} className={classes} style={{ width: '2em' }} onMouseOver={termtip.bind(null, tooltipContent)} onMouseOut={tooltip.bind(null, null)} onClick={close} onKeyDown={this._keyDown} ref={modItem => this.modItems[key] = modItem}>{grade}</li>);
|
||||
}
|
||||
@@ -429,7 +429,7 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
if (m.blueprint && m.blueprint.name && Modifications.modules[m.grp].blueprints[m.blueprint.fdname].grades[m.blueprint.grade]) {
|
||||
blueprintLabel = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||
haveBlueprint = true;
|
||||
blueprintTt = blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], Modifications.modules[m.grp].blueprints[m.blueprint.fdname].grades[m.blueprint.grade].engineers, m.grp);
|
||||
blueprintTt = blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade]);
|
||||
blueprintCv = getPercent(m);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,12 @@ import cn from 'classnames';
|
||||
import { Warning } from './SvgIcons';
|
||||
import * as Calc from '../shipyard/Calculations';
|
||||
|
||||
import { ShipProps } from 'ed-forge';
|
||||
const {
|
||||
SPEED, JUMP_RANGE, TOTAL_RANGE, SHIELD_METRICS, ARMOUR_METRICS, MODULE_ARMOUR,
|
||||
MODULE_PROTECTION
|
||||
} = ShipProps;
|
||||
|
||||
/**
|
||||
* Ship Summary Table / Stats
|
||||
*/
|
||||
@@ -12,10 +18,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
|
||||
static propTypes = {
|
||||
ship: PropTypes.object.isRequired,
|
||||
cargo: PropTypes.number.isRequired,
|
||||
fuel: PropTypes.number.isRequired,
|
||||
marker: PropTypes.string.isRequired,
|
||||
pips: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -35,14 +38,14 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
* @return {React.Component} Summary table
|
||||
*/
|
||||
render() {
|
||||
const { ship, cargo, fuel, pips } = this.props;
|
||||
const { ship } = this.props;
|
||||
let { language, tooltip, termtip } = this.context;
|
||||
let translate = language.translate;
|
||||
let u = language.units;
|
||||
let formats = language.formats;
|
||||
let { time, int, round, f1, f2 } = formats;
|
||||
let hide = tooltip.bind(null, null);
|
||||
const shieldGenerator = ship.findInternalByGroup('sg') || ship.findInternalByGroup('psg');
|
||||
const shieldGenerator = ship.getShieldGenerator();
|
||||
const sgClassNames = cn({ warning: shieldGenerator && !ship.shield, muted: !shieldGenerator });
|
||||
const sgTooltip = shieldGenerator ? 'TT_SUMMARY_SHIELDS' : 'TT_SUMMARY_SHIELDS_NONFUNCTIONAL';
|
||||
const timeToDrain = Calc.timeToDrainWep(ship, 4);
|
||||
@@ -64,15 +67,19 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
this.state = {
|
||||
shieldColour
|
||||
};
|
||||
|
||||
let speed = ship.get(SPEED);
|
||||
let jumpRange = ship.get(JUMP_RANGE);
|
||||
|
||||
return <div id='summary'>
|
||||
<div style={{display: "table", width: "100%"}}>
|
||||
<div style={{display: "table-row"}}>
|
||||
<table className={'summaryTable'}>
|
||||
<thead>
|
||||
<tr className='main'>
|
||||
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canThrust }) }>{translate('speed')}</th>
|
||||
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': speed == 0 }) }>{translate('speed')}</th>
|
||||
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canBoost }) }>{translate('boost')}</th>
|
||||
<th colSpan={5} className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('jump range')}</th>
|
||||
<th colSpan={5} className={ cn({ 'bg-warning-disabled': jumpRange == 0 }) }>{translate('jump range')}</th>
|
||||
<th rowSpan={2}>{translate('shield')}</th>
|
||||
<th rowSpan={2}>{translate('integrity')}</th>
|
||||
<th rowSpan={2}>{translate('DPS')}</th>
|
||||
|
||||
@@ -6,22 +6,19 @@ import AvailableModulesMenu from './AvailableModulesMenu';
|
||||
import ModificationsMenu from './ModificationsMenu';
|
||||
import { diffDetails } from '../utils/SlotFunctions';
|
||||
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
||||
import { Ship, Module } from 'ed-forge';
|
||||
import { REG_MILITARY_SLOT } from 'ed-forge/lib/data/slots';
|
||||
|
||||
/**
|
||||
* Abstract Slot
|
||||
*/
|
||||
export default class Slot extends TranslatedComponent {
|
||||
|
||||
static propTypes = {
|
||||
availableModules: PropTypes.func.isRequired,
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
onOpen: PropTypes.func.isRequired,
|
||||
maxClass: PropTypes.number.isRequired,
|
||||
selected: PropTypes.bool,
|
||||
m: PropTypes.object,
|
||||
enabled: PropTypes.bool.isRequired,
|
||||
ship: PropTypes.object.isRequired,
|
||||
eligible: PropTypes.object,
|
||||
slot: PropTypes.instanceOf(Module),
|
||||
ship: PropTypes.instanceOf(Ship),
|
||||
warning: PropTypes.func,
|
||||
drag: PropTypes.func,
|
||||
drop: PropTypes.func,
|
||||
@@ -61,7 +58,7 @@ export default class Slot extends TranslatedComponent {
|
||||
* @return {string} label
|
||||
*/
|
||||
_getMaxClassLabel() {
|
||||
return this.props.maxClass;
|
||||
return this.props.slot.getSize();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,7 +94,7 @@ export default class Slot extends TranslatedComponent {
|
||||
render() {
|
||||
let language = this.context.language;
|
||||
let translate = language.translate;
|
||||
let { ship, m, enabled, dropClass, dragOver, onOpen, onChange, selected, eligible, onSelect, warning, availableModules } = this.props;
|
||||
let { ship, slot, dropClass, dragOver, onOpen, onChange, selected, onSelect, warning } = this.props;
|
||||
let slotDetails, modificationsMarker, menu;
|
||||
|
||||
if (!selected) {
|
||||
@@ -105,36 +102,37 @@ export default class Slot extends TranslatedComponent {
|
||||
this._modificationsSelected = false;
|
||||
}
|
||||
|
||||
if (m) {
|
||||
slotDetails = this._getSlotDetails(m, enabled, translate, language.formats, language.units); // Must be implemented by sub classes
|
||||
modificationsMarker = JSON.stringify(m);
|
||||
if (!slot.isEmpty()) {
|
||||
slotDetails = this._getSlotDetails(slot, translate, language.formats, language.units); // Must be implemented by sub classes
|
||||
} else {
|
||||
slotDetails = <div className={'empty'}>{translate(eligible ? 'emptyrestricted' : 'empty')}</div>;
|
||||
modificationsMarker = '';
|
||||
slotDetails = <div className={'empty'}>
|
||||
{translate(
|
||||
slot.getSlot().match(REG_MILITARY_SLOT) ? 'emptyrestricted' : 'empty'
|
||||
)}
|
||||
</div>;
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
if (this._modificationsSelected) {
|
||||
menu = <ModificationsMenu
|
||||
className={this._getClassNames()}
|
||||
onChange={onChange}
|
||||
ship={ship}
|
||||
m={m}
|
||||
marker={modificationsMarker}
|
||||
modButton = {this.modButton}
|
||||
/>;
|
||||
} else {
|
||||
// if (this._modificationsSelected) {
|
||||
// menu = <ModificationsMenu
|
||||
// className={this._getClassNames()}
|
||||
// onChange={onChange}
|
||||
// ship={ship}
|
||||
// m={m}
|
||||
// marker={modificationsMarker}
|
||||
// modButton = {this.modButton}
|
||||
// />;
|
||||
// } else {
|
||||
menu = <AvailableModulesMenu
|
||||
className={this._getClassNames()}
|
||||
modules={availableModules()}
|
||||
m={m}
|
||||
m={slot}
|
||||
ship={ship}
|
||||
onSelect={onSelect}
|
||||
warning={warning}
|
||||
diffDetails={diffDetails.bind(ship, this.context.language)}
|
||||
slotDiv = {this.slotDiv}
|
||||
/>;
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
// TODO: implement touch dragging
|
||||
@@ -150,7 +148,6 @@ export default class Slot extends TranslatedComponent {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Toggle the modifications flag when selecting the modifications icon
|
||||
*/
|
||||
|
||||
@@ -5,19 +5,17 @@ import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
||||
import { canMount } from '../utils/SlotFunctions';
|
||||
import { Equalizer } from '../components/SvgIcons';
|
||||
import cn from 'classnames';
|
||||
import { Ship } from 'ed-forge';
|
||||
const browser = require('detect-browser');
|
||||
|
||||
/**
|
||||
* Abstract Slot Section
|
||||
*/
|
||||
export default class SlotSection extends TranslatedComponent {
|
||||
|
||||
static propTypes = {
|
||||
ship: PropTypes.object.isRequired,
|
||||
ship: PropTypes.instanceOf(Ship),
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onCargoChange: PropTypes.func.isRequired,
|
||||
onFuelChange: PropTypes.func.isRequired,
|
||||
code: PropTypes.string.isRequired,
|
||||
// code: PropTypes.string.isRequired,
|
||||
togglePwr: PropTypes.func,
|
||||
sectionMenuRefs: PropTypes.object
|
||||
};
|
||||
@@ -106,6 +104,7 @@ export default class SlotSection extends TranslatedComponent {
|
||||
this.sectionRefArr['ssHeadRef'].focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a menu
|
||||
* @param {string} menu Menu name
|
||||
@@ -126,7 +125,7 @@ export default class SlotSection extends TranslatedComponent {
|
||||
* @param {Object} m Selected module
|
||||
*/
|
||||
_selectModule(slot, m) {
|
||||
this.props.ship.use(slot, m, false);
|
||||
slot.setItem(m);
|
||||
this.props.onChange();
|
||||
this._close();
|
||||
}
|
||||
|
||||
@@ -6,24 +6,22 @@ import TranslatedComponent from './TranslatedComponent';
|
||||
import { diffDetails } from '../utils/SlotFunctions';
|
||||
import AvailableModulesMenu from './AvailableModulesMenu';
|
||||
import ModificationsMenu from './ModificationsMenu';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import { ListModifications, Modified } from './SvgIcons';
|
||||
import { Modifications } from 'coriolis-data/dist';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
import { blueprintTooltip } from '../utils/BlueprintFunctions';
|
||||
import { Ship, Module } from 'ed-forge';
|
||||
|
||||
/**
|
||||
* Standard Slot
|
||||
*/
|
||||
export default class StandardSlot extends TranslatedComponent {
|
||||
|
||||
static propTypes = {
|
||||
slot: PropTypes.object,
|
||||
modules: PropTypes.array.isRequired,
|
||||
slot: PropTypes.instanceOf(Module),
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
onOpen: PropTypes.func.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
ship: PropTypes.object.isRequired,
|
||||
ship: PropTypes.instanceOf(Ship),
|
||||
selected: PropTypes.bool,
|
||||
warning: PropTypes.func,
|
||||
};
|
||||
@@ -59,31 +57,26 @@ export default class StandardSlot extends TranslatedComponent {
|
||||
render() {
|
||||
let { termtip, tooltip } = this.context;
|
||||
let { translate, formats, units } = this.context.language;
|
||||
let { modules, slot, selected, warning, onSelect, onChange, ship } = this.props;
|
||||
let m = slot.m;
|
||||
let classRating = m.class + m.rating;
|
||||
let { slot, selected, warning, onSelect, onChange, ship } = this.props;
|
||||
let classRating = String(slot.getClass()) + (slot.getRating() || '');
|
||||
let menu;
|
||||
let validMods = m == null || !Modifications.modules[m.grp] ? [] : (Modifications.modules[m.grp].modifications || []);
|
||||
if (m && m.name && m.name === 'Guardian Hybrid Power Plant') {
|
||||
validMods = [];
|
||||
}
|
||||
if (m && m.name && m.name === 'Guardian Power Distributor') {
|
||||
validMods = [];
|
||||
}
|
||||
let validMods = slot.getApplicableBlueprints();
|
||||
let showModuleResistances = Persist.showModuleResistances();
|
||||
let mass = m.getMass() || m.cargo || m.fuel || 0;
|
||||
let mass = slot.get('cargo') || slot.get('fuel') || slot.get('mass') || 0;
|
||||
|
||||
// Modifications tooltip shows blueprint and grade, if available
|
||||
let modTT = translate('modified');
|
||||
if (m && m.blueprint && m.blueprint.name) {
|
||||
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||
if (m.blueprint.special && m.blueprint.special.id >= 0) {
|
||||
modTT += ', ' + translate(m.blueprint.special.name);
|
||||
const appliedBlueprint = slot.getBlueprint();
|
||||
const appliedExperimental = slot.getExperimental();
|
||||
if (appliedBlueprint) {
|
||||
modTT = translate(appliedBlueprint) + ' ' + translate('grade') + ' ' + slot.getBlueprintGrade();
|
||||
if (appliedExperimental) {
|
||||
modTT += ', ' + translate(appliedExperimental);
|
||||
}
|
||||
modTT = (
|
||||
<div>
|
||||
<div>{modTT}</div>
|
||||
{blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], null, m.grp, m)}
|
||||
{blueprintTooltip(translate, slot)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -93,23 +86,19 @@ export default class StandardSlot extends TranslatedComponent {
|
||||
this._modificationsSelected = false;
|
||||
}
|
||||
|
||||
const modificationsMarker = JSON.stringify(m);
|
||||
|
||||
if (selected) {
|
||||
if (this._modificationsSelected) {
|
||||
menu = <ModificationsMenu
|
||||
className='standard'
|
||||
onChange={onChange}
|
||||
ship={ship}
|
||||
m={m}
|
||||
marker={modificationsMarker}
|
||||
m={slot}
|
||||
modButton = {this.modButton}
|
||||
/>;
|
||||
} else {
|
||||
menu = <AvailableModulesMenu
|
||||
className='standard'
|
||||
modules={modules}
|
||||
m={m}
|
||||
m={slot}
|
||||
ship={ship}
|
||||
onSelect={onSelect}
|
||||
warning={warning}
|
||||
@@ -121,14 +110,14 @@ export default class StandardSlot extends TranslatedComponent {
|
||||
|
||||
return (
|
||||
<div className={cn('slot', { selected: this.props.selected })} onClick={this.props.onOpen} onKeyDown={this._keyDown} onContextMenu={stopCtxPropagation} tabIndex="0" ref={ slotDiv => this.slotDiv = slotDiv }>
|
||||
<div className={cn('details-container', { warning: warning && warning(slot.m), disabled: m.grp !== 'bh' && !slot.enabled })}>
|
||||
<div className={'sz'}>{m.grp == 'bh' ? m.name.charAt(0) : slot.maxClass}</div>
|
||||
<div className={cn('details-container', { warning: warning && warning(slot), disabled: slot.isEnabled() })}>
|
||||
<div className={'sz'}>{slot.getSize()}</div>
|
||||
<div>
|
||||
<div className={'l'}>{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }</div>
|
||||
<div className={'l'}>{classRating} {translate(slot.getItem())}{appliedBlueprint ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }</div>
|
||||
<div className={'r'}>{formats.round(mass)}{units.T}</div>
|
||||
<div/>
|
||||
<div className={'cb'}>
|
||||
{ m.getMinMass() ? <div className='l'>{translate('minimum mass')}: {formats.int(m.getMinMass())}{units.T}</div> : null }
|
||||
{/* { m.getMinMass() ? <div className='l'>{translate('minimum mass')}: {formats.int(m.getMinMass())}{units.T}</div> : null }
|
||||
{ m.getOptMass() ? <div className='l'>{translate('optimal mass')}: {formats.int(m.getOptMass())}{units.T}</div> : null }
|
||||
{ m.getMaxMass() ? <div className='l'>{translate('max mass')}: {formats.int(m.getMaxMass())}{units.T}</div> : null }
|
||||
{ m.getOptMul() ? <div className='l'>{translate('optimal multiplier')}: {formats.rPct(m.getOptMul())}</div> : null }
|
||||
@@ -143,8 +132,8 @@ export default class StandardSlot extends TranslatedComponent {
|
||||
{ showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null }
|
||||
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
||||
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
|
||||
{ m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null }
|
||||
{ validMods.length > 0 ? <div className='r' tabIndex="0" ref={ modButton => this.modButton = modButton }><button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
|
||||
{ m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null } */}
|
||||
{/* { validMods.length > 0 ? <div className='r' tabIndex="0" ref={ modButton => this.modButton = modButton }><button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null } */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,12 +5,12 @@ import StandardSlot from './StandardSlot';
|
||||
import Module from '../shipyard/Module';
|
||||
import * as ShipRoles from '../shipyard/ShipRoles';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
import { ShipProps } from 'ed-forge';
|
||||
|
||||
/**
|
||||
* Standard Slot section
|
||||
*/
|
||||
export default class StandardSlotSection extends SlotSection {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
@@ -39,8 +39,6 @@ export default class StandardSlotSection extends SlotSection {
|
||||
this.selectedRefId = 'maxjump';
|
||||
this.props.ship.useLightestStandard();
|
||||
this.props.onChange();
|
||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||
this.props.onFuelChange(this.props.ship.fuelCapacity);
|
||||
this._close();
|
||||
}
|
||||
|
||||
@@ -54,8 +52,6 @@ export default class StandardSlotSection extends SlotSection {
|
||||
if (bulkheadIndex === 2) this.selectedRefId = 'combat';
|
||||
ShipRoles.multiPurpose(this.props.ship, shielded, bulkheadIndex);
|
||||
this.props.onChange();
|
||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||
this.props.onFuelChange(this.props.ship.fuelCapacity);
|
||||
this._close();
|
||||
}
|
||||
|
||||
@@ -67,8 +63,6 @@ export default class StandardSlotSection extends SlotSection {
|
||||
this.selectedRefId = 'trader';
|
||||
ShipRoles.trader(this.props.ship, shielded);
|
||||
this.props.onChange();
|
||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||
this.props.onFuelChange(this.props.ship.fuelCapacity);
|
||||
this._close();
|
||||
}
|
||||
|
||||
@@ -80,8 +74,6 @@ export default class StandardSlotSection extends SlotSection {
|
||||
this.selectedRefId = 'miner';
|
||||
ShipRoles.miner(this.props.ship, shielded);
|
||||
this.props.onChange();
|
||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||
this.props.onFuelChange(this.props.ship.fuelCapacity);
|
||||
this._close();
|
||||
}
|
||||
|
||||
@@ -94,8 +86,6 @@ export default class StandardSlotSection extends SlotSection {
|
||||
if (planetary) this.selectedRefId = 'planetary';
|
||||
ShipRoles.explorer(this.props.ship, planetary);
|
||||
this.props.onChange();
|
||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||
this.props.onFuelChange(this.props.ship.fuelCapacity);
|
||||
this._close();
|
||||
}
|
||||
|
||||
@@ -106,8 +96,6 @@ export default class StandardSlotSection extends SlotSection {
|
||||
this.selectedRefId = 'racer';
|
||||
ShipRoles.racer(this.props.ship);
|
||||
this.props.onChange();
|
||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||
this.props.onFuelChange(this.props.ship.fuelCapacity);
|
||||
this._close();
|
||||
}
|
||||
|
||||
@@ -134,105 +122,114 @@ export default class StandardSlotSection extends SlotSection {
|
||||
* @return {Array} Array of Slots
|
||||
*/
|
||||
_getSlots() {
|
||||
let { ship, currentMenu, cargo, fuel } = this.props;
|
||||
let { ship, currentMenu } = this.props;
|
||||
let slots = new Array(8);
|
||||
let open = this._openMenu;
|
||||
let select = this._selectModule;
|
||||
let st = ship.standard;
|
||||
let avail = ship.getAvailableModules().standard;
|
||||
let bh = ship.bulkheads;
|
||||
// let st = ship.standard;
|
||||
// let avail = ship.getAvailableModules().standard;
|
||||
// let bh = ship.bulkheads;
|
||||
|
||||
let armour = ship.getAlloys();
|
||||
slots[0] = <StandardSlot
|
||||
key='bh'
|
||||
slot={bh}
|
||||
modules={ship.getAvailableModules().bulkheads}
|
||||
onOpen={open.bind(this, bh)}
|
||||
slot={armour}
|
||||
modules={armour.getApplicableItems()}
|
||||
onOpen={open.bind(this, armour)}
|
||||
onSelect={this._selectBulkhead}
|
||||
selected={currentMenu == bh}
|
||||
selected={currentMenu == armour}
|
||||
onChange={this.props.onChange}
|
||||
ship={ship}
|
||||
/>;
|
||||
|
||||
const powerPlant = ship.getPowerPlant();
|
||||
slots[1] = <StandardSlot
|
||||
key='pp'
|
||||
slot={st[0]}
|
||||
modules={avail[0]}
|
||||
onOpen={open.bind(this, st[0])}
|
||||
onSelect={select.bind(this, st[0])}
|
||||
selected={currentMenu == st[0]}
|
||||
slot={powerPlant}
|
||||
modules={powerPlant.getApplicableItems()}
|
||||
onOpen={open.bind(this, powerPlant)}
|
||||
onSelect={select.bind(this, powerPlant)}
|
||||
selected={currentMenu == powerPlant}
|
||||
onChange={this.props.onChange}
|
||||
ship={ship}
|
||||
warning={m => m instanceof Module ? m.getPowerGeneration() < ship.powerRetracted : m.pgen < ship.powerRetracted}
|
||||
warning={m => ship.get(ShipProps.CONSUMED_RETR) < m.get('powercapacity')}
|
||||
/>;
|
||||
|
||||
const thrusters = ship.getThrusters();
|
||||
slots[2] = <StandardSlot
|
||||
key='th'
|
||||
slot={st[1]}
|
||||
modules={avail[1]}
|
||||
onOpen={open.bind(this, st[1])}
|
||||
onSelect={select.bind(this, st[1])}
|
||||
selected={currentMenu == st[1]}
|
||||
slot={thrusters}
|
||||
modules={thrusters.getApplicableItems()}
|
||||
onOpen={open.bind(this, thrusters)}
|
||||
onSelect={select.bind(this, thrusters)}
|
||||
selected={currentMenu == thrusters}
|
||||
onChange={this.props.onChange}
|
||||
ship={ship}
|
||||
warning={m => m instanceof Module ? m.getMaxMass() < (ship.unladenMass + cargo + fuel - st[1].m.mass + m.mass) : m.maxmass < (ship.unladenMass + cargo + fuel - st[1].m.mass + m.mass)}
|
||||
warning={m => m.get('enginemaximalmass') < ship.get(ShipProps.LADEN_MASS)}
|
||||
/>;
|
||||
|
||||
|
||||
const fsd = ship.getFSD();
|
||||
slots[3] = <StandardSlot
|
||||
key='fsd'
|
||||
slot={st[2]}
|
||||
modules={avail[2]}
|
||||
onOpen={open.bind(this, st[2])}
|
||||
onSelect={select.bind(this, st[2])}
|
||||
slot={fsd}
|
||||
modules={fsd.getApplicableItems()}
|
||||
onOpen={open.bind(this, fsd)}
|
||||
onSelect={select.bind(this, fsd)}
|
||||
onChange={this.props.onChange}
|
||||
ship={ship}
|
||||
selected={currentMenu == st[2]}
|
||||
selected={currentMenu == fsd}
|
||||
/>;
|
||||
|
||||
const lifeSupport = ship.getLifeSupport();
|
||||
slots[4] = <StandardSlot
|
||||
key='ls'
|
||||
slot={st[3]}
|
||||
modules={avail[3]}
|
||||
onOpen={open.bind(this, st[3])}
|
||||
onSelect={select.bind(this, st[3])}
|
||||
slot={lifeSupport}
|
||||
modules={lifeSupport.getApplicableItems()}
|
||||
onOpen={open.bind(this, lifeSupport)}
|
||||
onSelect={select.bind(this, lifeSupport)}
|
||||
onChange={this.props.onChange}
|
||||
ship={ship}
|
||||
selected={currentMenu == st[3]}
|
||||
selected={currentMenu == lifeSupport}
|
||||
/>;
|
||||
|
||||
const powerDistributor = ship.getPowerDistributor();
|
||||
slots[5] = <StandardSlot
|
||||
key='pd'
|
||||
slot={st[4]}
|
||||
modules={avail[4]}
|
||||
onOpen={open.bind(this, st[4])}
|
||||
onSelect={select.bind(this, st[4])}
|
||||
selected={currentMenu == st[4]}
|
||||
slot={powerDistributor}
|
||||
modules={powerDistributor.getApplicableItems()}
|
||||
onOpen={open.bind(this, powerDistributor)}
|
||||
onSelect={select.bind(this, powerDistributor)}
|
||||
selected={currentMenu == powerDistributor}
|
||||
onChange={this.props.onChange}
|
||||
ship={ship}
|
||||
warning={m => m instanceof Module ? m.getEnginesCapacity() <= ship.boostEnergy : m.engcap <= ship.boostEnergy}
|
||||
/>;
|
||||
|
||||
const sensors = ship.getSensors();
|
||||
slots[6] = <StandardSlot
|
||||
key='ss'
|
||||
slot={st[5]}
|
||||
modules={avail[5]}
|
||||
onOpen={open.bind(this, st[5])}
|
||||
onSelect={select.bind(this, st[5])}
|
||||
selected={currentMenu == st[5]}
|
||||
slot={sensors}
|
||||
modules={sensors.getApplicableItems()}
|
||||
onOpen={open.bind(this, sensors)}
|
||||
onSelect={select.bind(this, sensors)}
|
||||
selected={currentMenu == sensors}
|
||||
onChange={this.props.onChange}
|
||||
ship={ship}
|
||||
/>;
|
||||
|
||||
const fuelTank = ship.getCoreFuelTank();
|
||||
slots[7] = <StandardSlot
|
||||
key='ft'
|
||||
slot={st[6]}
|
||||
modules={avail[6]}
|
||||
onOpen={open.bind(this, st[6])}
|
||||
onSelect={select.bind(this, st[6])}
|
||||
selected={currentMenu == st[6]}
|
||||
slot={fuelTank}
|
||||
modules={fuelTank.getApplicableItems()}
|
||||
onOpen={open.bind(this, fuelTank)}
|
||||
onSelect={select.bind(this, fuelTank)}
|
||||
selected={currentMenu == fuelTank}
|
||||
onChange={this.props.onChange}
|
||||
ship={ship}
|
||||
warning= {m => m.fuel < st[2].m.maxfuel} // Show warning when fuel tank is smaller than FSD Max Fuel
|
||||
// Show warning when fuel tank is smaller than FSD Max Fuel
|
||||
warning= {m => m.get('fuel') < fsd.get('maxfuel')}
|
||||
/>;
|
||||
|
||||
return slots;
|
||||
@@ -261,5 +258,4 @@ export default class StandardSlotSection extends SlotSection {
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -67,17 +67,12 @@ export default class UtilitySlotSection extends SlotSection {
|
||||
_getSlots() {
|
||||
let slots = [];
|
||||
let { ship, currentMenu } = this.props;
|
||||
let hardpoints = ship.hardpoints;
|
||||
let { originSlot, targetSlot } = this.state;
|
||||
let availableModules = ship.getAvailableModules();
|
||||
|
||||
for (let i = 0, l = hardpoints.length; i < l; i++) {
|
||||
let h = hardpoints[i];
|
||||
if (h.maxClass === 0) {
|
||||
for (let h of ship.getUtilities(undefined, true)) {
|
||||
slots.push(<HardpointSlot
|
||||
key={i}
|
||||
maxClass={h.maxClass}
|
||||
availableModules={() => availableModules.getHps(h.maxClass)}
|
||||
key={h.object.Slot}
|
||||
maxClass={h.getSize()}
|
||||
onOpen={this._openMenu.bind(this,h)}
|
||||
onSelect={this._selectModule.bind(this, h)}
|
||||
onChange={this.props.onChange}
|
||||
@@ -87,11 +82,10 @@ export default class UtilitySlotSection extends SlotSection {
|
||||
drop={this._drop}
|
||||
dropClass={this._dropClass(h, originSlot, targetSlot)}
|
||||
ship={ship}
|
||||
m={h.m}
|
||||
slot={h}
|
||||
enabled={h.enabled ? true : false}
|
||||
/>);
|
||||
}
|
||||
}
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import Page from './Page';
|
||||
import Router from '../Router';
|
||||
import Persist from '../stores/Persist';
|
||||
import * as Utils from '../utils/UtilityFunctions';
|
||||
import Ship from '../shipyard/Ship';
|
||||
import { Factory, Ship } from 'ed-forge';
|
||||
import * as _ from 'lodash';
|
||||
import { toDetailedBuild } from '../shipyard/Serializer';
|
||||
import { outfitURL } from '../utils/UrlGenerators';
|
||||
@@ -64,10 +64,6 @@ export default class OutfittingPage extends Page {
|
||||
this.state = this._initState(props, context);
|
||||
this._keyDown = this._keyDown.bind(this);
|
||||
this._exportBuild = this._exportBuild.bind(this);
|
||||
this._pipsUpdated = this._pipsUpdated.bind(this);
|
||||
this._boostUpdated = this._boostUpdated.bind(this);
|
||||
this._cargoUpdated = this._cargoUpdated.bind(this);
|
||||
this._fuelUpdated = this._fuelUpdated.bind(this);
|
||||
this._opponentUpdated = this._opponentUpdated.bind(this);
|
||||
this._engagementRangeUpdated = this._engagementRangeUpdated.bind(this);
|
||||
this._sectionMenuRefs = {};
|
||||
@@ -82,40 +78,14 @@ export default class OutfittingPage extends Page {
|
||||
_initState(props, context) {
|
||||
let params = context.route.params;
|
||||
let shipId = params.ship;
|
||||
let code = params.code;
|
||||
let buildName = params.bn;
|
||||
let data = Ships[shipId]; // Retrieve the basic ship properties, slots and defaults
|
||||
let savedCode = Persist.getBuild(shipId, buildName);
|
||||
if (!data) {
|
||||
return { error: { message: 'Ship not found: ' + shipId } };
|
||||
}
|
||||
let ship = new Ship(shipId, data.properties, data.slots); // Create a new Ship instance
|
||||
if (code) {
|
||||
ship.buildFrom(code); // Populate modules from serialized 'code' URL param
|
||||
} else {
|
||||
ship.buildWith(data.defaults); // Populate with default components
|
||||
}
|
||||
let code = params.code || savedCode;
|
||||
let ship = code ? new Ship(code) : Factory.newShip(shipId); // Create a new Ship instance
|
||||
code = ship.compress();
|
||||
|
||||
this._getTitle = getTitle.bind(this, data.properties.name);
|
||||
this._getTitle = getTitle.bind(this, ship.getShipType());
|
||||
|
||||
// Obtain ship control from code
|
||||
const {
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
fuel,
|
||||
cargo,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
opponentSys,
|
||||
opponentEng,
|
||||
opponentWep,
|
||||
engagementRange
|
||||
} = this._obtainControlFromCode(ship, code);
|
||||
return {
|
||||
error: null,
|
||||
title: this._getTitle(buildName),
|
||||
@@ -126,21 +96,6 @@ export default class OutfittingPage extends Page {
|
||||
ship,
|
||||
code,
|
||||
savedCode,
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
fuel,
|
||||
cargo,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
opponentSys,
|
||||
opponentEng,
|
||||
opponentWep,
|
||||
engagementRange
|
||||
};
|
||||
}
|
||||
|
||||
@@ -168,170 +123,13 @@ export default class OutfittingPage extends Page {
|
||||
/**
|
||||
* Update the control part of the route
|
||||
*/
|
||||
_updateRouteOnControlChange() {
|
||||
_updateRoute() {
|
||||
const { ship, shipId, buildName } = this.state;
|
||||
const code = this._fullCode(ship);
|
||||
const code = ship.compress();
|
||||
this._updateRoute(shipId, buildName, code);
|
||||
this.setState({ code });
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a full code for this ship, including any additions due to the outfitting page
|
||||
* @param {Object} ship the ship
|
||||
* @param {number} fuel the fuel carried by the ship (if different from that in state)
|
||||
* @param {number} cargo the cargo carried by the ship (if different from that in state)
|
||||
* @returns {string} the code for this ship
|
||||
*/
|
||||
_fullCode(ship, fuel, cargo) {
|
||||
return `${ship.toString()}.${LZString.compressToBase64(
|
||||
this._controlCode(fuel, cargo)
|
||||
)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the control information from the build code
|
||||
* @param {Object} ship The ship
|
||||
* @param {string} code The build code
|
||||
* @returns {Object} The control information
|
||||
*/
|
||||
_obtainControlFromCode(ship, code) {
|
||||
// Defaults
|
||||
let sys = 2;
|
||||
let eng = 2;
|
||||
let wep = 2;
|
||||
let mcSys = 0;
|
||||
let mcEng = 0;
|
||||
let mcWep = 0;
|
||||
let boost = false;
|
||||
let fuel = ship.fuelCapacity;
|
||||
let cargo = ship.cargoCapacity;
|
||||
let opponent = new Ship(
|
||||
'eagle',
|
||||
Ships['eagle'].properties,
|
||||
Ships['eagle'].slots
|
||||
).buildWith(Ships['eagle'].defaults);
|
||||
let opponentSys = 2;
|
||||
let opponentEng = 2;
|
||||
let opponentWep = 2;
|
||||
let opponentBuild;
|
||||
let engagementRange = 1000;
|
||||
|
||||
// Obtain updates from code, if available
|
||||
if (code) {
|
||||
const parts = code.split('.');
|
||||
if (parts.length >= 5) {
|
||||
// We have control information in the code
|
||||
const control = LZString.decompressFromBase64(
|
||||
Utils.fromUrlSafe(parts[4])
|
||||
).split('/');
|
||||
sys = parseFloat(control[0]);
|
||||
eng = parseFloat(control[1]);
|
||||
wep = parseFloat(control[2]);
|
||||
if (sys + eng + wep > 6) {
|
||||
sys = eng = wep = 2;
|
||||
}
|
||||
boost = control[3] == 1 ? true : false;
|
||||
fuel = parseFloat(control[4]) || fuel;
|
||||
cargo = parseInt(control[5]) || cargo;
|
||||
if (control[6]) {
|
||||
const shipId = control[6];
|
||||
opponent = new Ship(
|
||||
shipId,
|
||||
Ships[shipId].properties,
|
||||
Ships[shipId].slots
|
||||
);
|
||||
if (control[7] && Persist.getBuild(shipId, control[7])) {
|
||||
// Ship is a particular build
|
||||
const opponentCode = Persist.getBuild(shipId, control[7]);
|
||||
opponent.buildFrom(opponentCode);
|
||||
opponentBuild = control[7];
|
||||
if (opponentBuild) {
|
||||
// Obtain opponent's sys/eng/wep pips from their code
|
||||
const opponentParts = opponentCode.split('.');
|
||||
if (opponentParts.length >= 5) {
|
||||
const opponentControl = LZString.decompressFromBase64(
|
||||
Utils.fromUrlSafe(opponentParts[4])
|
||||
).split('/');
|
||||
opponentSys = parseFloat(opponentControl[0]) || opponentSys;
|
||||
opponentEng = parseFloat(opponentControl[1]) || opponentEng;
|
||||
opponentWep = parseFloat(opponentControl[2]) || opponentWep;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Ship is a stock build
|
||||
opponent.buildWith(Ships[shipId].defaults);
|
||||
}
|
||||
}
|
||||
engagementRange = parseInt(control[8]) || engagementRange;
|
||||
|
||||
// Multi-crew pips were introduced later on so assign default values
|
||||
// because those values might not be present.
|
||||
mcSys = parseInt(control[9]) || mcSys;
|
||||
mcEng = parseInt(control[10]) || mcEng;
|
||||
mcWep = parseInt(control[11]) || mcWep;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
fuel,
|
||||
cargo,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
opponentSys,
|
||||
opponentEng,
|
||||
opponentWep,
|
||||
engagementRange
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when pips have been updated. Multi-crew pips are already included
|
||||
* in sys, eng and wep but mcSys, mcEng and mcWep make clear where each pip
|
||||
* comes from.
|
||||
* @param {number} sys SYS pips
|
||||
* @param {number} eng ENG pips
|
||||
* @param {number} wep WEP pips
|
||||
* @param {number} mcSys SYS pips from multi-crew
|
||||
* @param {number} mcEng ENG pips from multi-crew
|
||||
* @param {number} mcWep WEP pips from multi-crew
|
||||
*/
|
||||
_pipsUpdated(sys, eng, wep, mcSys, mcEng, mcWep) {
|
||||
this.setState({ sys, eng, wep, mcSys, mcEng, mcWep }, () =>
|
||||
this._updateRouteOnControlChange()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when boost has been updated
|
||||
* @param {boolean} boost true if boosting
|
||||
*/
|
||||
_boostUpdated(boost) {
|
||||
this.setState({ boost }, () => this._updateRouteOnControlChange());
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when fuel has been updated
|
||||
* @param {number} fuel the amount of fuel, in T
|
||||
*/
|
||||
_fuelUpdated(fuel) {
|
||||
this.setState({ fuel }, () => this._updateRouteOnControlChange());
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when cargo has been updated
|
||||
* @param {number} cargo the amount of cargo, in T
|
||||
*/
|
||||
_cargoUpdated(cargo) {
|
||||
this.setState({ cargo }, () => this._updateRouteOnControlChange());
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when engagement range has been updated
|
||||
* @param {number} engagementRange the engagement range, in m
|
||||
@@ -387,36 +185,10 @@ export default class OutfittingPage extends Page {
|
||||
opponentEng,
|
||||
opponentWep
|
||||
},
|
||||
() => this._updateRouteOnControlChange()
|
||||
() => this._updateRoute()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the control code for this outfitting page
|
||||
* @param {number} fuel the fuel carried by the ship (if different from that in state)
|
||||
* @param {number} cargo the cargo carried by the ship (if different from that in state)
|
||||
* @returns {string} The control code
|
||||
*/
|
||||
_controlCode(fuel, cargo) {
|
||||
const {
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
engagementRange
|
||||
} = this.state;
|
||||
const code = `${sys}/${eng}/${wep}/${boost ? 1 : 0}/${fuel ||
|
||||
this.state.fuel}/${cargo || this.state.cargo}/${opponent.id}/${
|
||||
opponentBuild ? opponentBuild : ''
|
||||
}/${engagementRange}/${mcSys}/${mcEng}/${mcWep}`;
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the current build
|
||||
*/
|
||||
@@ -424,7 +196,7 @@ export default class OutfittingPage extends Page {
|
||||
const { ship, buildName, newBuildName, shipId } = this.state;
|
||||
|
||||
// If this is a stock ship the code won't be set, so ensure that we have it
|
||||
const code = this.state.code || ship.toString();
|
||||
const code = this.state.code || ship.compress();
|
||||
|
||||
Persist.saveBuild(shipId, newBuildName, code);
|
||||
this._updateRoute(shipId, newBuildName, code);
|
||||
@@ -497,39 +269,9 @@ export default class OutfittingPage extends Page {
|
||||
// Rebuild ship
|
||||
ship.buildWith(Ships[shipId].defaults);
|
||||
// Reset controls
|
||||
const code = ship.toString();
|
||||
const {
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
fuel,
|
||||
cargo,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
engagementRange
|
||||
} = this._obtainControlFromCode(ship, code);
|
||||
const code = ship.compress();
|
||||
// Update state, and refresh the ship
|
||||
this.setState(
|
||||
{
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
fuel,
|
||||
cargo,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
engagementRange
|
||||
},
|
||||
() => this._updateRoute(shipId, buildName, code)
|
||||
);
|
||||
this._updateRoute(shipId, buildName, code);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -573,65 +315,22 @@ export default class OutfittingPage extends Page {
|
||||
* Called when the code for the ship has been updated, to synchronise the rest of the data
|
||||
*/
|
||||
_codeUpdated() {
|
||||
const { code, ship, shipId, buildName } = this.state;
|
||||
const { code, shipId, buildName } = this.state;
|
||||
|
||||
// Rebuild ship from the code
|
||||
this.state.ship.buildFrom(code);
|
||||
|
||||
// Obtain controls from the code
|
||||
const {
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
fuel,
|
||||
cargo,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
engagementRange
|
||||
} = this._obtainControlFromCode(ship, code);
|
||||
// Update state, and refresh the route when complete
|
||||
this.setState(
|
||||
{
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
fuel,
|
||||
cargo,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
engagementRange
|
||||
},
|
||||
() => this._updateRoute(shipId, buildName, code)
|
||||
);
|
||||
this.setState({
|
||||
ship: new Ship(code),
|
||||
}, () => this._updateRoute(shipId, buildName, code));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the ship has been updated, to set the code and then update accordingly
|
||||
*/
|
||||
_shipUpdated() {
|
||||
let { ship, shipId, buildName, cargo, fuel } = this.state;
|
||||
if (cargo > ship.cargoCapacity) {
|
||||
cargo = ship.cargoCapacity;
|
||||
}
|
||||
if (fuel > ship.fuelCapacity) {
|
||||
fuel = ship.fuelCapacity;
|
||||
}
|
||||
const code = this._fullCode(ship, fuel, cargo);
|
||||
let { ship, shipId, buildName } = this.state;
|
||||
const code = ship.compress();
|
||||
// Only update the state if this really has been updated
|
||||
if (
|
||||
this.state.code != code ||
|
||||
this.state.cargo != cargo ||
|
||||
this.state.fuel != fuel
|
||||
) {
|
||||
this.setState({ code, cargo, fuel }, () =>
|
||||
if (this.state.code != code) {
|
||||
this.setState({ code }, () =>
|
||||
this._updateRoute(shipId, buildName, code)
|
||||
);
|
||||
}
|
||||
@@ -747,29 +446,20 @@ export default class OutfittingPage extends Page {
|
||||
*/
|
||||
renderPage() {
|
||||
let state = this.state,
|
||||
{ language, termtip, tooltip, sizeRatio, onWindowResize } = this.context,
|
||||
{ translate, units, formats } = language,
|
||||
{ language, termtip, tooltip, sizeRatio } = this.context,
|
||||
{ translate } = language,
|
||||
{
|
||||
ship,
|
||||
code,
|
||||
savedCode,
|
||||
buildName,
|
||||
newBuildName,
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
fuel,
|
||||
cargo,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
opponentSys,
|
||||
opponentEng,
|
||||
opponentWep,
|
||||
engagementRange
|
||||
// opponent,
|
||||
// opponentBuild,
|
||||
// opponentSys,
|
||||
// opponentEng,
|
||||
// opponentWep,
|
||||
// engagementRange
|
||||
} = state,
|
||||
hide = tooltip.bind(null, null),
|
||||
menu = this.props.currentMenu,
|
||||
@@ -782,88 +472,88 @@ export default class OutfittingPage extends Page {
|
||||
code = ship.name + (code || '');
|
||||
|
||||
// Markers are used to propagate state changes without requiring a deep comparison of the ship, as that takes a long time
|
||||
const _sStr = ship.getStandardString();
|
||||
const _iStr = ship.getInternalString();
|
||||
const _hStr = ship.getHardpointsString();
|
||||
const _pStr = `${ship.getPowerEnabledString()}${ship.getPowerPrioritiesString()}`;
|
||||
const _mStr = ship.getModificationsString();
|
||||
// const _sStr = ship.getStandardString();
|
||||
// const _iStr = ship.getInternalString();
|
||||
// const _hStr = ship.getHardpointsString();
|
||||
// const _pStr = `${ship.getPowerEnabledString()}${ship.getPowerPrioritiesString()}`;
|
||||
// const _mStr = ship.getModificationsString();
|
||||
|
||||
const standardSlotMarker = `${ship.name}${_sStr}${_pStr}${_mStr}${
|
||||
ship.ladenMass
|
||||
}${cargo}${fuel}`;
|
||||
const internalSlotMarker = `${ship.name}${_iStr}${_pStr}${_mStr}`;
|
||||
const hardpointsSlotMarker = `${ship.name}${_hStr}${_pStr}${_mStr}`;
|
||||
const boostMarker = `${ship.canBoost(cargo, fuel)}`;
|
||||
const shipSummaryMarker = `${
|
||||
ship.name
|
||||
}${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`;
|
||||
// const standardSlotMarker = `${ship.name}${_sStr}${_pStr}${_mStr}${
|
||||
// ship.ladenMass
|
||||
// }${cargo}${fuel}`;
|
||||
// const internalSlotMarker = `${ship.name}${_iStr}${_pStr}${_mStr}`;
|
||||
// const hardpointsSlotMarker = `${ship.name}${_hStr}${_pStr}${_mStr}`;
|
||||
// const boostMarker = `${ship.canBoost(cargo, fuel)}`;
|
||||
// const shipSummaryMarker = `${
|
||||
// ship.name
|
||||
// }${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`;
|
||||
|
||||
const requirements = Ships[ship.id].requirements;
|
||||
let requirementElements = [];
|
||||
/**
|
||||
* Render the requirements for a ship / etc
|
||||
* @param {string} className Class names
|
||||
* @param {string} textKey The key for translating
|
||||
* @param {String} tooltipTextKey Tooltip key
|
||||
*/
|
||||
function renderRequirement(className, textKey, tooltipTextKey) {
|
||||
if (textKey.startsWith('empire') || textKey.startsWith('federation')) {
|
||||
requirementElements.push(
|
||||
<div
|
||||
key={textKey}
|
||||
className={className}
|
||||
onMouseEnter={termtip.bind(null, tooltipTextKey)}
|
||||
onMouseLeave={hide}
|
||||
>
|
||||
<a
|
||||
href={
|
||||
textKey.startsWith('empire') ?
|
||||
'http://elite-dangerous.wikia.com/wiki/Empire/Ranks' :
|
||||
'http://elite-dangerous.wikia.com/wiki/Federation/Ranks'
|
||||
}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
{translate(textKey)}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
requirementElements.push(
|
||||
<div
|
||||
key={textKey}
|
||||
className={className}
|
||||
onMouseEnter={termtip.bind(null, tooltipTextKey)}
|
||||
onMouseLeave={hide}
|
||||
>
|
||||
{translate(textKey)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
// const requirements = Ships[ship.id].requirements;
|
||||
// let requirementElements = [];
|
||||
// /**
|
||||
// * Render the requirements for a ship / etc
|
||||
// * @param {string} className Class names
|
||||
// * @param {string} textKey The key for translating
|
||||
// * @param {String} tooltipTextKey Tooltip key
|
||||
// */
|
||||
// function renderRequirement(className, textKey, tooltipTextKey) {
|
||||
// if (textKey.startsWith('empire') || textKey.startsWith('federation')) {
|
||||
// requirementElements.push(
|
||||
// <div
|
||||
// key={textKey}
|
||||
// className={className}
|
||||
// onMouseEnter={termtip.bind(null, tooltipTextKey)}
|
||||
// onMouseLeave={hide}
|
||||
// >
|
||||
// <a
|
||||
// href={
|
||||
// textKey.startsWith('empire') ?
|
||||
// 'http://elite-dangerous.wikia.com/wiki/Empire/Ranks' :
|
||||
// 'http://elite-dangerous.wikia.com/wiki/Federation/Ranks'
|
||||
// }
|
||||
// target="_blank"
|
||||
// rel="noopener"
|
||||
// >
|
||||
// {translate(textKey)}
|
||||
// </a>
|
||||
// </div>
|
||||
// );
|
||||
// } else {
|
||||
// requirementElements.push(
|
||||
// <div
|
||||
// key={textKey}
|
||||
// className={className}
|
||||
// onMouseEnter={termtip.bind(null, tooltipTextKey)}
|
||||
// onMouseLeave={hide}
|
||||
// >
|
||||
// {translate(textKey)}
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
if (requirements) {
|
||||
requirements.federationRank &&
|
||||
renderRequirement(
|
||||
'federation',
|
||||
'federation rank ' + requirements.federationRank,
|
||||
'federation rank required'
|
||||
);
|
||||
requirements.empireRank &&
|
||||
renderRequirement(
|
||||
'empire',
|
||||
'empire rank ' + requirements.empireRank,
|
||||
'empire rank required'
|
||||
);
|
||||
requirements.horizons &&
|
||||
renderRequirement('horizons', 'horizons', 'horizons required');
|
||||
requirements.horizonsEarlyAdoption &&
|
||||
renderRequirement(
|
||||
'horizons',
|
||||
'horizons early adoption',
|
||||
'horizons early adoption required'
|
||||
);
|
||||
}
|
||||
// if (requirements) {
|
||||
// requirements.federationRank &&
|
||||
// renderRequirement(
|
||||
// 'federation',
|
||||
// 'federation rank ' + requirements.federationRank,
|
||||
// 'federation rank required'
|
||||
// );
|
||||
// requirements.empireRank &&
|
||||
// renderRequirement(
|
||||
// 'empire',
|
||||
// 'empire rank ' + requirements.empireRank,
|
||||
// 'empire rank required'
|
||||
// );
|
||||
// requirements.horizons &&
|
||||
// renderRequirement('horizons', 'horizons', 'horizons required');
|
||||
// requirements.horizonsEarlyAdoption &&
|
||||
// renderRequirement(
|
||||
// 'horizons',
|
||||
// 'horizons early adoption',
|
||||
// 'horizons early adoption required'
|
||||
// );
|
||||
// }
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -872,8 +562,8 @@ export default class OutfittingPage extends Page {
|
||||
style={{ fontSize: sizeRatio * 0.9 + 'em' }}
|
||||
>
|
||||
<div id="overview">
|
||||
<h1>{ship.name}</h1>
|
||||
<div id="requirements">{requirementElements}</div>
|
||||
<h1>{ship.getShipType()}</h1>
|
||||
{/* <div id="requirements">{requirementElements}</div> */}
|
||||
<div id="build">
|
||||
<input
|
||||
value={newBuildName || ''}
|
||||
@@ -933,7 +623,7 @@ export default class OutfittingPage extends Page {
|
||||
<Download className="lg" />
|
||||
</button>
|
||||
<button
|
||||
onClick={this._eddbShoppingList}
|
||||
// onClick={this._eddbShoppingList}
|
||||
onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_LIST')}
|
||||
onMouseOut={hide}
|
||||
>
|
||||
@@ -954,7 +644,7 @@ export default class OutfittingPage extends Page {
|
||||
<OrbisIcon className="lg" />
|
||||
</button>
|
||||
<button
|
||||
onClick={this._genShoppingList}
|
||||
// onClick={this._genShoppingList}
|
||||
onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_MATS')}
|
||||
onMouseOut={hide}
|
||||
>
|
||||
@@ -964,22 +654,13 @@ export default class OutfittingPage extends Page {
|
||||
</div>
|
||||
|
||||
{/* Main tables */}
|
||||
<ShipSummaryTable
|
||||
{/* <ShipSummaryTable
|
||||
ship={ship}
|
||||
fuel={fuel}
|
||||
cargo={cargo}
|
||||
marker={shipSummaryMarker}
|
||||
pips={{
|
||||
sys: this.state.sys,
|
||||
wep: this.state.wep,
|
||||
eng: this.state.eng
|
||||
}}
|
||||
/>
|
||||
/> */}
|
||||
<StandardSlotSection
|
||||
ship={ship}
|
||||
fuel={fuel}
|
||||
cargo={cargo}
|
||||
code={standardSlotMarker}
|
||||
// code={standardSlotMarker}
|
||||
onChange={shipUpdated}
|
||||
onCargoChange={this._cargoUpdated}
|
||||
onFuelChange={this._fuelUpdated}
|
||||
@@ -988,7 +669,7 @@ export default class OutfittingPage extends Page {
|
||||
/>
|
||||
<InternalSlotSection
|
||||
ship={ship}
|
||||
code={internalSlotMarker}
|
||||
// code={internalSlotMarker}
|
||||
onChange={shipUpdated}
|
||||
onCargoChange={this._cargoUpdated}
|
||||
onFuelChange={this._fuelUpdated}
|
||||
@@ -997,7 +678,7 @@ export default class OutfittingPage extends Page {
|
||||
/>
|
||||
<HardpointSlotSection
|
||||
ship={ship}
|
||||
code={hardpointsSlotMarker}
|
||||
// code={hardpointsSlotMarker}
|
||||
onChange={shipUpdated}
|
||||
onCargoChange={this._cargoUpdated}
|
||||
onFuelChange={this._fuelUpdated}
|
||||
@@ -1006,7 +687,7 @@ export default class OutfittingPage extends Page {
|
||||
/>
|
||||
<UtilitySlotSection
|
||||
ship={ship}
|
||||
code={hardpointsSlotMarker}
|
||||
// code={hardpointsSlotMarker}
|
||||
onChange={shipUpdated}
|
||||
onCargoChange={this._cargoUpdated}
|
||||
onFuelChange={this._fuelUpdated}
|
||||
@@ -1015,7 +696,7 @@ export default class OutfittingPage extends Page {
|
||||
/>
|
||||
|
||||
{/* Control of ship and opponent */}
|
||||
<div className="group quarter">
|
||||
{/* <div className="group quarter">
|
||||
<div className="group half">
|
||||
<h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>
|
||||
{translate('ship control')}
|
||||
@@ -1044,7 +725,6 @@ export default class OutfittingPage extends Page {
|
||||
<div className="group quarter">
|
||||
<Fuel
|
||||
fuelCapacity={ship.fuelCapacity}
|
||||
fuel={fuel}
|
||||
onChange={this._fuelUpdated}
|
||||
/>
|
||||
</div>
|
||||
@@ -1052,7 +732,6 @@ export default class OutfittingPage extends Page {
|
||||
{ship.cargoCapacity > 0 ? (
|
||||
<Cargo
|
||||
cargoCapacity={ship.cargoCapacity}
|
||||
cargo={cargo}
|
||||
onChange={this._cargoUpdated}
|
||||
/>
|
||||
) : null}
|
||||
@@ -1077,10 +756,10 @@ export default class OutfittingPage extends Page {
|
||||
engagementRange={engagementRange}
|
||||
onChange={this._engagementRangeUpdated}
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
{/* Tabbed subpages */}
|
||||
<OutfittingSubpages
|
||||
{/* <OutfittingSubpages
|
||||
ship={ship}
|
||||
code={code}
|
||||
buildName={buildName}
|
||||
@@ -1097,7 +776,7 @@ export default class OutfittingPage extends Page {
|
||||
opponentSys={opponentSys}
|
||||
opponentEng={opponentEng}
|
||||
opponentWep={opponentWep}
|
||||
/>
|
||||
/> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
import React from 'react';
|
||||
import Page from './Page';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import cn from 'classnames';
|
||||
import Ship from '../shipyard/Ship';
|
||||
import { Factory } from 'ed-forge';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import { SizeMap } from '../shipyard/Constants';
|
||||
import Link from '../components/Link';
|
||||
|
||||
/**
|
||||
* Counts the hardpoints by class/size
|
||||
* @param {Object} slot Hardpoint Slot model
|
||||
*/
|
||||
function countHp(slot) {
|
||||
this.hp[slot.maxClass]++;
|
||||
this.hpCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the internal slots and aggregated properties
|
||||
* @param {Object} slot Internal Slots
|
||||
@@ -53,50 +43,55 @@ function countInt(slot) {
|
||||
/**
|
||||
* Generate Ship summary and aggregated properties
|
||||
* @param {String} shipId Ship Id
|
||||
* @param {Object} shipData Ship Default Data
|
||||
* @return {Object} Ship summary and aggregated properties
|
||||
*/
|
||||
function shipSummary(shipId, shipData) {
|
||||
function shipSummary(shipId) {
|
||||
// Build Ship
|
||||
let ship = Factory.newShip(shipId);
|
||||
|
||||
let summary = {
|
||||
id: shipId,
|
||||
hpCount: 0,
|
||||
intCount: 0,
|
||||
beta: shipData.beta,
|
||||
maxCargo: 0,
|
||||
maxPassengers: 0,
|
||||
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
|
||||
int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8
|
||||
standard: shipData.slots.standard,
|
||||
standard: ship.readMeta('coreSizes'),
|
||||
agility:
|
||||
shipData.properties.pitch +
|
||||
shipData.properties.yaw +
|
||||
shipData.properties.roll
|
||||
ship.getBaseProperty('pitch') +
|
||||
ship.getBaseProperty('yaw') +
|
||||
ship.getBaseProperty('roll')
|
||||
};
|
||||
Object.assign(summary, shipData.properties);
|
||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||
|
||||
// Build Ship
|
||||
ship.buildWith(shipData.defaults); // Populate with stock/default components
|
||||
ship.hardpoints.forEach(countHp.bind(summary)); // Count Hardpoints by class
|
||||
ship.internal.forEach(countInt.bind(summary)); // Count Internal Compartments by class
|
||||
summary.retailCost = ship.totalCost; // Record Stock/Default/retail cost
|
||||
ship.optimizeMass({ pd: '1D' }); // Optimize Mass with 1D PD for maximum possible jump range
|
||||
summary.maxJumpRange = ship.unladenRange; // Record Jump Range
|
||||
// Count Hardpoints by class
|
||||
ship.getHardpoints(undefined, true).forEach(hardpoint => {
|
||||
summary.hp[hardpoint.getSize()]++;
|
||||
summary.hpCount++;
|
||||
});
|
||||
// Count Internal Compartments by class
|
||||
ship.getInternals(undefined, true).forEach(internal => {
|
||||
summary.int[internal.getSize()]++;
|
||||
summary.intCount++;
|
||||
});
|
||||
summary.retailCost = ship.readMeta('retailCost'); // Record Stock/Default/retail cost
|
||||
// ship.optimizeMass({ pd: '1D' }); // Optimize Mass with 1D PD for maximum possible jump range
|
||||
summary.maxJumpRange = -1; // ship.unladenRange; // Record Jump Range
|
||||
|
||||
// Best thrusters
|
||||
let th;
|
||||
if (ship.standard[1].maxClass === 3) {
|
||||
th = 'tz';
|
||||
} else if (ship.standard[1].maxClass === 2) {
|
||||
th = 'u0';
|
||||
} else {
|
||||
th = ship.standard[1].maxClass + 'A';
|
||||
}
|
||||
// let th;
|
||||
// if (ship.standard[1].maxClass === 3) {
|
||||
// th = 'tz';
|
||||
// } else if (ship.standard[1].maxClass === 2) {
|
||||
// th = 'u0';
|
||||
// } else {
|
||||
// th = ship.standard[1].maxClass + 'A';
|
||||
// }
|
||||
|
||||
ship.optimizeMass({ th, fsd: '2D', ft: '1C' }); // Optmize mass with Max Thrusters
|
||||
summary.topSpeed = ship.topSpeed;
|
||||
summary.topBoost = ship.topBoost;
|
||||
summary.baseArmour = ship.armour;
|
||||
// ship.optimizeMass({ th, fsd: '2D', ft: '1C' }); // Optmize mass with Max Thrusters
|
||||
summary.topSpeed = -1; // ship.topSpeed;
|
||||
summary.topBoost = -1; // ship.topBoost;
|
||||
summary.baseArmour = -1; // ship.armour;
|
||||
|
||||
return summary;
|
||||
}
|
||||
@@ -117,8 +112,8 @@ export default class ShipyardPage extends Page {
|
||||
|
||||
if (!ShipyardPage.cachedShipSummaries) {
|
||||
ShipyardPage.cachedShipSummaries = [];
|
||||
for (let s in Ships) {
|
||||
ShipyardPage.cachedShipSummaries.push(shipSummary(s, Ships[s]));
|
||||
for (let s of Factory.getAllShipTypes()) {
|
||||
ShipyardPage.cachedShipSummaries.push(shipSummary(s));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +330,7 @@ export default class ShipyardPage extends Page {
|
||||
onClick={() => this._toggleCompare(s.id)}
|
||||
>
|
||||
<td className="le">
|
||||
<Link href={'/outfit/' + s.id}>{s.name} {s.beta === true ? '(Beta)' : null}</Link>
|
||||
<Link href={'/outfit/' + s.id}>{s.id} {s.beta === true ? '(Beta)' : null}</Link>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
@@ -66,12 +66,10 @@ export function specialToolTip(translate, blueprint, grp, m, specialName) {
|
||||
* Generate a tooltip with details of a blueprint's effects
|
||||
* @param {Object} translate The translate object
|
||||
* @param {Object} blueprint The blueprint at the required grade
|
||||
* @param {Array} engineers The engineers supplying this blueprint
|
||||
* @param {string} grp The group of the module
|
||||
* @param {Object} m The module to compare with
|
||||
* @returns {Object} The react components
|
||||
*/
|
||||
export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
|
||||
export function blueprintTooltip(translate, blueprint, m) {
|
||||
const effects = [];
|
||||
if (!blueprint || !blueprint.features) {
|
||||
return undefined;
|
||||
@@ -105,7 +103,7 @@ export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
|
||||
const currentIsBeneficial = isValueBeneficial(feature, current);
|
||||
effects.push(
|
||||
<tr key={feature}>
|
||||
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
||||
<td style={{ textAlign: 'left' }}>{translate(feature)}</td>
|
||||
<td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td>
|
||||
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
||||
<td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td>
|
||||
@@ -115,7 +113,7 @@ export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
|
||||
// We do not have a module, no value
|
||||
effects.push(
|
||||
<tr key={feature}>
|
||||
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
||||
<td style={{ textAlign: 'left' }}>{translate(feature)}</td>
|
||||
<td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td>
|
||||
<td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td>
|
||||
</tr>
|
||||
@@ -144,7 +142,7 @@ export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
|
||||
const currentIsBeneficial = isValueBeneficial(feature, current);
|
||||
effects.push(
|
||||
<tr key={feature}>
|
||||
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
||||
<td style={{ textAlign: 'left' }}>{translate(feature)}</td>
|
||||
<td> </td>
|
||||
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
||||
<td> </td>
|
||||
@@ -175,7 +173,7 @@ export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
|
||||
const currentIsBeneficial = isValueBeneficial(feature, current);
|
||||
effects.push(
|
||||
<tr key={feature}>
|
||||
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
||||
<td style={{ textAlign: 'left' }}>{translate(feature)}</td>
|
||||
<td> </td>
|
||||
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
||||
<td> </td>
|
||||
@@ -200,17 +198,18 @@ export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
|
||||
}
|
||||
}
|
||||
|
||||
let engineersList;
|
||||
if (engineers) {
|
||||
engineersList = [];
|
||||
for (const engineer of engineers) {
|
||||
engineersList.push(
|
||||
<tr key={engineer}>
|
||||
<td style={{ textAlign: 'left' }}>{engineer}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
let engineersList = [];
|
||||
// TODO:
|
||||
// if (engineers) {
|
||||
// engineersList = [];
|
||||
// for (const engineer of engineers) {
|
||||
// engineersList.push(
|
||||
// <tr key={engineer}>
|
||||
// <td style={{ textAlign: 'left' }}>{engineer}</td>
|
||||
// </tr>
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -285,7 +284,7 @@ export function isValueBeneficial(feature, value) {
|
||||
* Is the change as shown beneficial?
|
||||
* @param {string} feature The name of the feature
|
||||
* @param {number} value The value of the feature as percentage change
|
||||
* @returns True if the value is beneficial
|
||||
* @returns {boolean} True if the value is beneficial
|
||||
*/
|
||||
export function isChangeValueBeneficial(feature, value) {
|
||||
let changeHigherBetter = STATS_FORMATTING[feature].higherbetter;
|
||||
|
||||
Reference in New Issue
Block a user