mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-08 22:33:24 +00:00
459 lines
17 KiB
JavaScript
459 lines
17 KiB
JavaScript
import React from 'react';
|
|
import { Modifications } from 'coriolis-data/dist';
|
|
import { STATS_FORMATTING } from '../shipyard/StatsFormatting';
|
|
|
|
/**
|
|
* Generate a tooltip with details of a blueprint's specials
|
|
* @param {Object} translate The translate object
|
|
* @param {Object} blueprint The blueprint at the required grade
|
|
* @param {string} grp The group of the module
|
|
* @param {Object} m The module to compare with
|
|
* @param {string} specialName The name of the special
|
|
* @returns {Object} The react components
|
|
*/
|
|
export function specialToolTip(translate, blueprint, grp, m, specialName) {
|
|
const description = [];
|
|
const effects = [];
|
|
if (!blueprint || !blueprint.features) {
|
|
return undefined;
|
|
}
|
|
if (m) {
|
|
// We also add in any benefits from specials that aren't covered above
|
|
if (m.blueprint) {
|
|
if (specialName) {
|
|
if (Modifications.specials[specialName].description) {
|
|
description.push(
|
|
<div className={'success'} style={{ maxWidth: 350, padding: 5, marginBottom: 10 }}>
|
|
{Modifications.specials[specialName].description}
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
for (const feature in Modifications.modifierActions[specialName]) {
|
|
// if (!blueprint.features[feature] && !m.mods.feature) {
|
|
const featureDef = Modifications.modifications[feature];
|
|
if (featureDef && !featureDef.hidden) {
|
|
let symbol = '';
|
|
if (feature === 'jitter') {
|
|
symbol = '°';
|
|
} else if (featureDef.type === 'percentage') {
|
|
symbol = '%';
|
|
}
|
|
let current = m.getModValue(feature) - m.getModValue(feature, true);
|
|
if (featureDef.type === 'percentage') {
|
|
current = Math.round(current / 10) / 10;
|
|
} else if (featureDef.type === 'numeric') {
|
|
current /= 100;
|
|
}
|
|
const currentIsBeneficial = isValueBeneficial(feature, current);
|
|
|
|
effects.push(
|
|
<tr key={feature + '_specialTT'}>
|
|
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
|
<td> </td>
|
|
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'}
|
|
style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
|
<td> </td>
|
|
</tr>
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
{description}
|
|
<table width='100%'>
|
|
<tbody>
|
|
{effects}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Generate a tooltip with details of a blueprint's effects
|
|
* @param {Object} translate The translate object
|
|
* @param {Object} blueprint The blueprint at the required grade
|
|
* @param {Array} engineers The engineers supplying this blueprint
|
|
* @param {string} grp The group of the module
|
|
* @param {Object} m The module to compare with
|
|
* @returns {Object} The react components
|
|
*/
|
|
export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
|
|
const effects = [];
|
|
if (!blueprint || !blueprint.features) {
|
|
return undefined;
|
|
}
|
|
for (const feature in blueprint.features) {
|
|
const featureIsBeneficial = isBeneficial(feature, blueprint.features[feature]);
|
|
const featureDef = Modifications.modifications[feature];
|
|
if (!featureDef.hidden) {
|
|
let symbol = '';
|
|
if (feature === 'jitter') {
|
|
symbol = '°';
|
|
} else if (featureDef.type === 'percentage') {
|
|
symbol = '%';
|
|
}
|
|
let lowerBound = blueprint.features[feature][0];
|
|
let upperBound = blueprint.features[feature][1];
|
|
if (featureDef.type === 'percentage') {
|
|
lowerBound = Math.round(lowerBound * 1000) / 10;
|
|
upperBound = Math.round(upperBound * 1000) / 10;
|
|
}
|
|
const lowerIsBeneficial = isValueBeneficial(feature, lowerBound);
|
|
const upperIsBeneficial = isValueBeneficial(feature, upperBound);
|
|
if (m) {
|
|
// We have a module - add in the current value
|
|
let current = m.getModValue(feature);
|
|
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
|
|
current = Math.round(current / 10) / 10;
|
|
} else if (featureDef.type === 'numeric') {
|
|
current /= 100;
|
|
}
|
|
const currentIsBeneficial = isValueBeneficial(feature, current);
|
|
effects.push(
|
|
<tr key={feature}>
|
|
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
|
<td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td>
|
|
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
|
<td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td>
|
|
</tr>
|
|
);
|
|
} else {
|
|
// We do not have a module, no value
|
|
effects.push(
|
|
<tr key={feature}>
|
|
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
|
<td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td>
|
|
<td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td>
|
|
</tr>
|
|
);
|
|
}
|
|
}
|
|
}
|
|
if (m) {
|
|
// Because we have a module add in any benefits that aren't part of the primary blueprint
|
|
for (const feature in m.mods) {
|
|
if (!blueprint.features[feature]) {
|
|
const featureDef = Modifications.modifications[feature];
|
|
if (featureDef && !featureDef.hidden) {
|
|
let symbol = '';
|
|
if (feature === 'jitter') {
|
|
symbol = '°';
|
|
} else if (featureDef.type === 'percentage') {
|
|
symbol = '%';
|
|
}
|
|
let current = m.getModValue(feature);
|
|
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
|
|
current = Math.round(current / 10) / 10;
|
|
} else if (featureDef.type === 'numeric') {
|
|
current /= 100;
|
|
}
|
|
const currentIsBeneficial = isValueBeneficial(feature, current);
|
|
effects.push(
|
|
<tr key={feature}>
|
|
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
|
<td> </td>
|
|
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
|
<td> </td>
|
|
</tr>
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// We also add in any benefits from specials that aren't covered above
|
|
if (m.blueprint && m.blueprint.special) {
|
|
for (const feature in Modifications.modifierActions[m.blueprint.special.edname]) {
|
|
if (!blueprint.features[feature] && !m.mods.feature) {
|
|
const featureDef = Modifications.modifications[feature];
|
|
if (featureDef && !featureDef.hidden) {
|
|
let symbol = '';
|
|
if (feature === 'jitter') {
|
|
symbol = '°';
|
|
} else if (featureDef.type === 'percentage') {
|
|
symbol = '%';
|
|
}
|
|
let current = m.getModValue(feature);
|
|
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
|
|
current = Math.round(current / 10) / 10;
|
|
} else if (featureDef.type === 'numeric') {
|
|
current /= 100;
|
|
}
|
|
const currentIsBeneficial = isValueBeneficial(feature, current);
|
|
effects.push(
|
|
<tr key={feature}>
|
|
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
|
<td> </td>
|
|
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
|
<td> </td>
|
|
</tr>
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let components;
|
|
if (!m) {
|
|
components = [];
|
|
for (const component in blueprint.components) {
|
|
components.push(
|
|
<tr key={component}>
|
|
<td style={{ textAlign: 'left' }}>{translate(component)}</td>
|
|
<td style={{ textAlign: 'right' }}>{blueprint.components[component]}</td>
|
|
</tr>
|
|
);
|
|
}
|
|
}
|
|
|
|
let engineersList;
|
|
if (engineers) {
|
|
engineersList = [];
|
|
for (const engineer of engineers) {
|
|
engineersList.push(
|
|
<tr key={engineer}>
|
|
<td style={{ textAlign: 'left' }}>{engineer}</td>
|
|
</tr>
|
|
);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
<table width='100%'>
|
|
<thead>
|
|
<tr>
|
|
<td>{translate('feature')}</td>
|
|
<td>{translate('worst')}</td>
|
|
{m ? <td>{translate('current')}</td> : null }
|
|
<td>{translate('best')}</td>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{effects}
|
|
</tbody>
|
|
</table>
|
|
{ components ? <table width='100%'>
|
|
<thead>
|
|
<tr>
|
|
<td>{translate('component')}</td>
|
|
<td>{translate('amount')}</td>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{components}
|
|
</tbody>
|
|
</table> : null }
|
|
{ engineersList ? <table width='100%'>
|
|
<thead>
|
|
<tr>
|
|
<td>{translate('engineers')}</td>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{engineersList}
|
|
</tbody>
|
|
</table> : null }
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Is this blueprint feature beneficial?
|
|
* @param {string} feature The name of the feature
|
|
* @param {array} values The value of the feature
|
|
* @returns {boolean} True if this feature is beneficial
|
|
*/
|
|
export function isBeneficial(feature, values) {
|
|
const fact = (values[0] < 0 || (values[0] === 0 && values[1] < 0));
|
|
if (Modifications.modifications[feature].higherbetter) {
|
|
return !fact;
|
|
} else {
|
|
return fact;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Is this feature value beneficial?
|
|
* @param {string} feature The name of the feature
|
|
* @param {number} value The value of the feature
|
|
* @returns {boolean} True if this value is beneficial
|
|
*/
|
|
export function isValueBeneficial(feature, value) {
|
|
if (Modifications.modifications[feature].higherbetter) {
|
|
return value > 0;
|
|
} else {
|
|
return value < 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Is the change as shown beneficial?
|
|
* @param {string} feature The name of the feature
|
|
* @param {number} value The value of the feature as percentage change
|
|
* @returns True if the value is beneficial
|
|
*/
|
|
export function isChangeValueBeneficial(feature, value) {
|
|
let changeHigherBetter = STATS_FORMATTING[feature].higherbetter;
|
|
if (changeHigherBetter === undefined) {
|
|
return isValueBeneficial(feature, value);
|
|
}
|
|
|
|
if (changeHigherBetter) {
|
|
return value > 0;
|
|
} else {
|
|
return value < 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get a blueprint with a given name and an optional module
|
|
* @param {string} name The name of the blueprint
|
|
* @param {Object} module The module for which to obtain this blueprint
|
|
* @returns {Object} The matching blueprint
|
|
*/
|
|
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
|
|
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)];
|
|
if (!found || !found.fdname) {
|
|
console.error('Blueprint not found:', name);
|
|
return {};
|
|
}
|
|
const blueprint = JSON.parse(JSON.stringify(found));
|
|
return blueprint;
|
|
}
|
|
|
|
/**
|
|
* Provide 'percent' primary modifications
|
|
* @param {Object} ship The ship for which to perform the modifications
|
|
* @param {Object} m The module for which to perform the modifications
|
|
* @param {Number} percent The percent to set values to of full.
|
|
*/
|
|
export function setPercent(ship, m, percent) {
|
|
ship.clearModifications(m);
|
|
// Pick given value as multiplier
|
|
const mult = percent / 100;
|
|
setQualityCB(m.blueprint, mult, (featureName, value) => ship.setModification(m, featureName, value));
|
|
}
|
|
|
|
/**
|
|
* Sets the blueprint quality and fires a callback for each property affected.
|
|
* @param {Object} blueprint The ship for which to perform the modifications
|
|
* @param {Number} quality The quality to apply - float number 0 to 1.
|
|
* @param {Function} cb The Callback to run for each property. Function (featureName, value)
|
|
*/
|
|
export function setQualityCB(blueprint, quality, cb) {
|
|
// Pick given value as multiplier
|
|
const features = blueprint.grades[blueprint.grade].features;
|
|
for (const featureName in features) {
|
|
let value;
|
|
if (Modifications.modifications[featureName].higherbetter) {
|
|
// Higher is better, but is this making it better or worse?
|
|
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
|
value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * quality);
|
|
} else {
|
|
value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * quality);
|
|
}
|
|
} else {
|
|
// Higher is worse, but is this making it better or worse?
|
|
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
|
value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * quality);
|
|
} else {
|
|
value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * quality);
|
|
}
|
|
}
|
|
|
|
if (Modifications.modifications[featureName].type == 'percentage') {
|
|
value = value * 10000;
|
|
} else if (Modifications.modifications[featureName].type == 'numeric') {
|
|
value = value * 100;
|
|
}
|
|
|
|
cb(featureName, value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provide 'random' primary modifications
|
|
* @param {Object} ship The ship for which to perform the modifications
|
|
* @param {Object} m The module for which to perform the modifications
|
|
*/
|
|
export function setRandom(ship, m) {
|
|
// Pick a single value for our randomness
|
|
setPercent(ship, m, Math.random() * 100);
|
|
}
|
|
|
|
/**
|
|
* Provide 'percent' primary query
|
|
* @param {Object} m The module for which to perform the query
|
|
* @returns {Number} percent The percentage indicator of current applied values.
|
|
*/
|
|
export function getPercent(m) {
|
|
let result = null;
|
|
const features = m.blueprint.grades[m.blueprint.grade].features;
|
|
for (const featureName in features) {
|
|
if (features[featureName][0] === features[featureName][1]) {
|
|
continue;
|
|
}
|
|
|
|
let value = _getValue(m, featureName);
|
|
let mult;
|
|
if (Modifications.modifications[featureName].higherbetter) {
|
|
// Higher is better, but is this making it better or worse?
|
|
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
|
mult = Math.round((value - features[featureName][1]) / (features[featureName][0] - features[featureName][1]) * 100);
|
|
} else {
|
|
mult = Math.round((value - features[featureName][0]) / (features[featureName][1] - features[featureName][0]) * 100);
|
|
}
|
|
} else {
|
|
// Higher is worse, but is this making it better or worse?
|
|
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
|
mult = Math.round((value - features[featureName][0]) / (features[featureName][1] - features[featureName][0]) * 100);
|
|
} else {
|
|
mult = Math.round((value - features[featureName][1]) / (features[featureName][0] - features[featureName][1]) * 100);
|
|
}
|
|
}
|
|
|
|
if (result && result != mult) {
|
|
return null;
|
|
} else if (result != mult) {
|
|
result = mult;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Query a feature value
|
|
* @param {Object} m The module for which to perform the query
|
|
* @param {string} featureName The feature being queried
|
|
* @returns {number} The value of the modification as a %
|
|
*/
|
|
function _getValue(m, featureName) {
|
|
if (Modifications.modifications[featureName].type == 'percentage') {
|
|
return m.getModValue(featureName, true) / 10000;
|
|
} else if (Modifications.modifications[featureName].type == 'numeric') {
|
|
return m.getModValue(featureName, true) / 100;
|
|
} else {
|
|
return m.getModValue(featureName, true);
|
|
}
|
|
}
|