From 2437abb0ee57b36e7d3542743bd6247c0ae02370 Mon Sep 17 00:00:00 2001 From: boyestrous Date: Wed, 21 Aug 2024 15:19:39 -0500 Subject: [PATCH] description field for special tooltips --- src/app/utils/BlueprintFunctions.js | 882 ++++++++++++++-------------- 1 file changed, 447 insertions(+), 435 deletions(-) diff --git a/src/app/utils/BlueprintFunctions.js b/src/app/utils/BlueprintFunctions.js index 80259bea..9b0bfbce 100644 --- a/src/app/utils/BlueprintFunctions.js +++ b/src/app/utils/BlueprintFunctions.js @@ -1,435 +1,447 @@ -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 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) { - 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( - - {translate(feature, grp)} -   - {current}{symbol} -   - - ); - } - } - } - } - - return ( -
- - - {effects} - -
-
- ); -} - -/** - * 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( - - {translate(feature, grp)} - {lowerBound}{symbol} - {current}{symbol} - {upperBound}{symbol} - - ); - } else { - // We do not have a module, no value - effects.push( - - {translate(feature, grp)} - {lowerBound}{symbol} - {upperBound}{symbol} - - ); - } - } - } - 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( - - {translate(feature, grp)} -   - {current}{symbol} -   - - ); - } - } - } - - // 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( - - {translate(feature, grp)} -   - {current}{symbol} -   - - ); - } - } - } - } - } - - let components; - if (!m) { - components = []; - for (const component in blueprint.components) { - components.push( - - {translate(component)} - {blueprint.components[component]} - - ); - } - } - - let engineersList; - if (engineers) { - engineersList = []; - for (const engineer of engineers) { - engineersList.push( - - {engineer} - - ); - } - } - - return ( -
- - - - - - {m ? : null } - - - - - {effects} - -
{translate('feature')}{translate('worst')}{translate('current')}{translate('best')}
- { components ? - - - - - - - - {components} - -
{translate('component')}{translate('amount')}
: null } - { engineersList ? - - - - - - - {engineersList} - -
{translate('engineers')}
: null } -
- ); -} - -/** - * 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) { - // 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) { - 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); - } -} +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( +
+ {Modifications.specials[specialName].description} +
+ ) + } + + } + 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( + + {translate(feature, grp)} +   + {current}{symbol} +   + + ); + } + } + } + } + + return ( +
+ {description} + + + {effects} + +
+
+ ); +} + +/** + * 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( + + {translate(feature, grp)} + {lowerBound}{symbol} + {current}{symbol} + {upperBound}{symbol} + + ); + } else { + // We do not have a module, no value + effects.push( + + {translate(feature, grp)} + {lowerBound}{symbol} + {upperBound}{symbol} + + ); + } + } + } + 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( + + {translate(feature, grp)} +   + {current}{symbol} +   + + ); + } + } + } + + // 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( + + {translate(feature, grp)} +   + {current}{symbol} +   + + ); + } + } + } + } + } + + let components; + if (!m) { + components = []; + for (const component in blueprint.components) { + components.push( + + {translate(component)} + {blueprint.components[component]} + + ); + } + } + + let engineersList; + if (engineers) { + engineersList = []; + for (const engineer of engineers) { + engineersList.push( + + {engineer} + + ); + } + } + + return ( +
+ + + + + + {m ? : null } + + + + + {effects} + +
{translate('feature')}{translate('worst')}{translate('current')}{translate('best')}
+ { components ? + + + + + + + + {components} + +
{translate('component')}{translate('amount')}
: null } + { engineersList ? + + + + + + + {engineersList} + +
{translate('engineers')}
: null } +
+ ); +} + +/** + * 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) { + // 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) { + 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); + } +}