Merge pull request #799 from EDCD/develop

Develop
This commit is contained in:
Alex Williams
2024-11-23 21:52:48 +00:00
committed by GitHub
34 changed files with 2036 additions and 1539 deletions

View File

@@ -2,7 +2,7 @@
## About ## About
The Coriolis project was inspired by [E:D Shipyard](http://www.edshipyard.com/) and, of course, [Elite Dangerous](http://www.elitedangerous.com). 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. The Coriolis project was inspired by E:D Shipyard and, of course, [Elite Dangerous](http://www.elitedangerous.com). 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.
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 and no employee of Frontier Developments was involved in the making of it. 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 and no employee of Frontier Developments was involved in the making of it.

View File

@@ -68,7 +68,8 @@ export default class Coriolis extends React.Component {
this.state = { this.state = {
noTouch: !('ontouchstart' in window || navigator.msMaxTouchPoints || navigator.maxTouchPoints), noTouch: !('ontouchstart' in window || navigator.msMaxTouchPoints || navigator.maxTouchPoints),
page: null, page: null,
announcements: [], // Announcements must have an expiry date in format "YYYY-MM-DDTHH:MM:SSZ"
announcements: [{expiry: "2024-11-30T00:00:00Z", text: "Mandalay added"}, {expiry: "2024-12-06T00:00:00Z", text: "Concord Cannon added"}, {expiry: "2024-12-08T00:00:00Z", text: "Boost Interval Feature added"}],
language: getLanguage(Persist.getLangCode()), language: getLanguage(Persist.getLangCode()),
route: {}, route: {},
sizeRatio: Persist.getSizeRatio() sizeRatio: Persist.getSizeRatio()

View File

@@ -39,13 +39,18 @@ const GRPCAT = {
'ml': 'lasers', 'ml': 'lasers',
'c': 'projectiles', 'c': 'projectiles',
'mc': 'projectiles', 'mc': 'projectiles',
'advmc': 'projectiles',
'axmc': 'experimental', 'axmc': 'experimental',
'axmce': 'experimental',
'ntp': 'experimental',
'fc': 'projectiles', 'fc': 'projectiles',
'rfl': 'experimental', 'rfl': 'experimental',
'pa': 'projectiles', 'pa': 'projectiles',
'rg': 'projectiles', 'rg': 'projectiles',
'mr': 'ordnance', 'mr': 'ordnance',
'amr': 'ordnance',
'axmr': 'experimental', 'axmr': 'experimental',
'axmre': 'experimental',
'rcpl': 'experimental', 'rcpl': 'experimental',
'dtl': 'experimental', 'dtl': 'experimental',
'tbsc': 'experimental', 'tbsc': 'experimental',
@@ -104,8 +109,8 @@ const CATEGORIES = {
// Hardpoints // Hardpoints
'lasers': ['pl', 'ul', 'bl'], 'lasers': ['pl', 'ul', 'bl'],
'projectiles': ['mc', 'c', 'fc', 'pa', 'rg'], 'projectiles': ['mc', 'advmc', 'c', 'fc', 'pa', 'rg'],
'ordnance': ['mr', 'tp', 'nl'], 'ordnance': ['mr', 'amr', 'tp', 'nl'],
// Utilities // Utilities
'sb': ['sb'], 'sb': ['sb'],
'hs': ['hs'], 'hs': ['hs'],
@@ -113,7 +118,7 @@ const CATEGORIES = {
'defence': ['ch', 'po', 'ec'], 'defence': ['ch', 'po', 'ec'],
'scanners': ['sc', 'ss', 'cs', 'kw', 'ws'], // Overloaded with internal scanners 'scanners': ['sc', 'ss', 'cs', 'kw', 'ws'], // Overloaded with internal scanners
// Experimental // Experimental
'experimental': ['axmc', 'axmr', 'rfl', 'tbrfl', 'tbsc', 'tbem', 'xs', 'sfn', 'rcpl', 'dtl', 'rsl', 'mahr',], 'experimental': ['axmc', 'axmce', 'axmr', 'axmre', 'ntp','rfl', 'tbrfl', 'tbsc', 'tbem', 'xs', 'sfn', 'rcpl', 'dtl', 'rsl', 'mahr',],
'weapon stabilizers': ['ews'], 'weapon stabilizers': ['ews'],
// Guardian // Guardian
'guardian': ['gpp', 'gpd', 'gpc', 'ggc', 'gsrp', 'gfsb', 'ghrp', 'gmrp', 'gsc'], 'guardian': ['gpp', 'gpd', 'gpc', 'ggc', 'gsrp', 'gfsb', 'ghrp', 'gmrp', 'gsc'],
@@ -213,17 +218,31 @@ export default class AvailableModulesMenu extends TranslatedComponent {
if (categories.length === 1) { if (categories.length === 1) {
// Show category header instead of group header // Show category header instead of group header
if (m && grp == m.grp) { if (m && grp == m.grp) {
// If this is a missing module/weapon, skip it
if (m.grp == "mh" || m.grp == "mm"){
continue;
} else {
list.push(<div ref={(elem) => this.groupElem = elem} key={category} list.push(<div ref={(elem) => this.groupElem = elem} key={category}
className={'select-category upp'}>{translate(category)}</div>); className={'select-category upp'}>{translate(category)}</div>);
}
} else {
if (category == "mh" || category == "mm"){
continue;
} else { } else {
list.push(<div key={category} className={'select-category upp'}>{translate(category)}</div>); list.push(<div key={category} className={'select-category upp'}>{translate(category)}</div>);
} }
}
} else { } else {
// Show category header as well as group header // Show category header as well as group header
if (!categoryHeader) { if (!categoryHeader) {
if (category == "mh" || category == "mm"){
continue;
}
else {
list.push(<div key={category} className={'select-category upp'}>{translate(category)}</div>); list.push(<div key={category} className={'select-category upp'}>{translate(category)}</div>);
categoryHeader = true; categoryHeader = true;
} }
}
if (m && grp == m.grp) { if (m && grp == m.grp) {
list.push(<div ref={(elem) => this.groupElem = elem} key={grp} list.push(<div ref={(elem) => this.groupElem = elem} key={grp}
className={'select-group cap'}>{translate(grp)}</div>); className={'select-group cap'}>{translate(grp)}</div>);
@@ -241,7 +260,11 @@ export default class AvailableModulesMenu extends TranslatedComponent {
} else if (i.mount === 'T') { } else if (i.mount === 'T') {
mount = 'Turreted'; mount = 'Turreted';
} }
const fuzz = { grp, m: i, name: `${i.class}${i.rating}${mount ? ' ' + mount : ''} ${translate(grp)}` }; let special = '';
if (typeof(i.special) !== 'undefined') {
special = `(${translate(i.special)})`;
}
const fuzz = { grp, m: i, name: `${i.class}${i.rating}${mount ? ' ' + mount : ''} ${translate(grp)} ${translate(special)}` };
fuzzy.push(fuzz); fuzzy.push(fuzz);
} }
} }
@@ -298,6 +321,11 @@ export default class AvailableModulesMenu extends TranslatedComponent {
let itemsOnThisRow = 0; let itemsOnThisRow = 0;
for (let i = 0; i < sortedModules.length; i++) { for (let i = 0; i < sortedModules.length; i++) {
let m = sortedModules[i]; let m = sortedModules[i];
// If m.grp is mh or mm, or m.symbol contains 'Missing' skip it
if (m.grp == 'mh' || m.grp == 'mm' || (typeof(m.symbol) !== 'undefined' && m.symbol.includes("Missing"))) {
// If this is a missing module, skip it
continue;
}
let mount = null; let mount = null;
let disabled = false; let disabled = false;
prevName = m.name; prevName = m.name;

View File

@@ -136,6 +136,7 @@ export default class HardpointSlot extends Slot {
{showModuleResistances && m.getThermalResistance() ? <div {showModuleResistances && m.getThermalResistance() ? <div
className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null} className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null}
{m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null} {m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null}
{m.getInfo() ? <div className='l'>{translate(m.getInfo())}</div> : null}
{m && validMods.length > 0 ? <div className='r' tabIndex="0" ref={modButton => this.modButton = modButton}> {m && validMods.length > 0 ? <div className='r' tabIndex="0" ref={modButton => this.modButton = modButton}>
<button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} <button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation}
onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}> onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}>

View File

@@ -388,9 +388,11 @@ export default class Header extends TranslatedComponent {
if (this.props.announcements) { if (this.props.announcements) {
announcements = []; announcements = [];
for (let announce of this.props.announcements) { for (let announce of this.props.announcements) {
if (announce.expiry < Date.now()) { // Announcement has expired, skip it
if (Date.now() > Date.parse(announce.expiry)) {
continue; continue;
} }
// Add announcements which have not expired to the menu
announcements.push(<Announcement text={announce.text} />); announcements.push(<Announcement text={announce.text} />);
announcements.push(<hr/>); announcements.push(<hr/>);
} }
@@ -398,7 +400,6 @@ export default class Header extends TranslatedComponent {
return ( return (
<div className='menu-list' onClick={ (e) => e.stopPropagation() } style={{ whiteSpace: 'nowrap' }}> <div className='menu-list' onClick={ (e) => e.stopPropagation() } style={{ whiteSpace: 'nowrap' }}>
{announcements} {announcements}
<hr />
</div> </div>
); );
} }

View File

@@ -88,6 +88,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.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.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.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null }
{ m.getInfo() ? <div className='l'>{translate(m.getInfo())}</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>
</div>; </div>;

View File

@@ -34,6 +34,18 @@ export default class ModalPermalink extends TranslatedComponent {
); );
} }
/**
* Copy the shortened URL to the clipboard
* @param {Event} e Click event
* @return {void}
*/
copyShortLink() {
let copyText = document.getElementById("shortenedUrl");
// Copy the text inside the shortendUrl input to the clipboard
copyText.select();
document.execCommand("copy");
}
/** /**
* Render the modal * Render the modal
* @return {React.Component} Modal Content * @return {React.Component} Modal Content
@@ -42,15 +54,17 @@ export default class ModalPermalink extends TranslatedComponent {
let translate = this.context.language.translate; let translate = this.context.language.translate;
return <div className='modal' onClick={ (e) => e.stopPropagation() }> return <div className='modal' onClick={ (e) => e.stopPropagation() }>
<h2>{translate('permalink')}</h2> <h3>{translate('permalink')}</h3>
<br/> <br/>
<h3>{translate('URL')}</h3> <h3>{translate('URL')}</h3>
<input value={this.props.url} size={40} readOnly onFocus={ (e) => e.target.select() }/> <input value={this.props.url} size={40} readOnly onFocus={ (e) => e.target.select() }/>
<br/><br/> <br/><br/>
<h3 >{translate('shortened')}</h3> <h3 >{translate('shortened')}</h3>
<input value={this.state.shortenedUrl} readOnly size={25} onFocus={ (e) => e.target.select() }/> <input id={'shortenedUrl'} value={this.state.shortenedUrl} readOnly size={25} onFocus={ (e) => e.target.select() }/><button className={'cb dismiss cap'} onClick={this.copyShortLink}>{translate('copy to clipboard')}</button>
<br/><br/> <br/><br/>
<p>s.orbis.zone is the new URL shortener domain, old eddp.co urls are considered end of life and could go down at any moment. Sorry for any inconvenience.</p> <hr />
<p>s.orbis.zone is the URL shortener domain. These links should persist indefinitely going forward. If for some reason there is a problem with the link shortening process, please report it in the EDCD Discord Server.</p>
<hr />
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button> <button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
</div>; </div>;
} }

View File

@@ -12,7 +12,8 @@ const base64url = require('base64url');
export default class ModalShoppingList extends TranslatedComponent { export default class ModalShoppingList extends TranslatedComponent {
static propTypes = { static propTypes = {
ship: PropTypes.object.isRequired ship: PropTypes.object.isRequired,
buildName: PropTypes.string
}; };
/** /**
@@ -90,8 +91,10 @@ export default class ModalShoppingList extends TranslatedComponent {
request request
.get('http://localhost:44405/commanders') .get('http://localhost:44405/commanders')
.end((err, res) => { .end((err, res) => {
this.display = 'block';
if (err) { if (err) {
console.log(err); console.log(err);
this.display = 'none';
return this.setState({ failed: true }); return this.setState({ failed: true });
} }
const cmdrs = JSON.parse(res.text); const cmdrs = JSON.parse(res.text);
@@ -147,6 +150,34 @@ export default class ModalShoppingList extends TranslatedComponent {
} }
} }
/**
* Fix issues with the item name for bulkheads when sending to EDOMH
* @param {*} ship Ship object
* @param {*} item Item name
* @returns updated item name
*/
fixArmourItemNameForEDOMH(ship, item) {
// The module blueprint fdname contains "Armour_" it's a bulkhead and we need to pre-populate the item field with the correct name from the ship object
switch (ship.bulkheads.m.name){
case "Lightweight Alloy":
item = ship.id + "_Armour_Grade1";
break;
case "Reinforced Alloy":
item = ship.id + "_Armour_Grade2";
break;
case "Military Grade Composite":
item = ship.id + "_Armour_Grade3";
break;
case "Mirrored Surface Composite":
item = ship.id + "_Armour_Mirrored";
break;
case "Reactive Surface Composite":
item = ship.id + "_Armour_Reactive";
break;
}
return item;
}
/** /**
* Send all blueprints to EDOMH. This is a modified copy of registerBPs because this.state.blueprints was empty when I tried to modify sendToEDEng and I couldn't figure out why * Send all blueprints to EDOMH. This is a modified copy of registerBPs because this.state.blueprints was empty when I tried to modify sendToEDEng and I couldn't figure out why
* @param {Event} event React event * @param {Event} event React event
@@ -154,6 +185,7 @@ export default class ModalShoppingList extends TranslatedComponent {
sendToEDOMH(event) { sendToEDOMH(event) {
event.preventDefault(); event.preventDefault();
const ship = this.props.ship; const ship = this.props.ship;
const buildName = this.props.buildName;
let blueprints = []; let blueprints = [];
//create the json //create the json
@@ -166,20 +198,38 @@ export default class ModalShoppingList extends TranslatedComponent {
continue; continue;
} }
if (module.m.blueprint.special) { if (module.m.blueprint.special) {
let item = "";
// If the module blueprint fdname contains "Armour_" it's a bulkhead and we need to pre-populate the item field with the correct name from the ship object
if (module.m.blueprint.fdname.includes("Armour_")) {
item = this.fixArmourItemNameForEDOMH(ship, item)
}
else {
item = module.m.symbol;
}
blueprints.push({ blueprints.push({
"item": module.m.symbol, "item": item,
"blueprint": module.m.blueprint.special.edname "blueprint": module.m.blueprint.special.edname
}); });
} }
for (const g in module.m.blueprint.grades) { for (let g in module.m.blueprint.grades) {
if (!module.m.blueprint.grades.hasOwnProperty(g)) { if (!module.m.blueprint.grades.hasOwnProperty(g)) {
continue; continue;
} }
if (g < module.m.blueprint.grade) { // We only want the grade that the module is currently at, not every grade up to that point
if (Number(g) !== module.m.blueprint.grade) {
continue; continue;
} }
let item = "";
// If the module blueprint fdname contains "Armour_" it's a bulkhead and we need to pre-populate the item field with the correct name from the ship object
if (module.m.blueprint.fdname.includes("Armour_")) {
item = this.fixArmourItemNameForEDOMH(ship, item)
}
else {
item = module.m.symbol;
}
blueprints.push({ blueprints.push({
"item": module.m.symbol, "item": item,
"blueprint": module.m.blueprint.fdname, "blueprint": module.m.blueprint.fdname,
"grade": module.m.blueprint.grade, "grade": module.m.blueprint.grade,
"highestGradePercentage":1.0 "highestGradePercentage":1.0
@@ -188,10 +238,12 @@ export default class ModalShoppingList extends TranslatedComponent {
} }
} }
let shipName = buildName + " - " + ship.name;
//create JSON to encode //create JSON to encode
let baseJson = { let baseJson = {
"version":1, "version":1,
"name":ship.name, // TO-DO: Import build name and put that here correctly "name": shipName, // TO-DO: Import build name and put that here correctly
"items": blueprints "items": blueprints
} }
@@ -219,14 +271,15 @@ export default class ModalShoppingList extends TranslatedComponent {
if (!module.m.blueprint.grade || !module.m.blueprint.grades) { if (!module.m.blueprint.grade || !module.m.blueprint.grades) {
continue; continue;
} }
for (const g in module.m.blueprint.grades) { for (let g in module.m.blueprint.grades) {
if (!module.m.blueprint.grades.hasOwnProperty(g)) { if (!module.m.blueprint.grades.hasOwnProperty(g)) {
continue; continue;
} }
if (g > module.m.blueprint.grade) { // Ignore grades higher than the grade selected
if (Number(g) > module.m.blueprint.grade) {
continue; continue;
} }
for (const i in module.m.blueprint.grades[g].components) { for (let i in module.m.blueprint.grades[g].components) {
if (!module.m.blueprint.grades[g].components.hasOwnProperty(i)) { if (!module.m.blueprint.grades[g].components.hasOwnProperty(i)) {
continue; continue;
} }
@@ -236,6 +289,7 @@ export default class ModalShoppingList extends TranslatedComponent {
mats[i] = module.m.blueprint.grades[g].components[i] * this.state.matsPerGrade[g]; mats[i] = module.m.blueprint.grades[g].components[i] * this.state.matsPerGrade[g];
} }
} }
}
if (module.m.blueprint.special) { if (module.m.blueprint.special) {
for (const j in module.m.blueprint.special.components) { for (const j in module.m.blueprint.special.components) {
if (!module.m.blueprint.special.components.hasOwnProperty(j)) { if (!module.m.blueprint.special.components.hasOwnProperty(j)) {
@@ -250,7 +304,6 @@ export default class ModalShoppingList extends TranslatedComponent {
} }
} }
} }
}
let matsString = ''; let matsString = '';
for (const i in mats) { for (const i in mats) {
if (!mats.hasOwnProperty(i)) { if (!mats.hasOwnProperty(i)) {
@@ -302,34 +355,46 @@ export default class ModalShoppingList extends TranslatedComponent {
this.sendToEDEng = this.sendToEDEng.bind(this); this.sendToEDEng = this.sendToEDEng.bind(this);
this.sendToEDOMH = this.sendToEDOMH.bind(this); this.sendToEDOMH = this.sendToEDOMH.bind(this);
return <div className='modal' onClick={ (e) => e.stopPropagation() }> return <div className='modal' onClick={ (e) => e.stopPropagation() }>
<h2>{translate('PHRASE_SHOPPING_MATS')}</h2> <h3>{translate('PHRASE_SHOPPING_MATS')}</h3>
<label>{translate('Grade 1 rolls ')}</label>
<input id={1} type={'number'} min={0} defaultValue={this.state.matsPerGrade[1]} onChange={this.changeHandler} />
<br/>
<label>{translate('Grade 2 rolls ')}</label>
<input id={2} type={'number'} min={0} defaultValue={this.state.matsPerGrade[2]} onChange={this.changeHandler} />
<br/>
<label>{translate('Grade 3 rolls ')}</label>
<input id={3} type={'number'} min={0} value={this.state.matsPerGrade[3]} onChange={this.changeHandler} />
<br/>
<label>{translate('Grade 4 rolls ')}</label>
<input id={4} type={'number'} min={0} value={this.state.matsPerGrade[4]} onChange={this.changeHandler} />
<br/>
<label>{translate('Grade 5 rolls ')}</label>
<input id={5} type={'number'} min={0} value={this.state.matsPerGrade[5]} onChange={this.changeHandler} />
<div> <div>
<textarea className='cb json' readOnly value={this.state.matsList} /> <p>{translate('PHRASE_DIFFERENT_ROLLS')}</p>
<label>{translate('G1')}</label>
<input className={'groll'} id={1} type={'number'} min={0} defaultValue={this.state.matsPerGrade[1]} onChange={this.changeHandler} />
&nbsp;|&nbsp;<label>{translate('G2')}</label>
<input className={'groll'} id={2} type={'number'} min={0} defaultValue={this.state.matsPerGrade[2]} onChange={this.changeHandler} />
&nbsp;|&nbsp;<label>{translate('G3')}</label>
<input className={'groll'} id={3} type={'number'} min={0} value={this.state.matsPerGrade[3]} onChange={this.changeHandler} />
&nbsp;|&nbsp;<label>{translate('G4')}</label>
<input className={'groll'} id={4} type={'number'} min={0} value={this.state.matsPerGrade[4]} onChange={this.changeHandler} />
&nbsp;|&nbsp;<label>{translate('G5')}</label>
<input className={'groll'} id={5} type={'number'} min={0} value={this.state.matsPerGrade[5]} onChange={this.changeHandler} />
</div> </div>
<label hidden={!compatible} className={'l cap'}>{translate('CMDR Name')}</label>
<br/> <div>
<select hidden={!compatible} className={'cmdr-select l cap'} onChange={this.cmdrChangeHandler} defaultValue={this.state.cmdrName}> <p>{translate('PHRASE_ALL_MODULES_ALL_ROLLS')}</p>
<textarea className='cb json' readOnly value={this.state.matsList} />
<p>{translate('PHRASE_FOR_FINER_CONTROL')}</p>
</div>
<div id='edengineer' display={this.display} hidden={!!this.state.failed && !compatible}>
<hr />
<h3>ED Engineer</h3>
<h4 hidden={compatible} id={'browserbad'} className={'l'}>{translate('PHRASE_FIREFOX_EDENGINEER')}</h4>
<h4 hidden={!this.state.failed} id={'failed'} className={'l'}>{translate('PHRASE_FAILED_TO_FIND_EDENGINEER')}</h4>
<label for='cmdr-select' hidden={!!this.state.failed || !compatible} className={'l cap'}>{translate('CMDR Name:')}</label>
<select id='cmdr-select' hidden={!!this.state.failed || !compatible} className={'cmdr-select l cap'} onChange={this.cmdrChangeHandler} defaultValue={this.state.cmdrName}>
{this.state.cmdrs.map(e => <option key={e}>{e}</option>)} {this.state.cmdrs.map(e => <option key={e}>{e}</option>)}
</select> </select>
<br/> <br/>
<p hidden={!this.state.failed} id={'failed'} className={'l'}>{translate('PHRASE_FAIL_EDENGINEER')}</p>
<p hidden={compatible} id={'browserbad'} className={'l'}>{translate('PHRASE_FIREFOX_EDENGINEER')}</p>
<button className={'l cb dismiss cap'} disabled={!!this.state.failed || !compatible} onClick={this.sendToEDEng}>{translate('Send to EDEngineer')}</button> <button className={'l cb dismiss cap'} disabled={!!this.state.failed || !compatible} onClick={this.sendToEDEng}>{translate('Send to EDEngineer')}</button>
<button style={{marginTop: 5}} className={'l cb dismiss cap'} disabled={!!this.state.failed} onClick={this.sendToEDOMH}>{translate('Send to EDOMH')}</button> </div>
<div id='edomh'>
<hr />
<h3>ED Odyssey Materials Helper</h3>
<p>{translate('PHRASE_ENSURE_EDOMH')}</p>
<button className={'l cb dismiss cap'} onClick={this.sendToEDOMH}>{translate('Send to EDOMH')}</button>
</div>
<hr />
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button> <button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
</div>; </div>;
} }

View File

@@ -57,6 +57,7 @@ export default class ModificationsMenu extends TranslatedComponent {
this.modValDidChange = false; // used to determine if component update was caused by change in modification value. this.modValDidChange = false; // used to determine if component update was caused by change in modification value.
this._handleModChange = this._handleModChange.bind(this); this._handleModChange = this._handleModChange.bind(this);
// console.log(props.m.blueprint)
this.state = { this.state = {
blueprintMenuOpened: !(props.m.blueprint && props.m.blueprint.name), blueprintMenuOpened: !(props.m.blueprint && props.m.blueprint.name),
specialMenuOpened: false specialMenuOpened: false
@@ -421,15 +422,31 @@ export default class ModificationsMenu extends TranslatedComponent {
let haveBlueprint = false; let haveBlueprint = false;
let blueprintTt; let blueprintTt;
let blueprintCv; let blueprintCv;
let bprintSearchName;
// If the fdname is Weapon_Overcharged, we need to check if it's an MC
if (m.blueprint && m.blueprint.fdname) {
// Set the bprintSearchName value to the fdname of the blueprint for this module
bprintSearchName = m.blueprint.fdname;
if (m.blueprint.fdname === 'Weapon_Overcharged') {
// If the module is a MultiCannon, we need to fix the blueprint search name, else it will find the Laser Weapon_Overcharged Blueprint and not the MC Weapon_Overcharged Blueprint
if (m.symbol.match(/MultiCannon/i)) {
// console.log(Modifications.modules[m.grp].blueprints['MC_Overcharged']);
// console.log(m.blueprint.fdname);
bprintSearchName = 'MC_Overcharged';
}
}
}
// TODO: Fix this to actually find the correct blueprint. // TODO: Fix this to actually find the correct blueprint.
if (!m.blueprint || !m.blueprint.name || !m.blueprint.fdname || !Modifications.modules[m.grp].blueprints || !Modifications.modules[m.grp].blueprints[m.blueprint.fdname]) { if (!m.blueprint || !m.blueprint.name || !m.blueprint.fdname || !Modifications.modules[m.grp].blueprints || !Modifications.modules[m.grp].blueprints[bprintSearchName]) {
this.props.ship.clearModuleBlueprint(m); this.props.ship.clearModuleBlueprint(m);
this.props.ship.clearModuleSpecial(m); this.props.ship.clearModuleSpecial(m);
} }
if (m.blueprint && m.blueprint.name && Modifications.modules[m.grp].blueprints[m.blueprint.fdname].grades[m.blueprint.grade]) { if (m.blueprint && m.blueprint.name && Modifications.modules[m.grp].blueprints[bprintSearchName].grades[m.blueprint.grade]) {
blueprintLabel = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade; blueprintLabel = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
haveBlueprint = true; 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); // console.log(haveBlueprint);
blueprintTt = blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], Modifications.modules[m.grp].blueprints[bprintSearchName].grades[m.blueprint.grade].engineers, m.grp);
blueprintCv = getPercent(m); blueprintCv = getPercent(m);
} }

View File

@@ -125,15 +125,32 @@ export default class PowerManagement extends TranslatedComponent {
retractedElem = <td className='ptr disabled upp' colSpan='2' onClick={toggleEnabled}>{translate('disabled')}</td>; retractedElem = <td className='ptr disabled upp' colSpan='2' onClick={toggleEnabled}>{translate('disabled')}</td>;
} }
// If this is a Guardian Shield Reinforcement Package or Guardian Hull Reinforcement Package, or Guardian Module Reinforcement Package, it cannot change priority
console.log(slot);
let priorityField;
if (m.symbol) {
if (m.symbol.match(/GuardianShield/i) || m.symbol.match(/GuardianHull/i) || m.symbol.match(/GuardianModule/i)) {
priorityField = <td>1</td>;
} else {
priorityField = <td>
<span className='flip ptr btn' onClick={this._priority.bind(this, slot, -1)}>&#9658;</span>
{' ' + (slot.priority + 1) + ' '}
<span className='ptr btn' onClick={this._priority.bind(this, slot, 1)}>&#9658;</span>
</td>;
}
}
else {
priorityField = <td>
<span className='flip ptr btn' onClick={this._priority.bind(this, slot, -1)}>&#9658;</span>
{' ' + (slot.priority + 1) + ' '}
<span className='ptr btn' onClick={this._priority.bind(this, slot, 1)}>&#9658;</span>
</td>;
}
powerRows.push(<tr key={i} className={cn('highlight', { disabled: !slot.enabled })}> powerRows.push(<tr key={i} className={cn('highlight', { disabled: !slot.enabled })}>
<td className='ptr' style={{ width: '1em' }} onClick={toggleEnabled}>{m.class + m.rating}</td> <td className='ptr' style={{ width: '1em' }} onClick={toggleEnabled}>{m.class + m.rating}</td>
<td className='ptr le shorten cap' onClick={toggleEnabled}>{slotName(translate, slot)}</td> <td className='ptr le shorten cap' onClick={toggleEnabled}>{slotName(translate, slot)}</td>
<td className='ptr' onClick={toggleEnabled}><u>{translate(slot.type)}</u></td> <td className='ptr' onClick={toggleEnabled}><u>{translate(slot.type)}</u></td>
<td> {priorityField}
<span className='flip ptr btn' onClick={this._priority.bind(this, slot, -1)}>&#9658;</span>
{' ' + (slot.priority + 1) + ' '}
<span className='ptr btn' onClick={this._priority.bind(this, slot, 1)}>&#9658;</span>
</td>
<td className='ri ptr' style={{ width: '3.25em' }} onClick={toggleEnabled}>{pwr(m.getPowerUsage())}</td> <td className='ri ptr' style={{ width: '3.25em' }} onClick={toggleEnabled}>{pwr(m.getPowerUsage())}</td>
<td className='ri ptr' style={{ width: '3em' }} onClick={toggleEnabled}><u>{pct(m.getPowerUsage() / ship.powerAvailable)}</u></td> <td className='ri ptr' style={{ width: '3em' }} onClick={toggleEnabled}><u>{pct(m.getPowerUsage() / ship.powerAvailable)}</u></td>
{retractedElem} {retractedElem}

View File

@@ -52,7 +52,8 @@ export default class ShipSummaryTable extends TranslatedComponent {
const boostTooltip = canBoost ? 'TT_SUMMARY_BOOST' : canThrust ? 'TT_SUMMARY_BOOST_NONFUNCTIONAL' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL'; const boostTooltip = canBoost ? 'TT_SUMMARY_BOOST' : canThrust ? 'TT_SUMMARY_BOOST_NONFUNCTIONAL' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
const canJump = ship.getSlotStatus(ship.standard[2]) == 3; const canJump = ship.getSlotStatus(ship.standard[2]) == 3;
const sgMetrics = Calc.shieldMetrics(ship, pips.sys); const sgMetrics = Calc.shieldMetrics(ship, pips.sys);
const shipBoost = canBoost ? Calc.calcBoost(ship) : 'No Boost'; const distBoost = canBoost ? Calc.calcBoost(ship) : 'No Boost';
//const shipBoost = ship.boostInterval(ship)
const restingHeat = Math.sqrt(((ship.standard[0].m.pgen * ship.standard[0].m.eff) / ship.heatCapacity) / 0.2); const restingHeat = Math.sqrt(((ship.standard[0].m.pgen * ship.standard[0].m.eff) / ship.heatCapacity) / 0.2);
const armourMetrics = Calc.armourMetrics(ship); const armourMetrics = Calc.armourMetrics(ship);
let shieldColour = 'blue'; let shieldColour = 'blue';
@@ -72,6 +73,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
<tr className='main'> <tr className='main'>
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canThrust }) }>{translate('speed')}</th> <th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canThrust }) }>{translate('speed')}</th>
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canBoost }) }>{translate('boost')}</th> <th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canBoost }) }>{translate('boost')}</th>
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_INTERVALS', { cap: 0 })}colSpan={2} className={ cn({ 'bg-warning-disabled': !canBoost }) }>{translate('boost int')}</th>
<th colSpan={5} className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('jump range')}</th> <th colSpan={5} className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('jump range')}</th>
<th rowSpan={2}>{translate('shield')}</th> <th rowSpan={2}>{translate('shield')}</th>
<th rowSpan={2}>{translate('integrity')}</th> <th rowSpan={2}>{translate('integrity')}</th>
@@ -86,10 +88,11 @@ export default class ShipSummaryTable extends TranslatedComponent {
<th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th> <th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th>
<th rowSpan={2}>{translate('crew')}</th> <th rowSpan={2}>{translate('crew')}</th>
<th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th> <th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_INTERVAL', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('boost interval')}</th>
<th rowSpan={2}>{translate('resting heat (Beta)')}</th> <th rowSpan={2}>{translate('resting heat (Beta)')}</th>
</tr> </tr>
<tr> <tr>
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_INTERVAL', { cap: 0 })} onMouseLeave={hide}>{translate('distro')}</th>
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_SHIP_BOOST_INTERVAL', { cap: 0 })} onMouseLeave={hide}>{translate('ship')}</th>
<th className={ cn({ 'lft': true, 'bg-warning-disabled': !canJump }) }>{translate('max')}</th> <th className={ cn({ 'lft': true, 'bg-warning-disabled': !canJump }) }>{translate('max')}</th>
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('unladen')}</th> <th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('unladen')}</th>
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('laden')}</th> <th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('laden')}</th>
@@ -104,6 +107,8 @@ export default class ShipSummaryTable extends TranslatedComponent {
<tr> <tr>
<td onMouseEnter={termtip.bind(null, speedTooltip, { cap: 0 })} onMouseLeave={hide}>{ canThrust ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, false))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td> <td onMouseEnter={termtip.bind(null, speedTooltip, { cap: 0 })} onMouseLeave={hide}>{ canThrust ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, false))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td onMouseEnter={termtip.bind(null, boostTooltip, { cap: 0 })} onMouseLeave={hide}>{ canBoost ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, true))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td> <td onMouseEnter={termtip.bind(null, boostTooltip, { cap: 0 })} onMouseLeave={hide}>{ canBoost ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, true))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td>{distBoost !== 'No Boost' ? formats.time(distBoost) : 'No Boost'}</td>
<td>{ship.boostInt && ship.boostInt !== 'undefined' ? formats.time(ship.boostInt) : 0 }</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{ f2(Calc.jumpRange(ship.unladenMass + ship.standard[2].m.getMaxFuelPerJump(), ship.standard[2].m, ship.standard[2].m.getMaxFuelPerJump(), ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{ f2(Calc.jumpRange(ship.unladenMass + ship.standard[2].m.getMaxFuelPerJump(), ship.standard[2].m, ship.standard[2].m.getMaxFuelPerJump(), ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
@@ -124,7 +129,6 @@ export default class ShipSummaryTable extends TranslatedComponent {
<td>{int(ship.hardness)}</td> <td>{int(ship.hardness)}</td>
<td>{ship.crew}</td> <td>{ship.crew}</td>
<td>{ship.masslock}</td> <td>{ship.masslock}</td>
<td>{shipBoost !== 'No Boost' ? formats.time(shipBoost) : 'No Boost'}</td>
<td>{formats.pct(restingHeat)}</td> <td>{formats.pct(restingHeat)}</td>
</tr> </tr>
</tbody> </tbody>

View File

@@ -99,6 +99,7 @@ export default class Slot extends TranslatedComponent {
let translate = language.translate; let translate = language.translate;
let { ship, m, enabled, dropClass, dragOver, onOpen, onChange, selected, eligible, onSelect, warning, availableModules } = this.props; let { ship, m, enabled, dropClass, dragOver, onOpen, onChange, selected, eligible, onSelect, warning, availableModules } = this.props;
let slotDetails, modificationsMarker, menu; let slotDetails, modificationsMarker, menu;
let missing = false;
if (!selected) { if (!selected) {
// If not selected then sure that modifications flag is unset // If not selected then sure that modifications flag is unset
@@ -108,6 +109,11 @@ export default class Slot extends TranslatedComponent {
if (m) { if (m) {
slotDetails = this._getSlotDetails(m, enabled, translate, language.formats, language.units); // Must be implemented by sub classes slotDetails = this._getSlotDetails(m, enabled, translate, language.formats, language.units); // Must be implemented by sub classes
modificationsMarker = JSON.stringify(m); modificationsMarker = JSON.stringify(m);
if(typeof m.grp !== 'undefined' || m.grp !== null) {
if(m.grp == "mh" || m.grp == "mm") {
missing = true;
}
}
} else { } else {
slotDetails = <div className={'empty'}>{translate(eligible ? 'emptyrestricted' : 'empty')}</div>; slotDetails = <div className={'empty'}>{translate(eligible ? 'emptyrestricted' : 'empty')}</div>;
modificationsMarker = ''; modificationsMarker = '';
@@ -141,7 +147,10 @@ export default class Slot extends TranslatedComponent {
return ( return (
<div className={cn('slot', dropClass, { selected })} onClick={onOpen} onKeyDown={this._keyDown} onContextMenu={this._contextMenu} onDragOver={dragOver} tabIndex="0" ref={slotDiv => this.slotDiv = slotDiv}> <div className={cn('slot', dropClass, { selected })} onClick={onOpen} onKeyDown={this._keyDown} onContextMenu={this._contextMenu} onDragOver={dragOver} tabIndex="0" ref={slotDiv => this.slotDiv = slotDiv}>
<div className='details-container'> {
// If missing module/hardpoint, set the div container to warning status.
}
<div className={ missing === true ? 'details-container warning' : 'details-container'}>
<div className='sz'>{this._getMaxClassLabel(translate)}</div> <div className='sz'>{this._getMaxClassLabel(translate)}</div>
{slotDetails} {slotDetails}
</div> </div>

View File

@@ -93,6 +93,11 @@ export default class StandardSlot extends TranslatedComponent {
this._modificationsSelected = false; this._modificationsSelected = false;
} }
// If this is a missing module, therefore has the 'info' field, set the warning value on the module to be true when loaded.
if (m.info) {
warning = () => true;
}
const modificationsMarker = JSON.stringify(m); const modificationsMarker = JSON.stringify(m);
if (selected) { if (selected) {
@@ -124,14 +129,14 @@ export default class StandardSlot extends TranslatedComponent {
<div className={cn('details-container', { warning: warning && warning(slot.m), disabled: m.grp !== 'bh' && !slot.enabled })}> <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={'sz'}>{m.grp == 'bh' ? m.name.charAt(0) : slot.maxClass}</div>
<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} {m.getInfo() ? translate(m.ukName) : 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={'r'}>{formats.round(mass)}{units.T}</div> <div className={'r'}>{formats.round(mass)}{units.T}</div>
<div/> <div/>
<div className={'cb'}> <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('minmass')}: {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.getOptMass() ? <div className='l'>{translate('optmass')}: {formats.int(m.getOptMass())}{units.T}</div> : null }
{ m.getMaxMass() ? <div className='l'>{translate('maxmass')}: {formats.int(m.getMaxMass())}{units.T}</div> : null } { m.getMaxMass() ? <div className='l'>{translate('maxmass')}: {formats.int(m.getMaxMass())}{units.T}</div> : null }
{ m.getOptMul() ? <div className='l'>{translate('optimal multiplier')}: {formats.rPct(m.getOptMul())}</div> : null } { m.getOptMul() ? <div className='l'>{translate('optmul')}: {formats.rPct(m.getOptMul())}</div> : null }
{ m.getRange() ? <div className='l'>{translate('range', m.grp)}: {formats.f2(m.getRange())}{units.km}</div> : null } { m.getRange() ? <div className='l'>{translate('range', m.grp)}: {formats.f2(m.getRange())}{units.km}</div> : null }
{ m.time ? <div className='l'>{translate('time')}: {formats.time(m.time)}</div> : null } { m.time ? <div className='l'>{translate('time')}: {formats.time(m.time)}</div> : null }
{ m.getThermalEfficiency() ? <div className='l'>{translate('efficiency')}: {formats.f2(m.getThermalEfficiency())}</div> : null } { m.getThermalEfficiency() ? <div className='l'>{translate('efficiency')}: {formats.f2(m.getThermalEfficiency())}</div> : null }
@@ -144,7 +149,8 @@ export default class StandardSlot extends TranslatedComponent {
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</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 } { 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 } { 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.getInfo() ? <div className='l'>{translate(m.getInfo())}</div> : null }
{ m.getInfo() ? <div className='r'></div> : 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> </div>
</div> </div>

View File

@@ -209,7 +209,7 @@ export default class StandardSlotSection extends SlotSection {
selected={currentMenu == st[4]} selected={currentMenu == st[4]}
onChange={this.props.onChange} onChange={this.props.onChange}
ship={ship} ship={ship}
warning={m => m instanceof Module ? m.getEnginesCapacity() <= ship.boostEnergy : m.engcap <= ship.boostEnergy} warning={m => m instanceof Module ? m.getEnginesCapacity() < ship.boostEnergy : m.engcap < ship.boostEnergy}
/>; />;
slots[6] = <StandardSlot slots[6] = <StandardSlot

View File

@@ -26,6 +26,9 @@
"PHRASE_NO_SPECIAL": "No experimental effect", "PHRASE_NO_SPECIAL": "No experimental effect",
"PHRASE_SHOPPING_LIST": "Stations that sell this build", "PHRASE_SHOPPING_LIST": "Stations that sell this build",
"PHRASE_SHOPPING_MATS": "Materials needed for this build", "PHRASE_SHOPPING_MATS": "Materials needed for this build",
"PHRASE_DIFFERENT_ROLLS": "Material requirements will differ, based on the number of rolls per grade, per module.",
"PHRASE_FOR_FINER_CONTROL": "For finer control over rolls per grade/module, a more accurate list of required mats and help in finding those mats, please consider using the export options below to ED Odyssey Materials Helper, or ED Engineer, or use the Crafting Lists feature on inara.cz.",
"PHRASE_ALL_MODULES_ALL_ROLLS": "The list below, assumes standard to G5 Engineered (approx 80% - 90%) with the rolls above, for ALL engineered modules in the build. You can adjust the number of rolls above for each grade, however it will still apply to all engineered modules in the build.",
"PHRASE_REFIT_SHOPPING_LIST": "Stations that sell required modules", "PHRASE_REFIT_SHOPPING_LIST": "Stations that sell required modules",
"PHRASE_TOTAL_EFFECTIVE_SHIELD": "Total amount of damage that can be taken from each damage type, if using all shield cells", "PHRASE_TOTAL_EFFECTIVE_SHIELD": "Total amount of damage that can be taken from each damage type, if using all shield cells",
"PHRASE_TIME_TO_LOSE_SHIELDS": "Shields will hold for", "PHRASE_TIME_TO_LOSE_SHIELDS": "Shields will hold for",
@@ -63,7 +66,9 @@
"TT_SUMMARY_SPEED": "With full fuel tank and 4 pips to ENG", "TT_SUMMARY_SPEED": "With full fuel tank and 4 pips to ENG",
"TT_SUMMARY_SPEED_NONFUNCTIONAL": "Thrusters powered off or over maximum mass with full fuel and cargo loads", "TT_SUMMARY_SPEED_NONFUNCTIONAL": "Thrusters powered off or over maximum mass with full fuel and cargo loads",
"TT_SUMMARY_BOOST": "With full fuel tank and 4 pips to ENG", "TT_SUMMARY_BOOST": "With full fuel tank and 4 pips to ENG",
"TT_SUMMARY_BOOST_INTERVAL": "Time between each boost with 4 pips to ENG", "TT_SUMMARY_SHIP_BOOST_INTERVAL": "Time each boost lasts on this ship",
"TT_SUMMARY_BOOST_INTERVAL": "Time between boosts with 4 pips to ENG",
"TT_SUMMARY_BOOST_INTERVALS": "If DISTRO is less than or equal to SHIP, the ship can boost continuously",
"TT_SUMMARY_BOOST_NONFUNCTIONAL": "Power distributor not able to supply enough power to boost", "TT_SUMMARY_BOOST_NONFUNCTIONAL": "Power distributor not able to supply enough power to boost",
"TT_SUMMARY_SHIELDS": "Raw shield strength, including boosters", "TT_SUMMARY_SHIELDS": "Raw shield strength, including boosters",
"TT_SUMMARY_SHIELDS_SCB": "Raw shield strength, including boosters and SCBs", "TT_SUMMARY_SHIELDS_SCB": "Raw shield strength, including boosters and SCBs",
@@ -81,8 +86,10 @@
"TT_SUMMARY_UNLADEN_TOTAL_JUMP": "Farthest possible range with no cargo, a full fuel tank, and jumping as far as possible each time", "TT_SUMMARY_UNLADEN_TOTAL_JUMP": "Farthest possible range with no cargo, a full fuel tank, and jumping as far as possible each time",
"TT_SUMMARY_LADEN_TOTAL_JUMP": "Farthest possible range with full cargo, a full fuel tank, and jumping as far as possible each time", "TT_SUMMARY_LADEN_TOTAL_JUMP": "Farthest possible range with full cargo, a full fuel tank, and jumping as far as possible each time",
"HELP_MODIFICATIONS_MENU": "Click on a number to enter a new value, or drag along the bar for small changes", "HELP_MODIFICATIONS_MENU": "Click on a number to enter a new value, or drag along the bar for small changes",
"PHRASE_FAIL_EDENGINEER": "Failed to send to EDEngineer (Launch EDEngineer and make sure the API is started then refresh the page.)", "PHRASE_ENSURE_EDOMH": "Ensure ED Odyssey Material Helper is installed and registered to handle edomh:// urls, else this button will do nothing!",
"PHRASE_FIREFOX_EDENGINEER": "Sending to EDEngineer is not compatible with Firefox's security settings. Please try again with Chrome.", "PHRASE_FIREFOX_EDENGINEER": "Sending to EDEngineer is not compatible with Firefox's security settings. Please try again with Chrome.",
"PHRASE_FAILED_TO_FIND_EDENGINEER": "Failed to find ED Engineer API. Please ensure it is running and try again.",
"MISSING_MODULES": "Missing Modules",
"am": "Auto Field-Maintenance Unit", "am": "Auto Field-Maintenance Unit",
"bh": "Bulkheads", "bh": "Bulkheads",
"bl": "Beam Laser", "bl": "Beam Laser",
@@ -109,11 +116,17 @@
"kw": "Kill Warrant Scanner", "kw": "Kill Warrant Scanner",
"ls": "Life Support", "ls": "Life Support",
"mc": "Multi-cannon", "mc": "Multi-cannon",
"mh": "Missing Weapon/Utility",
"mm": "Missing Module",
"advmc": "Multi-cannon (Advanced)",
"axmc": "AX Multi-cannon", "axmc": "AX Multi-cannon",
"axmce": "AX Multi-cannon (Enhanced)",
"ml": "Mining Laser", "ml": "Mining Laser",
"mlc": "Multi Limpet Controller", "mlc": "Multi Limpet Controller",
"mr": "Missile Rack", "mr": "Missile Rack",
"amr": "Missile Rack (Advanced)",
"axmr": "AX Missile Rack", "axmr": "AX Missile Rack",
"axmre": "AX Missile Rack (Enhanced)",
"ews": "Experimental Weapon Stabilizer", "ews": "Experimental Weapon Stabilizer",
"mrp": "Module Reinforcement Package", "mrp": "Module Reinforcement Package",
"nl": "Mine Launcher", "nl": "Mine Launcher",
@@ -159,6 +172,7 @@
"sua": "Supercruise Assist", "sua": "Supercruise Assist",
"t": "thrusters", "t": "thrusters",
"tp": "Torpedo Pylon", "tp": "Torpedo Pylon",
"ntp": "Nanite Torpedo Pylon",
"ul": "Burst Laser", "ul": "Burst Laser",
"Send To EDEngineer": "Send To EDEngineer", "Send To EDEngineer": "Send To EDEngineer",
"Send To EDOMH": "Send To EDOMH", "Send To EDOMH": "Send To EDOMH",
@@ -212,6 +226,7 @@
"boost interval": "Boost interval", "boost interval": "Boost interval",
"total": "Total", "total": "Total",
"ammo": "Ammunition maximum", "ammo": "Ammunition maximum",
"info": "Info",
"boot": "Boot time", "boot": "Boot time",
"hacktime": "Hack time", "hacktime": "Hack time",
"brokenregen": "Broken regeneration rate", "brokenregen": "Broken regeneration rate",
@@ -261,8 +276,10 @@
"thermres": "Thermal resistance", "thermres": "Thermal resistance",
"wepcap": "Weapons capacity", "wepcap": "Weapons capacity",
"weprate": "Weapons recharge rate", "weprate": "Weapons recharge rate",
"minmass": "Minimum mass",
"minmass_sg": "Minimum hull mass", "minmass_sg": "Minimum hull mass",
"optmass_sg": "Optimal hull mass", "optmass_sg": "Optimal hull mass",
"maxmass": "Maximum mass",
"maxmass_sg": "Maximum hull mass", "maxmass_sg": "Maximum hull mass",
"minmul_sg": "Minimum strength", "minmul_sg": "Minimum strength",
"optmul_sg": "Optimal strength", "optmul_sg": "Optimal strength",

File diff suppressed because one or more lines are too long

View File

@@ -45,6 +45,9 @@ export default class ErrorDetails extends React.Component {
return <div className='error'> return <div className='error'>
<h1>Jameson, we have a problem..</h1> <h1>Jameson, we have a problem..</h1>
<h1><small>{error.message}</small></h1> <h1><small>{error.message}</small></h1>
Import Error handling has been improved, but still isn't perfect. <br/>MOST Import failures are a result of missing modules in Coriolis, <br />OR incorrect import strings generated by third party apps. If you're seeing this page, we may have failed to handle the errors in your import correctly. Please see the data output below, specifically the 'scriptUrl:' section if it's there and then if you feel confident enough, please check the github issues page linked below and see if there is a similar issue already logged. If not, please create a new issue with the data below. If you're not confident, please ask for help on the Coriolis Channel of the EDCD Discord server.
<br/>
<br/>
<br/> <br/>
{importerror ? <div>If you are attempting to import a ship from EDDI or EDMC and are seeing a 'Z_BUF_ERROR' it means that the URL has not been provided correctly. This is a common problem when using Microsoft Internet Explorer or Microsoft Edge, and you should use another browser instead.</div> : null } {importerror ? <div>If you are attempting to import a ship from EDDI or EDMC and are seeing a 'Z_BUF_ERROR' it means that the URL has not been provided correctly. This is a common problem when using Microsoft Internet Explorer or Microsoft Edge, and you should use another browser instead.</div> : null }
<br/> <br/>

View File

@@ -700,7 +700,9 @@ export default class OutfittingPage extends Page {
* Generates the shopping list * Generates the shopping list
*/ */
_genShoppingList() { _genShoppingList() {
this.context.showModal(<ModalShoppingList ship={this.state.ship} />); this.context.showModal(<ModalShoppingList
ship={this.state.ship}
buildName={this.state.buildName} />);
} }
/** /**

View File

@@ -25,7 +25,6 @@ export const ModuleGroupToName = {
pd: 'Power Distributor', pd: 'Power Distributor',
s: 'Sensors', s: 'Sensors',
ft: 'Fuel Tank', ft: 'Fuel Tank',
pas: 'Planetary Approach Suite',
// Internal // Internal
fs: 'Fuel Scoop', fs: 'Fuel Scoop',
@@ -60,6 +59,7 @@ export const ModuleGroupToName = {
sua: 'Supercruise Assist', sua: 'Supercruise Assist',
mlc: "Multi Limpet Controller", mlc: "Multi Limpet Controller",
rpl: "Repair Limpet Controller", rpl: "Repair Limpet Controller",
pas: 'Planetary Approach Suite',
// Hard Points // Hard Points
bl: 'Beam Laser', bl: 'Beam Laser',
@@ -77,11 +77,15 @@ export const ModuleGroupToName = {
nl: 'Mine Launcher', nl: 'Mine Launcher',
ml: 'Mining Laser', ml: 'Mining Laser',
mr: 'Missile Rack', mr: 'Missile Rack',
amr: 'Missile Rack (Advanced)',
axmr: 'AX Missile Rack', axmr: 'AX Missile Rack',
axmre: 'AX Missile Rack (Enhanced)',
pa: 'Plasma Accelerator', pa: 'Plasma Accelerator',
po: 'Point Defence', po: 'Point Defence',
mc: 'Multi-cannon', mc: 'Multi-cannon',
advmc: 'Multi-cannon (Advanced)',
axmc: 'AX Multi-cannon', axmc: 'AX Multi-cannon',
axmce: 'AX Multi-cannon (Enhanced)',
pl: 'Pulse Laser', pl: 'Pulse Laser',
rg: 'Rail Gun', rg: 'Rail Gun',
sb: 'Shield Booster', sb: 'Shield Booster',

View File

@@ -439,6 +439,15 @@ export default class Module {
return this.get('integrity', modified); return this.get('integrity', modified);
} }
/**
* Get the info of this module
* @param {Boolean} [modified=false] Whether to take modifications into account
* @return {String} the info of this module
*/
getInfo(modified = false) {
return (modified && this.getModValue('info')) || this.info;
}
/** /**
* Get the mass of this module * Get the mass of this module
* @param {Boolean} [modified=true] Whether to take modifications into account * @param {Boolean} [modified=true] Whether to take modifications into account

View File

@@ -140,7 +140,7 @@ export default class Ship {
*/ */
canBoost(cargo, fuel) { canBoost(cargo, fuel) {
return this.canThrust(cargo, fuel) && // Thrusters operational return this.canThrust(cargo, fuel) && // Thrusters operational
this.standard[4].m.getEnginesCapacity() > this.boostEnergy; // PD capacitor is sufficient for boost this.standard[4].m.getEnginesCapacity() >= this.boostEnergy; // PD capacitor is sufficient for boost
} }
/** /**

View File

@@ -108,9 +108,9 @@ export class Persist extends EventEmitter {
this.matsPerGrade = matsPerGrade || { this.matsPerGrade = matsPerGrade || {
1: 2, 1: 2,
2: 2, 2: 2,
3: 4, 3: 3,
4: 4, 4: 4,
5: 10 5: 5
}; };
this.cmdrName = cmdrName || { selected: '', cmdrs: [] }; this.cmdrName = cmdrName || { selected: '', cmdrs: [] };
this.tooltipsEnabled = tips === null ? true : tips; this.tooltipsEnabled = tips === null ? true : tips;

View File

@@ -307,10 +307,22 @@ export function isChangeValueBeneficial(feature, value) {
* @returns {Object} The matching blueprint * @returns {Object} The matching blueprint
*/ */
export function getBlueprint(name, module) { export function getBlueprint(name, module) {
// Special case for multi-cannons. Conflicting 'Weapon_Overcharged' Blueprints exist due to FD's naming conventions. If this blueprint is for a multi-cannon, we need to use the correct blueprint.
if (name === 'weapon_overcharged') {
if (module.symbol.match(/MultiCannon/i)) {
name = 'mc_overcharged';
}
}
else if (name === 'Weapon_Overcharged') {
if (module.symbol.match(/MultiCannon/i)) {
name = 'MC_Overcharged';
}
}
// Start with a copy of the blueprint // Start with a copy of the blueprint
const findMod = val => Object.keys(Modifications.blueprints).find(elem => elem.toString().toLowerCase().search(val.toString().toLowerCase().replace(/(OutfittingFieldType_|persecond)/igm, '')) >= 0); const findMod = val => Object.keys(Modifications.blueprints).find(elem => elem.toString().toLowerCase().search(val.toString().toLowerCase().replace(/(OutfittingFieldType_|persecond)/igm, '')) >= 0);
const found = Modifications.blueprints[findMod(name)]; const found = Modifications.blueprints[findMod(name)];
if (!found || !found.fdname) { if (!found || !found.fdname) {
console.error('Blueprint not found:', name);
return {}; return {};
} }
const blueprint = JSON.parse(JSON.stringify(found)); const blueprint = JSON.parse(JSON.stringify(found));

View File

@@ -31,6 +31,7 @@ export const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
'Independant_Trader': 'keelback', 'Independant_Trader': 'keelback',
'Krait_MkII': 'krait_mkii', 'Krait_MkII': 'krait_mkii',
'Mamba': 'mamba', 'Mamba': 'mamba',
'Mandalay': 'mandalay',
'Krait_Light': 'krait_phantom', 'Krait_Light': 'krait_phantom',
'Orca': 'orca', 'Orca': 'orca',
'Python': 'python', 'Python': 'python',
@@ -38,6 +39,7 @@ export const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
'SideWinder': 'sidewinder', 'SideWinder': 'sidewinder',
'Type6': 'type_6_transporter', 'Type6': 'type_6_transporter',
'Type7': 'type_7_transport', 'Type7': 'type_7_transport',
'Type8': 'type_8_transport',
'Type9': 'type_9_heavy', 'Type9': 'type_9_heavy',
'Type9_Military': 'type_10_defender', 'Type9_Military': 'type_10_defender',
'TypeX': 'alliance_chieftain', 'TypeX': 'alliance_chieftain',

View File

@@ -6,6 +6,22 @@ import { Modules } from 'coriolis-data/dist';
import { Modifications } from 'coriolis-data/dist'; import { Modifications } from 'coriolis-data/dist';
import { getBlueprint, setQualityCB } from './BlueprintFunctions'; import { getBlueprint, setQualityCB } from './BlueprintFunctions';
/**
* Check if an imported module is valid
* @param {Object} module the module to check
* @param {Object} moduleType the type of module to check
* @return {boolean} true if the module is valid
*/
function _isValidImportedModule(module, moduleType) {
// First of all, has the _moduleFromFdName function returned 'null'?
if (!module){
return false
}
else {
return true
}
}
/** /**
* Obtain a module given its FD Name * Obtain a module given its FD Name
* @param {string} fdname the FD Name of the module * @param {string} fdname the FD Name of the module
@@ -98,49 +114,90 @@ export function shipFromLoadoutJSON(json) {
if (module.Engineering) _addModifications(ship.bulkheads.m, module.Engineering.Modifiers, module.Engineering.Quality, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect); if (module.Engineering) _addModifications(ship.bulkheads.m, module.Engineering.Modifiers, module.Engineering.Quality, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
break; break;
case 'powerplant': case 'powerplant':
const powerplant = _moduleFromFdName(module.Item); let powerplant = _moduleFromFdName(module.Item);
// Check the powerplant returned is valid
if (!_isValidImportedModule(powerplant, 'powerplant'))
{
powerplant = _moduleFromFdName('Int_Missing_Powerplant');
module.Engineering = null;
}
ship.use(ship.standard[0], powerplant, true); ship.use(ship.standard[0], powerplant, true);
ship.standard[0].enabled = module.On; ship.standard[0].enabled = module.On;
ship.standard[0].priority = module.Priority; ship.standard[0].priority = module.Priority;
if (module.Engineering) _addModifications(powerplant, module.Engineering.Modifiers, module.Engineering.Quality, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect); if (module.Engineering) _addModifications(powerplant, module.Engineering.Modifiers, module.Engineering.Quality, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
break; break;
case 'mainengines': case 'mainengines':
const thrusters = _moduleFromFdName(module.Item); let thrusters = _moduleFromFdName(module.Item);
// Check the thrusters returned is valid
if (!_isValidImportedModule(thrusters, 'thrusters'))
{
thrusters = _moduleFromFdName('Int_Missing_Engine');
module.Engineering = null;
}
ship.use(ship.standard[1], thrusters, true); ship.use(ship.standard[1], thrusters, true);
ship.standard[1].enabled = module.On; ship.standard[1].enabled = module.On;
ship.standard[1].priority = module.Priority; ship.standard[1].priority = module.Priority;
if (module.Engineering) _addModifications(thrusters, module.Engineering.Modifiers, module.Engineering.Quality, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect); if (module.Engineering) _addModifications(thrusters, module.Engineering.Modifiers, module.Engineering.Quality, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
break; break;
case 'frameshiftdrive': case 'frameshiftdrive':
const frameshiftdrive = _moduleFromFdName(module.Item); let frameshiftdrive = _moduleFromFdName(module.Item);
// Check the frameshiftdrive returned is valid
if (!_isValidImportedModule(frameshiftdrive, 'frameshiftdrive'))
{
frameshiftdrive = _moduleFromFdName('Int_Missing_Hyperdrive');
module.Engineering = null;
}
ship.use(ship.standard[2], frameshiftdrive, true); ship.use(ship.standard[2], frameshiftdrive, true);
ship.standard[2].enabled = module.On; ship.standard[2].enabled = module.On;
ship.standard[2].priority = module.Priority; ship.standard[2].priority = module.Priority;
if (module.Engineering) _addModifications(frameshiftdrive, module.Engineering.Modifiers, module.Engineering.Quality, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect); if (module.Engineering) _addModifications(frameshiftdrive, module.Engineering.Modifiers, module.Engineering.Quality, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
break; break;
case 'lifesupport': case 'lifesupport':
const lifesupport = _moduleFromFdName(module.Item); let lifesupport = _moduleFromFdName(module.Item);
// Check the lifesupport returned is valid
if (!_isValidImportedModule(lifesupport, 'lifesupport'))
{
lifesupport = _moduleFromFdName('Int_Missing_LifeSupport');
module.Engineering = null;
}
ship.use(ship.standard[3], lifesupport, true); ship.use(ship.standard[3], lifesupport, true);
ship.standard[3].enabled = module.On === true; ship.standard[3].enabled = module.On === true;
ship.standard[3].priority = module.Priority; ship.standard[3].priority = module.Priority;
if (module.Engineering) _addModifications(lifesupport, module.Engineering.Modifiers, module.Engineering.Quality, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect); if (module.Engineering) _addModifications(lifesupport, module.Engineering.Modifiers, module.Engineering.Quality, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
break; break;
case 'powerdistributor': case 'powerdistributor':
const powerdistributor = _moduleFromFdName(module.Item); let powerdistributor = _moduleFromFdName(module.Item);
// Check the powerdistributor returned is valid
if (!_isValidImportedModule(powerdistributor, 'powerdistributor'))
{
powerdistributor = _moduleFromFdName('Int_Missing_PowerDistributor');
module.Engineering = null;
}
ship.use(ship.standard[4], powerdistributor, true); ship.use(ship.standard[4], powerdistributor, true);
ship.standard[4].enabled = module.On; ship.standard[4].enabled = module.On;
ship.standard[4].priority = module.Priority; ship.standard[4].priority = module.Priority;
if (module.Engineering) _addModifications(powerdistributor, module.Engineering.Modifiers, module.Engineering.Quality, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect); if (module.Engineering) _addModifications(powerdistributor, module.Engineering.Modifiers, module.Engineering.Quality, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
break; break;
case 'radar': case 'radar':
const sensors = _moduleFromFdName(module.Item); let sensors = _moduleFromFdName(module.Item);
// Check the sensors returned is valid
if (!_isValidImportedModule(sensors, 'sensors'))
{
sensors = _moduleFromFdName('Int_Missing_Sensors');
module.Engineering = null;
}
ship.use(ship.standard[5], sensors, true); ship.use(ship.standard[5], sensors, true);
ship.standard[5].enabled = module.On; ship.standard[5].enabled = module.On;
ship.standard[5].priority = module.Priority; ship.standard[5].priority = module.Priority;
if (module.Engineering) _addModifications(sensors, module.Engineering.Modifiers, module.Engineering.Quality, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect); if (module.Engineering) _addModifications(sensors, module.Engineering.Modifiers, module.Engineering.Quality, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
break; break;
case 'fueltank': case 'fueltank':
const fueltank = _moduleFromFdName(module.Item); let fueltank = _moduleFromFdName(module.Item);
// Check the fueltank returned is valid
if (!_isValidImportedModule(fueltank, 'fueltank'))
{
fueltank = _moduleFromFdName('Int_Missing_FuelTank');
}
ship.use(ship.standard[6], fueltank, true); ship.use(ship.standard[6], fueltank, true);
ship.standard[6].enabled = true; ship.standard[6].enabled = true;
ship.standard[6].priority = 0; ship.standard[6].priority = 0;
@@ -170,11 +227,28 @@ export function shipFromLoadoutJSON(json) {
// This can happen with old imports that don't contain new hardpoints // This can happen with old imports that don't contain new hardpoints
} else { } else {
hardpoint = _moduleFromFdName(hardpointSlot.Item); hardpoint = _moduleFromFdName(hardpointSlot.Item);
// Check the hardpoint module returned is valid
if (!_isValidImportedModule(hardpoint, 'hardpoint')){
// Check if it's a Utility or Hardpoint
if (hardpointSlot.Slot.toLowerCase().search(/tiny/))
{
// Use the missing_hardpoint module 'Missing Hardpoint' which will inform the user that the module is missing
hardpoint = _moduleFromFdName('Hpt_Missing_Hardpoint');
}
else {
// Use the missing_hardpoint module 'Missing Utility' which will inform the user that the module is missing
hardpoint = _moduleFromFdName('Hpt_Missing_Utility');
}
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true);
ship.hardpoints[hardpointArrayNum].enabled = hardpointSlot.On;
ship.hardpoints[hardpointArrayNum].priority = hardpointSlot.Priority;
} else {
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true); ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true);
ship.hardpoints[hardpointArrayNum].enabled = hardpointSlot.On; ship.hardpoints[hardpointArrayNum].enabled = hardpointSlot.On;
ship.hardpoints[hardpointArrayNum].priority = hardpointSlot.Priority; ship.hardpoints[hardpointArrayNum].priority = hardpointSlot.Priority;
modsToAdd.push({ coriolisMod: hardpoint, json: hardpointSlot }); modsToAdd.push({ coriolisMod: hardpoint, json: hardpointSlot });
} }
}
hardpointArrayNum++; hardpointArrayNum++;
} }
} }
@@ -187,13 +261,17 @@ export function shipFromLoadoutJSON(json) {
continue; continue;
} }
const isMilitary = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].name == 'Military' : false; const isMilitary = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].name == 'Military' : false;
const isPlanetary = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].name == 'PlanetaryApproachSuite' : false;
// The internal slot might be a standard or a military slot. Military slots have a different naming system // The internal slot might be a standard or a military slot, or a planetary slot. Military and Planetary slots have a different naming system
let internalSlot = null; let internalSlot = null;
if (isMilitary) { if (isMilitary) {
const internalName = 'Military0' + militarySlotNum; const internalName = 'Military0' + militarySlotNum;
internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase()); internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase());
militarySlotNum++; militarySlotNum++;
} else if (isPlanetary) {
const internalName = 'PlanetaryApproachSuite';
internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase());
} else { } else {
// Slot numbers are not contiguous so handle skips. // Slot numbers are not contiguous so handle skips.
for (; internalSlot === null && internalSlotNum < 99; internalSlotNum++) { for (; internalSlot === null && internalSlotNum < 99; internalSlotNum++) {
@@ -212,13 +290,24 @@ export function shipFromLoadoutJSON(json) {
// This can happen with old imports that don't contain new slots // This can happen with old imports that don't contain new slots
} else { } else {
const internalJson = internalSlot; const internalJson = internalSlot;
const internal = _moduleFromFdName(internalJson.Item); let internal = _moduleFromFdName(internalJson.Item);
// Check the internal module returned is valid
if (!_isValidImportedModule(internal, 'internal'))
{
internal = _moduleFromFdName('Int_Missing_Module');
ship.use(ship.internal[i], internal, true);
ship.internal[i].enabled = internalJson.On === true;
ship.internal[i].priority = internalJson.Priority;
//throw 'Unknown internal module: "' + module.Item + '"';
}
else {
ship.use(ship.internal[i], internal, true); ship.use(ship.internal[i], internal, true);
ship.internal[i].enabled = internalJson.On === true; ship.internal[i].enabled = internalJson.On === true;
ship.internal[i].priority = internalJson.Priority; ship.internal[i].priority = internalJson.Priority;
modsToAdd.push({ coriolisMod: internal, json: internalSlot }); modsToAdd.push({ coriolisMod: internal, json: internalSlot });
} }
} }
}
for (const i of modsToAdd) { for (const i of modsToAdd) {
if (i.json.Engineering) { if (i.json.Engineering) {
@@ -260,6 +349,7 @@ function _addModifications(module, modifiers, quality, blueprint, grade, special
// Add the blueprint definition, grade and special // Add the blueprint definition, grade and special
if (blueprint) { if (blueprint) {
module.blueprint = getBlueprint(blueprint, module); module.blueprint = getBlueprint(blueprint, module);
if (grade) { if (grade) {
module.blueprint.grade = Number(grade); module.blueprint.grade = Number(grade);
} }

View File

@@ -183,4 +183,5 @@ footer {
.announcement { .announcement {
border: 1px @secondary solid; border: 1px @secondary solid;
padding: 10px; padding: 10px;
margin: 5px;
} }

View File

@@ -41,6 +41,48 @@
h2 { h2 {
margin: 0; margin: 0;
} }
h4 {
text-transform: uppercase;
font-family: @fStandard;
font-weight: normal;
font-size: 1em;
margin: 1em;
color: @warning;
}
p {
margin: 1em;
text-wrap: pretty;
}
button {
clear: bottom;
margin: 5px;
}
hr {
clear: both;
margin: 15px, 10px, 15px, 10px;
padding: top, 5;
}
select {
clear: bottom;
margin: 10px;
width: 50%;
}
label {
clear: bottom;
margin: 20px;
color: @primary;
}
.groll {
width: 6%;
margin: 5px;
text-align: center;
} }
textarea { textarea {
@@ -55,10 +97,11 @@ textarea {
min-height: 10em; min-height: 10em;
resize: vertical; resize: vertical;
user-select: text; user-select: text;
margin:2em 0; margin:1em 0;
} }
} }
.dismiss { .dismiss {
background-color: @primary-bg; background-color: @primary-bg;
} }
}

View File

@@ -5,6 +5,7 @@ const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { InjectManifest } = require('workbox-webpack-plugin'); const { InjectManifest } = require('workbox-webpack-plugin');
const { max } = require('lodash');
module.exports = merge(common, { module.exports = merge(common, {
// devtool: 'source-map', // devtool: 'source-map',