import React from 'react'; import Page from './Page'; import { Ships } from 'coriolis-data/dist'; import cn from 'classnames'; import Ship from '../shipyard/Ship'; import * as ModuleUtils from '../shipyard/ModuleUtils'; import { SizeMap } from '../shipyard/Constants'; import Link from '../components/Link'; /** * Counts the hardpoints by class/size * @param {Object} slot Hardpoint Slot model */ function countHp(slot) { this.hp[slot.maxClass]++; this.hpCount++; } /** * Counts the internal slots and aggregated properties * @param {Object} slot Internal Slots */ function countInt(slot) { let crEligible = !slot.eligible || slot.eligible.cr; this.int[slot.maxClass - 1]++; // Subtract 1 since there is no Class 0 Internal compartment this.intCount++; this.maxCargo += crEligible ? ModuleUtils.findInternal('cr', slot.maxClass, 'E').cargo : 0; // if no eligiblity, then assume pce let passSlotType = null; let passSlotRating = null; if (!slot.eligible || slot.eligible.pce) { passSlotType = 'pce'; passSlotRating = 'E'; } else if (slot.eligible.pci) { passSlotType = 'pci'; passSlotRating = 'D'; } else if (slot.eligible.pcm) { passSlotType = 'pcm'; passSlotRating = 'C'; } else if (slot.eligible.pcq) { passSlotType = 'pcq'; passSlotRating = 'B'; } let passengerBay = passSlotType ? ModuleUtils.findInternal(passSlotType, slot.maxClass, passSlotRating) : null; this.maxPassengers += passengerBay ? passengerBay.passengers : 0; } /** * Generate Ship summary and aggregated properties * @param {String} shipId Ship Id * @param {Object} shipData Ship Default Data * @return {Object} Ship summary and aggregated properties */ function shipSummary(shipId, shipData) { let summary = { id: shipId, hpCount: 0, intCount: 0, maxCargo: 0, maxPassengers: 0, hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8 standard: shipData.slots.standard, agility: shipData.properties.pitch + shipData.properties.yaw + shipData.properties.roll }; Object.assign(summary, shipData.properties); let ship = new Ship(shipId, shipData.properties, shipData.slots); // Build Ship ship.buildWith(shipData.defaults); // Populate with stock/default components ship.hardpoints.forEach(countHp.bind(summary)); // Count Hardpoints by class ship.internal.forEach(countInt.bind(summary)); // Count Internal Compartments by class summary.retailCost = ship.totalCost; // Record Stock/Default/retail cost ship.optimizeMass({ pd: '1D' }); // Optimize Mass with 1D PD for maximum possible jump range summary.maxJumpRange = ship.unladenRange; // Record Jump Range // Best thrusters let th; if (ship.standard[1].maxClass === 3) { th = 'tz'; } else if (ship.standard[1].maxClass === 2) { th = 'u0'; } else { th = ship.standard[1].maxClass + 'A'; } ship.optimizeMass({ th, fsd: '2D', ft: '1C' }); // Optmize mass with Max Thrusters summary.topSpeed = ship.topSpeed; summary.topBoost = ship.topBoost; summary.baseArmour = ship.armour; return summary; } /** * The Shipyard summary page */ export default class ShipyardPage extends Page { static cachedShipSummaries = null; /** * Constructor * @param {Object} props React Component properties * @param {Object} context React Component context */ constructor(props, context) { super(props, context); if (!ShipyardPage.cachedShipSummaries) { ShipyardPage.cachedShipSummaries = []; for (let s in Ships) { ShipyardPage.cachedShipSummaries.push(shipSummary(s, Ships[s])); } } this.state = { title: 'Coriolis EDCD Edition - Shipyard', shipPredicate: 'name', shipDesc: true, shipSummaries: ShipyardPage.cachedShipSummaries }; } /** * Higlight the current ship in the table * @param {String} shipId Ship Id * @param {SyntheticEvent} event Event */ _highlightShip(shipId, event) { event.stopPropagation(); this.setState({ shipId }); } /** * Update state with the specified sort predicates * @param {String} shipPredicate Sort predicate - property name * @param {number} shipPredicateIndex Sort predicate - property index */ _sortShips(shipPredicate, shipPredicateIndex) { let shipDesc = this.state.shipDesc; if (typeof shipPredicateIndex == 'object') { shipPredicateIndex = undefined; } if (this.state.shipPredicate == shipPredicate && this.state.shipPredicateIndex == shipPredicateIndex) { shipDesc = !shipDesc; } this.setState({ shipPredicate, shipDesc, shipPredicateIndex }); }; /** * Generate the table row summary for the ship * @param {Object} s Ship summary * @param {Function} translate Translate function * @param {Object} u Localized unit map * @param {Function} fInt Localized integer formatter * @param {Function} fRound Localized round formatter * @param {Boolean} highlight Should this row be highlighted * @return {React.Component} Table Row */ _shipRowElement(s, translate, u, fInt, fRound, highlight) { let noTouch = this.context.noTouch; return {s.manufacturer} {fInt(s.retailCost)} {translate(SizeMap[s.class])} {fInt(s.crew)} {s.masslock} {fInt(s.agility)} {fInt(s.hardness)} {fInt(s.hullMass)} {fInt(s.speed)} {fInt(s.boost)} {fInt(s.baseArmour)} {fInt(s.baseShieldStrength)} {fInt(s.topSpeed)} {fInt(s.topBoost)} {fRound(s.maxJumpRange)} {fInt(s.maxCargo)} {fInt(s.maxPassengers)} {s.standard[0]} {s.standard[1]} {s.standard[2]} {s.standard[3]} {s.standard[4]} {s.standard[5]} {s.standard[6]} {s.hp[1]} {s.hp[2]} {s.hp[3]} {s.hp[4]} {s.hp[0]} {s.int[0]} {s.int[1]} {s.int[2]} {s.int[3]} {s.int[4]} {s.int[5]} {s.int[6]} {s.int[7]} ; } /** * Render the Page * @return {React.Component} The page contents */ renderPage() { let { sizeRatio, language, termtip, noTouch } = this.context; let { translate, formats, units } = language; let hide = this.context.tooltip.bind(null, null); let fInt = formats.int; let fRound = formats.round; let { shipSummaries, shipPredicate, shipPredicateIndex } = this.state; let sortShips = (predicate, index) => this._sortShips.bind(this, predicate, index); let filters = { // 'class': { 1: 1, 2: 1} }; shipSummaries = shipSummaries.filter(s => { for (let prop in filters) { if (!(s[prop] in filters[prop])) { return false; } } return true; }); // Sort shipsOverview shipSummaries.sort((a, b) => { let valA = a[shipPredicate], valB = b[shipPredicate]; if (shipPredicateIndex != undefined) { valA = valA[shipPredicateIndex]; valB = valB[shipPredicateIndex]; } if (!this.state.shipDesc) { let val = valA; valA = valB; valB = val; } if(valA == valB) { if (a.name > b.name) { return 1; } else { return -1; } } else if (valA > valB) { return 1; } else { return -1; } }); let i = 0; let shipRows = new Array(shipSummaries.length); let detailRows = new Array(shipSummaries.length); let lastShipSortValue = null; let backgroundHighlight = false; for (let s of shipSummaries) { let shipSortValue = s[shipPredicate]; if( shipPredicateIndex != undefined ) { shipSortValue = shipSortValue[shipPredicateIndex]; } if( shipSortValue != lastShipSortValue ) { backgroundHighlight = !backgroundHighlight; lastShipSortValue = shipSortValue; } detailRows[i] = this._shipRowElement(s, translate, units, fInt, formats.f1, backgroundHighlight); shipRows[i] = ( {s.name} ); i++; } return (
{shipRows}
 
{translate('ship')}
{units['m/s']}
{detailRows}
{translate('manufacturer')}   {translate('size')} {translate('crew')} {translate('MLF')} {translate('agility')} {translate('hrd')}   {translate('base')} {translate('max')}
{translate('cost')} {translate('hull')} {translate('speed')} {translate('boost')} {translate('armour')} {translate('shields')} {translate('speed')} {translate('boost')} {translate('jump')} {translate('cargo')} {translate('passengers')} {translate('core module classes')} {translate('hardpoints')} {translate('internal compartments')}
{units.CR} {units.T} {units['m/s']} {units['m/s']}   {units.MJ} {units['m/s']} {units['m/s']} {units.LY} {units.T}   {'pp'} {'th'} {'fsd'} {'ls'} {'pd'} {'s'} {'ft'} {translate('S')} {translate('M')} {translate('L')} {translate('H')} {translate('U')} 1 2 3 4 5 6 7 8
); } }