mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-08 22:33:24 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c347336055 | ||
|
|
cd87184169 | ||
|
|
09895852c3 | ||
|
|
32759d3e0e | ||
|
|
965134fc02 | ||
|
|
7da5360349 | ||
|
|
bf0701340c | ||
|
|
a3a490f442 | ||
|
|
33b22b8280 | ||
|
|
e6278858b9 | ||
|
|
ed7a9ef037 | ||
|
|
9c42179617 |
@@ -108,7 +108,8 @@ describe('Import Modal', function() {
|
||||
it('catches an invalid backup', function() {
|
||||
const importData = require('./fixtures/valid-backup');
|
||||
let invalidImportData = Object.assign({}, importData);
|
||||
invalidImportData.builds.asp = null; // Remove Asp Miner build used in comparison
|
||||
//invalidImportData.builds.asp = null; // Remove Asp Miner build used in comparison
|
||||
delete(invalidImportData.builds.asp);
|
||||
|
||||
pasteText('"this is not valid"');
|
||||
expect(modal.state.importValid).toBeFalsy();
|
||||
|
||||
@@ -16,7 +16,7 @@ describe("Ship", function() {
|
||||
ship.buildWith(shipData.defaults);
|
||||
|
||||
expect(ship.totalCost).toEqual(shipData.retailCost, s + ' retail cost does not match default build cost');
|
||||
expect(ship.cargoCapacity).toBeDefined(s + ' cargo');
|
||||
expect(ship.cargoCapacity).toBeDefined();
|
||||
expect(ship.priorityBands[0].retracted).toBeGreaterThan(0, s + ' priorityBands');
|
||||
expect(ship.powerAvailable).toBeGreaterThan(0, s + ' powerAvailable');
|
||||
expect(ship.unladenRange).toBeGreaterThan(0, s + ' unladenRange');
|
||||
|
||||
15
package.json
15
package.json
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "coriolis_shipyard",
|
||||
"version": "2.1.2",
|
||||
"version": "2.2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cmmcleod/coriolis"
|
||||
"url": "https://github.com/EDCD/coriolis"
|
||||
},
|
||||
"homepage": "https://coriolis.io",
|
||||
"bugs": "https://github.com/cmmcleod/coriolis/issues",
|
||||
"homepage": "https://coriolis.edcd.io",
|
||||
"bugs": "https://github.com/EDCD/coriolis/issues",
|
||||
"private": true,
|
||||
"engine": "node >= 4.0.0",
|
||||
"license": "MIT",
|
||||
@@ -24,14 +24,13 @@
|
||||
},
|
||||
"jest": {
|
||||
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
|
||||
"testFileExtensions": [
|
||||
"js"
|
||||
],
|
||||
"testRegex": "(/__tests__/test-.*|\\.(test|spec))\\.js$",
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"jsx"
|
||||
],
|
||||
"automock": true,
|
||||
"unmockedModulePathPatterns": [
|
||||
"<rootDir>/node_modules/react",
|
||||
"<rootDir>/node_modules/react-dom",
|
||||
@@ -68,7 +67,7 @@
|
||||
"extract-text-webpack-plugin": "^0.9.1",
|
||||
"file-loader": "^0.8.4",
|
||||
"html-webpack-plugin": "^1.7.0",
|
||||
"jest-cli": "^0.9.2",
|
||||
"jest-cli": "^16.0.1",
|
||||
"jsen": "^0.6.0",
|
||||
"json-loader": "^0.5.3",
|
||||
"less": "^2.5.3",
|
||||
|
||||
@@ -606,6 +606,7 @@ export default class CostSection extends TranslatedComponent {
|
||||
}
|
||||
|
||||
if (nextProps.ship != this.props.ship || nextProps.code != this.props.code) {
|
||||
nextProps.ship.applyDiscounts(Persist.getShipDiscount(), Persist.getModuleDiscount());
|
||||
this._updateAmmoCosts(nextProps.ship);
|
||||
this._updateRetrofit(nextProps.ship, retrofitShip);
|
||||
this._sortCost(nextProps.ship);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import Slot from './Slot';
|
||||
import { DamageKinetic, DamageThermal, DamageExplosive, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||
|
||||
/**
|
||||
* Hardpoint / Utility Slot
|
||||
@@ -33,24 +34,31 @@ export default class HardpointSlot extends Slot {
|
||||
*/
|
||||
_getSlotDetails(m, translate, formats, u) {
|
||||
if (m) {
|
||||
let classRating = `${m.class}${m.rating}${m.mount ? '/' + m.mount : ''}${m.missile ? m.missile : ''}`;
|
||||
let classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
|
||||
let { drag, drop } = this.props;
|
||||
|
||||
return <div className='details' draggable='true' onDragStart={drag} onDragEnd={drop}>
|
||||
<div className={'cb'}>
|
||||
<div className={'l'}>{classRating} {translate(m.name || m.grp)}</div>
|
||||
<div className={'l'}>
|
||||
{m.mount && m.mount == 'F' ? <MountFixed /> : ''}
|
||||
{m.mount && m.mount == 'G' ? <MountGimballed /> : ''}
|
||||
{m.mount && m.mount == 'T' ? <MountTurret /> : ''}
|
||||
{m.type && m.type == 'K' ? <DamageKinetic /> : ''}
|
||||
{m.type && m.type == 'T' ? <DamageThermal /> : ''}
|
||||
{m.type && m.type == 'KT' ? <span><DamageKinetic /><DamageThermal /></span> : ''}
|
||||
{m.type && m.type == 'E' ? <DamageExplosive /> : ''}
|
||||
{classRating} {translate(m.name || m.grp)}</div>
|
||||
<div className={'r'}>{m.mass}{u.T}</div>
|
||||
</div>
|
||||
<div className={'cb'}>
|
||||
{ m.damage ? <div className={'l'}>{translate('damage')}: {m.damage} { m.ssdam ? <span>({formats.int(m.ssdam)} {u.MJ})</span> : null }</div> : null }
|
||||
{ m.dps ? <div className={'l'}>{translate('DPS')}: {m.dps} { m.mjdps ? <span>({formats.int(m.mjdps)} {u.MJ})</span> : null }</div> : null }
|
||||
{ m.thermload ? <div className={'l'}>{translate('T-Load')}: {m.thermload}</div> : null }
|
||||
{ m.type ? <div className={'l'}>{translate('type')}: {m.type}</div> : null }
|
||||
{ m.dps ? <div className={'l'}>{translate('DPS')}: {formats.round1(m.dps)} { m.clip ? <span>({formats.round1((m.clip * m.dps / m.rof) / ((m.clip / m.rof) + m.reload)) })</span> : null }</div> : null }
|
||||
{ m.eps ? <div className={'l'}>{translate('EPS')}: {formats.round1(m.eps)} { m.clip ? <span>({formats.round1((m.clip * m.eps / m.rof) / ((m.clip / m.rof) + m.reload)) })</span> : null }</div> : null }
|
||||
{ m.hps ? <div className={'l'}>{translate('HPS')}: {formats.round1(m.hps)} { m.clip ? <span>({formats.round1((m.clip * m.hps / m.rof) / ((m.clip / m.rof) + m.reload)) })</span> : null }</div> : null }
|
||||
{ m.dps && m.eps ? <div className={'l'}>{translate('DPE')}: {formats.round1(m.dps / m.eps)}</div> : null }
|
||||
{ m.rof ? <div className={'l'}>{translate('ROF')}: {m.rof}{u.ps}</div> : null }
|
||||
{ m.armourpen ? <div className={'l'}>{translate('pen')}: {m.armourpen}</div> : null }
|
||||
{ m.range && !m.dps ? <div className={'l'}>{translate('Range')} : {formats.round(m.range / 1000)}{u.km}</div> : null }
|
||||
{ m.shieldmul ? <div className={'l'}>+{formats.rPct(m.shieldmul)}</div> : null }
|
||||
{ m.range ? <div className={'l'}>{m.range} <u>km</u></div> : null }
|
||||
{ m.ammo >= 0 ? <div className={'l'}>{translate('ammo')}: {formats.int(m.clip)}+{formats.int(m.ammo)}</div> : null }
|
||||
{ m.ammo >= 0 ? <div className={'l'}>{translate('ammo')}: {formats.int(m.clip)}/{formats.int(m.ammo)}</div> : null }
|
||||
</div>
|
||||
</div>;
|
||||
} else {
|
||||
|
||||
@@ -39,7 +39,7 @@ export default class InternalSlot extends Slot {
|
||||
{ m.time ? <div className={'l'}>{translate('time')}: {formats.time(m.time)}</div> : null }
|
||||
{ m.maximum ? <div className={'l'}>{translate('max')}: {(m.maximum)}</div> : null }
|
||||
{ m.rangeLS ? <div className={'l'}>{translate('range')}: {m.rangeLS}{u.Ls}</div> : null }
|
||||
{ m.rangeLS === null ? <div className={'l'}><Infinite/>{u.Ls}</div> : null }
|
||||
{ m.rangeLS === null ? <div className={'l'}>∞{u.Ls}</div> : null }
|
||||
{ m.rangeRating ? <div className={'l'}>{translate('range')}: {m.rangeRating}</div> : null }
|
||||
{ m.armouradd ? <div className={'l'}>+{m.armouradd} <u className='cap'>{translate('armour')}</u></div> : null }
|
||||
</div>
|
||||
|
||||
@@ -58,7 +58,7 @@ export default class InternalSlotSection extends SlotSection {
|
||||
let ship = this.props.ship;
|
||||
let chargeCap = 0; // Capacity of single activation
|
||||
ship.internal.forEach(function(slot) {
|
||||
if ((!slot.m || (clobber && !ModuleUtils.isShieldGenerator(slot.m.grp))) && (!slot.eligible || slot.eligible.scb)) { // Check eligibility due to Orca special case
|
||||
if ((!slot.m || (clobber && !ModuleUtils.isShieldGenerator(slot.m.grp))) && (!slot.eligible || slot.eligible.scb)) { // Check eligibility due to passenger ships special case
|
||||
ship.use(slot, ModuleUtils.findInternal('scb', slot.maxClass, 'A'));
|
||||
ship.setSlotEnabled(slot, chargeCap <= ship.shieldStrength); // Don't waste cell capacity on overcharge
|
||||
chargeCap += slot.m.recharge;
|
||||
@@ -108,7 +108,7 @@ export default class InternalSlotSection extends SlotSection {
|
||||
slots.push(<InternalSlot
|
||||
key={i}
|
||||
maxClass={s.maxClass}
|
||||
availableModules={() => availableModules.getInts(s.maxClass, s.eligible)}
|
||||
availableModules={() => availableModules.getInts(ship, s.maxClass, s.eligible)}
|
||||
onOpen={this._openMenu.bind(this,s)}
|
||||
onSelect={this._selectModule.bind(this, s)}
|
||||
selected={currentMenu == s}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
||||
import { canMount } from '../utils/SlotFunctions';
|
||||
import { Equalizer } from '../components/SvgIcons';
|
||||
import cn from 'classnames';
|
||||
|
||||
@@ -90,7 +91,7 @@ export default class SlotSection extends TranslatedComponent {
|
||||
e.stopPropagation();
|
||||
let os = this.state.originSlot;
|
||||
if (os) {
|
||||
e.dataTransfer.dropEffect = os != targetSlot && targetSlot.maxClass >= os.m.class ? 'copyMove' : 'none';
|
||||
e.dataTransfer.dropEffect = os != targetSlot && canMount(this.props.ship, targetSlot, os.m.grp, os.m.class) ? 'copyMove' : 'none';
|
||||
this.setState({ targetSlot });
|
||||
} else {
|
||||
e.dataTransfer.dropEffect = 'none';
|
||||
@@ -116,9 +117,9 @@ export default class SlotSection extends TranslatedComponent {
|
||||
let { originSlot, targetSlot } = this.state;
|
||||
let m = originSlot.m;
|
||||
|
||||
if (targetSlot && m && targetSlot.maxClass >= m.class) {
|
||||
if (targetSlot && m && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
|
||||
// Swap modules if possible
|
||||
if (targetSlot.m && originSlot.maxClass >= targetSlot.m.class) {
|
||||
if (targetSlot.m && canMount(this.props.ship, originSlot, targetSlot.m.grp, targetSlot.m.class)) {
|
||||
this.props.ship.use(originSlot, targetSlot.m, true);
|
||||
} else { // Otherwise empty the origin slot
|
||||
this.props.ship.use(originSlot, null, true); // Empty but prevent summary update
|
||||
@@ -141,12 +142,12 @@ export default class SlotSection extends TranslatedComponent {
|
||||
return null;
|
||||
}
|
||||
if (slot === originSlot) {
|
||||
if (targetSlot && targetSlot.m && originSlot.maxClass < targetSlot.m.class) {
|
||||
if (targetSlot && targetSlot.m && !canMount(this.props.ship, originSlot, targetSlot.m.grp, targetSlot.m.class)) {
|
||||
return 'dropEmpty'; // Origin slot will be emptied
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (originSlot.m && slot.maxClass >= originSlot.m.class) { // Eligble drop slot
|
||||
if (originSlot.m && canMount(this.props.ship, slot, originSlot.m.grp, originSlot.m.class)) { // Eligble drop slot
|
||||
if (slot === targetSlot) {
|
||||
return 'drop'; // Can drop
|
||||
}
|
||||
|
||||
@@ -319,6 +319,90 @@ export class Warning extends SvgIcon {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thermal damage
|
||||
*/
|
||||
export class DamageThermal extends SvgIcon {
|
||||
/**
|
||||
* Overriden view box
|
||||
* @return {String} view box
|
||||
*/
|
||||
viewBox() { return '0 0 200 200'; }
|
||||
/**
|
||||
* Generate the SVG
|
||||
* @return {React.Component} SVG Contents
|
||||
*/
|
||||
svg() {
|
||||
return <g>
|
||||
<ellipse cx='100' cy='100' rx='90' ry='90' fillOpacity='0' />
|
||||
<ellipse cx='100' cy='100' rx='30' ry='30' fillOpacity='1' />
|
||||
<path d='M100 20v80' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kinetic damage
|
||||
*/
|
||||
export class DamageKinetic extends SvgIcon {
|
||||
/**
|
||||
* Overriden view box
|
||||
* @return {String} view box
|
||||
*/
|
||||
viewBox() { return '0 0 200 200'; }
|
||||
/**
|
||||
* Generate the SVG
|
||||
* @return {React.Component} SVG Contents
|
||||
*/
|
||||
svg() {
|
||||
return <g>
|
||||
<ellipse cx='100' cy='100' rx='90' ry='90' fillOpacity='0' />
|
||||
<ellipse cx='62' cy='67' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='62' cy='101' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='62' cy='135' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='50' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='84' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='118' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='152' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='138' cy='67' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='138' cy='101' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='138' cy='135' rx='5' ry='5' fillOpacity='1' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Explosive damage
|
||||
*/
|
||||
export class DamageExplosive extends SvgIcon {
|
||||
/**
|
||||
* Overriden view box
|
||||
* @return {String} view box
|
||||
*/
|
||||
viewBox() { return '0 0 200 200'; }
|
||||
/**
|
||||
* Generate the SVG
|
||||
* @return {React.Component} SVG Contents
|
||||
*/
|
||||
svg() {
|
||||
return <g>
|
||||
<ellipse cx='100' cy='100' rx='50' ry='50' fillOpacity='0' />
|
||||
<ellipse cx='100' cy='20' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='156.57' cy='36.57' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='180' cy='100' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='156.57' cy='163.43' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='180' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='43.43' cy='163.43' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='20' cy='100' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='43.43' cy='36.57' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='75' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='125' cy='100' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='125' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='75' cy='100' rx='5' ry='5' fillOpacity='1' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixed mount hardpoint
|
||||
*/
|
||||
@@ -334,11 +418,11 @@ export class MountFixed extends SvgIcon {
|
||||
*/
|
||||
svg() {
|
||||
return <g>
|
||||
<circle fillOpacity='0' r='70' cy='100' cx='100' strokeWidth='5' />
|
||||
<line y2='60' x2='101' y1='0' x1='101' strokeWidth='5' />
|
||||
<line y2='101' x2='200' y1='101' x1='140' strokeWidth='5' />
|
||||
<line y2='101' x2='60' y1='101' x1='0' strokeWidth='5' />
|
||||
<line y2='200' x2='101' y1='140' x1='101' strokeWidth='5' />
|
||||
<circle cx='100' cy='100' r='76' fillOpacity='0' />
|
||||
<path d='M0 100h48' />
|
||||
<path d='M152 100h48' />
|
||||
<path d='M100 0v48' />
|
||||
<path d='M100 152v48' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
@@ -358,8 +442,8 @@ export class MountGimballed extends SvgIcon {
|
||||
*/
|
||||
svg() {
|
||||
return <g>
|
||||
<ellipse ry='25' rx='95' cy='100' cx='100' fillOpacity='0' strokeWidth='5' />
|
||||
<ellipse ry='95' rx='25' cy='100' cx='100' fillOpacity='0' strokeWidth='5' />
|
||||
<ellipse cx='100' cy='100' rx='90' ry='25' fillOpacity='0' />
|
||||
<ellipse cx='100' cy='100' rx='20' ry='95' fillOpacity='0' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
@@ -379,9 +463,14 @@ export class MountTurret extends SvgIcon {
|
||||
*/
|
||||
svg() {
|
||||
return <g>
|
||||
<line y2='170' x2='162' y1='170' x1='8' strokeWidth='6' />
|
||||
<path d='m13,138l144,0l0,-50l-27,-40l-90,0l-27,40l0,50z' id='svg_12' fillOpacity='0' strokeWidth='6' />
|
||||
<line y2='91' x2='200' y1='91' x1='159' strokeWidth='6' />
|
||||
<path d='M40 50 A 40 40 0 0 0 0 90' />
|
||||
<path d='M40 50h40' />
|
||||
<path d='M120 90 A 40 40 0 0 0 80 50' />
|
||||
<path d='M0 90v40' />
|
||||
<path d='M120 90v40' />
|
||||
<path d='M0 120h120' />
|
||||
<path d='M120 90h80' />
|
||||
<path d='M0 160h120' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,12 +41,14 @@ export function getLanguage(langCode) {
|
||||
formats: {
|
||||
gen, // General number format (.e.g 1,001,001.1234)
|
||||
int: d3Locale.numberFormat(',.0f'), // Fixed to 0 decimal places (.e.g 1,001)
|
||||
f1: d3Locale.numberFormat(',.1f'), // Fixed to 1 decimal place (.e.g 1,001.1)
|
||||
f2: d3Locale.numberFormat(',.2f'), // Fixed to 2 decimal places (.e.g 1,001.10)
|
||||
s2: d3Locale.numberFormat('.2s'), // SI Format to 2 decimal places (.e.g 1.1k)
|
||||
pct: d3Locale.numberFormat('.2%'), // % to 2 decimal places (.e.g 5.40%)
|
||||
pct1: d3Locale.numberFormat('.1%'), // % to 1 decimal places (.e.g 5.4%)
|
||||
r2: d3Locale.numberFormat('.2r'), // Rounded to 2 significant numbers (.e.g 512 => 510, 4.122 => 4.1)
|
||||
rPct: d3.format('%'), // % to 0 decimal places (.e.g 5%)
|
||||
round1: (d) => gen(d3.round(d, 1)), // Rounded to 0-1 decimal places (.e.g 5.1, 4)
|
||||
round: (d) => gen(d3.round(d, 2)), // Rounded to 0-2 decimal places (.e.g 5.12, 4.1)
|
||||
time: (d) => (d < 0 ? '-' : '') + Math.floor(Math.abs(d) / 60) + ':' + ('00' + Math.floor(Math.abs(d) % 60)).substr(-2, 2)
|
||||
},
|
||||
|
||||
@@ -42,6 +42,7 @@ export const terms = {
|
||||
cs: 'Cargo Scanner',
|
||||
dc: 'Docking Computer',
|
||||
fc: 'Fragment Cannon',
|
||||
fh: 'Fighter Hangar',
|
||||
fi: 'FSD Interdictor',
|
||||
fs: 'Fuel Scoop',
|
||||
fsd: 'Frame Shift Drive',
|
||||
@@ -58,6 +59,10 @@ export const terms = {
|
||||
pa: 'Plasma Accelerator',
|
||||
pas: 'Planetary Approach Suite',
|
||||
pc: 'Prospector Limpet Controller',
|
||||
pce: 'Economy Class Passenger Cabin',
|
||||
pci: 'Business Class Passenger Cabin',
|
||||
pcm: 'First Class Passenger Cabin',
|
||||
pcq: 'Luxury Passenger Cabin',
|
||||
pd: 'power distributor',
|
||||
pl: 'Pulse Laser',
|
||||
pp: 'Power Plant',
|
||||
|
||||
@@ -29,7 +29,7 @@ export default class AboutPage extends Page {
|
||||
<p>This is a clone of the Coriolis project, whose original author is currently unable to maintain it. This clone is maintained by the <a href="http://edcd.github.io/">EDCD community</a>.</p>
|
||||
<p>To recover your builds, go to <a href='https://coriolis.io/' target='_blank'>https://coriolis.io/</a>, backup your builds (Settings / Backup), copy the text, return here and import (Settings / Import).</p>
|
||||
<p>The Coriolis project was inspired by <a href='http://www.edshipyard.com/' target='_blank'>E:D Shipyard</a> and, of course, <a href='http://www.elitedangerous.com' target='_blank'>Elite Dangerous</a>. The ultimate goal of Coriolis is to provide rich features to support in-game play and planning while engaging the E:D community to support its development.</p>
|
||||
<p>Coriolis was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments.</p>
|
||||
<p>Coriolis was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments. A number of assets were sourced from <a href='http://edassets.org' target='_blank'>ED Assets</a></p>
|
||||
|
||||
<a style={{ display: 'block', textDecoration: 'none' }} href='https://github.com/EDCD/coriolis' target='_blank' title='Coriolis Github Project'>
|
||||
<GitHub style={{ margin: '0.4em' }} className='l fg xl'/>
|
||||
|
||||
@@ -293,7 +293,7 @@ export default class OutfittingPage extends Page {
|
||||
<div id='overview'>
|
||||
<h1>{ship.name}</h1>
|
||||
<div id='build'>
|
||||
<input value={newBuildName} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxsize={50} />
|
||||
<input value={newBuildName} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxLength={50} />
|
||||
<button onClick={canSave && this._saveBuild} disabled={!canSave} onMouseOver={termtip.bind(null, 'save')} onMouseOut={hide}>
|
||||
<FloppyDisk className='lg' />
|
||||
</button>
|
||||
|
||||
@@ -241,7 +241,7 @@ export default class ShipyardPage extends Page {
|
||||
|
||||
return (
|
||||
<div className='page' style={{ fontSize: sizeRatio + 'em' }}>
|
||||
<p style={{ textAlign: 'center' }}>This is <strong>Coriolis EDCD Edition</strong> - a temporary clone of <a href='https://coriolis.io/' target='_blank'>https://coriolis.io/</a> with added support for E:D 2.1. For more info see Settings / <Link href="/about" className='block'>About</Link></p>
|
||||
<p style={{ textAlign: 'center' }}>This is <strong>Coriolis EDCD Edition</strong> - a temporary clone of <a href='https://coriolis.io/' target='_blank'>https://coriolis.io/</a> with added support for E:D 2.2. For more info see Settings / <Link href="/about" className='block'>About</Link></p>
|
||||
<div style={{ whiteSpace: 'nowrap', margin: '0 auto', fontSize: '0.8em', position: 'relative', display: 'inline-block', maxWidth: '100%' }}>
|
||||
<table style={{ width: '12em', position: 'absolute', zIndex: 1 }}>
|
||||
<thead>
|
||||
|
||||
@@ -37,6 +37,7 @@ export const ModuleGroupToName = {
|
||||
am: 'Auto Field-Maintenance Unit',
|
||||
bsg: 'Bi-Weave Shield Generator',
|
||||
cr: 'Cargo Rack',
|
||||
fh: 'Fighter Hangar',
|
||||
fi: 'Frame Shift Drive Interdictor',
|
||||
hb: 'Hatch Breaker Limpet Controller',
|
||||
hr: 'Hull Reinforcement Package',
|
||||
@@ -48,6 +49,10 @@ export const ModuleGroupToName = {
|
||||
dc: 'Docking Computer',
|
||||
fx: 'Fuel Transfer Limpet Controller',
|
||||
pc: 'Prospector Limpet Controller',
|
||||
pce: 'Economy Class Passenger Cabin',
|
||||
pci: 'Business Class Passenger Cabin',
|
||||
pcm: 'First Class Passenger Cabin',
|
||||
pcq: 'Luxury Passenger Cabin',
|
||||
cc: 'Collector Limpet Controller',
|
||||
|
||||
// Hard Points
|
||||
|
||||
@@ -71,16 +71,23 @@ export default class ModuleSet {
|
||||
|
||||
/**
|
||||
* Determine the modules that areeligible for an internal slot
|
||||
* @param {Object} ship The ship
|
||||
* @param {integer} c The max class module that can be mounted in the slot
|
||||
* @param {Object} eligible) The map of eligible internal groups
|
||||
* @return {object} A map of all eligible modules by group
|
||||
*/
|
||||
getInts(c, eligible) {
|
||||
getInts(ship, c, eligible) {
|
||||
let o = {};
|
||||
for (let key in this.internal) {
|
||||
if (eligible && !eligible[key]) {
|
||||
continue;
|
||||
}
|
||||
if (key == 'pcq' && !(ship.luxuryCabins && ship.luxuryCabins === true)) {
|
||||
continue;
|
||||
}
|
||||
if (key == 'fh' && !(ship.fighterHangars && ship.fighterHangars === true)) {
|
||||
continue;
|
||||
}
|
||||
let data = filter(this.internal[key], c, 0, this.mass);
|
||||
if (data.length) { // If group is not empty
|
||||
o[key] = data;
|
||||
|
||||
@@ -6,6 +6,29 @@ import LZString from 'lz-string';
|
||||
|
||||
const STANDARD = ['powerPlant', 'thrusters', 'frameShiftDrive', 'lifeSupport', 'powerDistributor', 'sensors', 'fuelTank'];
|
||||
|
||||
/**
|
||||
* Generates ship-loadout JSON Schema standard object
|
||||
* @param {Object} standard model
|
||||
* @return {Object} JSON Schema
|
||||
*/
|
||||
function standardToSchema(standard) {
|
||||
if (standard.m) {
|
||||
let o = {
|
||||
class: standard.m.class,
|
||||
rating: standard.m.rating,
|
||||
enabled: Boolean(standard.enabled),
|
||||
priority: standard.priority + 1
|
||||
};
|
||||
|
||||
if (standard.m.name) {
|
||||
o.name = standard.m.name;
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates ship-loadout JSON Schema slot object
|
||||
* @param {Object} slot Slot model
|
||||
@@ -61,13 +84,13 @@ export function toDetailedBuild(buildName, ship) {
|
||||
standard: {
|
||||
bulkheads: BulkheadNames[ship.bulkheads.m.index],
|
||||
cargoHatch: { enabled: Boolean(ship.cargoHatch.enabled), priority: ship.cargoHatch.priority + 1 },
|
||||
powerPlant: { class: standard[0].m.class, rating: standard[0].m.rating, enabled: Boolean(standard[0].enabled), priority: standard[0].priority + 1 },
|
||||
thrusters: { class: standard[1].m.class, rating: standard[1].m.rating, enabled: Boolean(standard[1].enabled), priority: standard[1].priority + 1 },
|
||||
frameShiftDrive: { class: standard[2].m.class, rating: standard[2].m.rating, enabled: Boolean(standard[2].enabled), priority: standard[2].priority + 1 },
|
||||
lifeSupport: { class: standard[3].m.class, rating: standard[3].m.rating, enabled: Boolean(standard[3].enabled), priority: standard[3].priority + 1 },
|
||||
powerDistributor: { class: standard[4].m.class, rating: standard[4].m.rating, enabled: Boolean(standard[4].enabled), priority: standard[4].priority + 1 },
|
||||
sensors: { class: standard[5].m.class, rating: standard[5].m.rating, enabled: Boolean(standard[5].enabled), priority: standard[5].priority + 1 },
|
||||
fuelTank: { class: standard[6].m.class, rating: standard[6].m.rating, enabled: Boolean(standard[6].enabled), priority: standard[6].priority + 1 }
|
||||
powerPlant: standardToSchema(standard[0]),
|
||||
thrusters: standardToSchema(standard[1]),
|
||||
frameShiftDrive: standardToSchema(standard[2]),
|
||||
lifeSupport: standardToSchema(standard[3]),
|
||||
powerDistributor: standardToSchema(standard[4]),
|
||||
sensors: standardToSchema(standard[5]),
|
||||
fuelTank: standardToSchema(standard[6])
|
||||
},
|
||||
hardpoints: hardpoints.filter(slot => slot.maxClass > 0).map(slotToSchema),
|
||||
utility: hardpoints.filter(slot => slot.maxClass === 0).map(slotToSchema),
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as Calc from './Calculations';
|
||||
import * as ModuleUtils from './ModuleUtils';
|
||||
import LZString from 'lz-string';
|
||||
|
||||
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs'];
|
||||
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh'];
|
||||
|
||||
/**
|
||||
* Returns the power usage type of a slot and it's particular modul
|
||||
|
||||
@@ -15,7 +15,7 @@ export function multiPurpose(ship, shielded, bulkheadIndex) {
|
||||
|
||||
if (shielded) {
|
||||
ship.internal.some(function(slot) {
|
||||
if (canMount(slot, 'sg')) { // Assuming largest slot can hold an eligible shield
|
||||
if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield
|
||||
ship.use(slot, ModuleUtils.findInternal('sg', slot.maxClass, 'A'));
|
||||
ship.setSlotEnabled(slot, true);
|
||||
return true;
|
||||
@@ -35,7 +35,7 @@ export function trader(ship, shielded, standardOpts) {
|
||||
|
||||
for (let i = ship.internal.length; i--;) {
|
||||
let slot = ship.internal[i];
|
||||
if (sg && canMount(slot, 'sg', sg.class)) {
|
||||
if (sg && canMount(ship, slot, 'sg', sg.class)) {
|
||||
ship.use(slot, sg);
|
||||
sg = null;
|
||||
} else {
|
||||
@@ -77,23 +77,23 @@ export function explorer(ship, planetary) {
|
||||
let slot = ship.internal[i];
|
||||
let nextSlot = (i + 1) < intLength ? ship.internal[i + 1] : null;
|
||||
// Fit best possible Fuel Scoop
|
||||
if (!fuelScoopSlot && canMount(slot, 'fs')) {
|
||||
if (!fuelScoopSlot && canMount(ship, slot, 'fs')) {
|
||||
fuelScoopSlot = slot;
|
||||
ship.use(slot, ModuleUtils.findInternal('fs', slot.maxClass, 'A'));
|
||||
ship.setSlotEnabled(slot, true);
|
||||
// Mount a Shield generator if possible AND an AFM Unit has been mounted already (Guarantees at least 1 AFM Unit)
|
||||
} else if (!sgSlot && shieldNext && canMount(slot, 'sg', sg.class) && !canMount(nextSlot, 'sg', sg.class)) {
|
||||
} else if (!sgSlot && shieldNext && canMount(ship, slot, 'sg', sg.class) && !canMount(ship, nextSlot, 'sg', sg.class)) {
|
||||
sgSlot = slot;
|
||||
shieldNext = false;
|
||||
ship.use(slot, sg);
|
||||
ship.setSlotEnabled(slot, true);
|
||||
// if planetary explorer and the next slot cannot mount a PVH or the next modul to mount is a SG
|
||||
} else if (planetary && !pvhSlot && canMount(slot, 'pv') && (shieldNext || !canMount(nextSlot, 'pv', 2))) {
|
||||
} else if (planetary && !pvhSlot && canMount(ship, slot, 'pv') && (shieldNext || !canMount(ship, nextSlot, 'pv', 2))) {
|
||||
pvhSlot = slot;
|
||||
ship.use(slot, ModuleUtils.findInternal('pv', Math.min(Math.floor(pvhSlot.maxClass / 2) * 2, 6), 'G'));
|
||||
ship.setSlotEnabled(slot, false); // Disabled power for PVH
|
||||
shieldNext = !sgSlot;
|
||||
} else if (afmUnitCount > 0 && canMount(slot, 'am')) {
|
||||
} else if (afmUnitCount > 0 && canMount(ship, slot, 'am')) {
|
||||
afmUnitCount--;
|
||||
ship.use(slot, ModuleUtils.findInternal('am', slot.maxClass, 'A'));
|
||||
ship.setSlotEnabled(slot, false); // Disabled power for AFM Unit
|
||||
|
||||
@@ -5,14 +5,19 @@ import { Infinite } from '../components/SvgIcons';
|
||||
import Persist from '../stores/Persist';
|
||||
|
||||
/**
|
||||
* Determine if a slot can mount a module of a particular class and group
|
||||
* Determine if a slot on a ship can mount a module of a particular class and group
|
||||
* @param {Object} ship Ship object
|
||||
* @param {Object} slot Slot object
|
||||
* @param {String} group Module group/type abbrivation/code
|
||||
* @param {Integer} clazz [Optional] Module Class/Size
|
||||
* @return {Boolean} True if the slot can mount the module
|
||||
*/
|
||||
export function canMount(slot, group, clazz) {
|
||||
if (slot && (!slot.eligible || slot.eligible[group]) && (clazz === undefined || slot.maxClass >= clazz)) {
|
||||
export function canMount(ship, slot, group, clazz) {
|
||||
if (slot &&
|
||||
(!slot.eligible || slot.eligible[group]) &&
|
||||
(group != 'pcq' || (ship.luxuryCabins && ship.luxuryCabins === true)) &&
|
||||
(group != 'fh' || (ship.fighterHangars && ship.fighterHangars === true)) &&
|
||||
(clazz === undefined || slot.maxClass >= clazz)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -122,7 +127,11 @@ const PROP_BLACKLIST = {
|
||||
enginecapacity: 1,
|
||||
enginerecharge: 1,
|
||||
systemcapacity: 1,
|
||||
systemrecharge: 1
|
||||
systemrecharge: 1,
|
||||
breachdps: 1,
|
||||
breachmin: 1,
|
||||
breachmax: 1,
|
||||
integrity: 1
|
||||
};
|
||||
|
||||
const TERM_LOOKUP = {
|
||||
@@ -175,7 +184,7 @@ function diffClass(a, b, negative) {
|
||||
*/
|
||||
function diff(format, mVal, mmVal) {
|
||||
if (mVal == Infinity) {
|
||||
return <Infinite/>;
|
||||
return '∞';
|
||||
} else {
|
||||
let diff = mVal - mmVal;
|
||||
if (!diff || mVal === undefined || diff == mVal || Math.abs(diff) == Infinity) {
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
background-color: @primary-bg;
|
||||
border: 1px solid @primary-disabled;
|
||||
color: @fg;
|
||||
stroke: @fg;
|
||||
stroke-width: 20;
|
||||
fill: @fg;
|
||||
|
||||
.details-container {
|
||||
@@ -54,6 +56,8 @@
|
||||
font-size: 1.2em;
|
||||
width: 1.2em;
|
||||
color: @primary-disabled;
|
||||
stroke: @primary-disabled;
|
||||
stroke-width: 20;
|
||||
border-right: 1px solid @primary-disabled;
|
||||
box-sizing: border-box;
|
||||
padding-top: 0.2em;
|
||||
@@ -64,6 +68,8 @@
|
||||
text-transform: uppercase;
|
||||
font-size: 1.3em;
|
||||
color: lighten(@primary-bg, 12%);
|
||||
stroke: lighten(@primary-bg, 12%);
|
||||
stroke-width: 20;
|
||||
text-align: center;
|
||||
letter-spacing: 0.1em;
|
||||
line-height: 1.7em;
|
||||
@@ -71,12 +77,16 @@
|
||||
|
||||
&.selected {
|
||||
color: @primary-bg;
|
||||
stroke: @primary-bg;
|
||||
stroke-width: 20;
|
||||
fill: @primary-bg;
|
||||
background-color: @primary;
|
||||
border: 1px solid @primary;
|
||||
z-index: 1;
|
||||
.sz {
|
||||
color: @primary;
|
||||
stroke: @primary;
|
||||
stroke-width: 20;
|
||||
background-color: @primary-bg;
|
||||
border-right: 1px solid @primary;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,11 @@
|
||||
"class": { "type": "integer", "minimum": 2, "maximum": 8 },
|
||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||
"enabled": { "type": "boolean" },
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 }
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||
"name": {
|
||||
"description": "The name identifing the thrusters (if applicable), e.g. 'Enhanced Performance'",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"frameShiftDrive": {
|
||||
|
||||
Reference in New Issue
Block a user