diff --git a/src/data/BodyCodes.ts b/src/data/BodyCodes.ts new file mode 100644 index 0000000..5d7c9fa --- /dev/null +++ b/src/data/BodyCodes.ts @@ -0,0 +1,156 @@ +export const BodyCodes = { + // https://github.com/EDSM-NET/Alias/blob/master/Body/Star/Type.php + starTypes: { + // Main sequence + 'O': 1, + + 'B': 2, + 'B_BlueSuperGiant': 201, + + 'A': 3, + 'A_BlueWhiteSuperGiant': 301, + + 'F': 4, + 'F_WhiteSuperGiant': 401, + + 'G': 5, + 'G_WhiteSuperGiant': 5001, + + 'K': 6, + 'K_OrangeGiant': 601, + + 'M': 7, + 'M_RedGiant': 701, + 'M_RedSuperGiant': 702, + + 'L': 8, + 'T': 9, + 'Y': 10, + + // Proto stars + 'TTS': 11, + 'AeBe': 12, + + // Wolf-Rayet + 'W': 21, + 'WN': 22, + 'WNC': 23, + 'WC': 24, + 'WO': 25, + + // Carbon stars + 'CS': 31, + 'C': 32, + 'CN': 33, + 'CJ': 34, + 'CH': 35, + 'CHd': 36, + + 'MS': 41, + 'S': 42, + + // White dwarfs + 'D': 51, + 'DA': 501, + 'DAB': 502, + 'DAO': 503, + 'DAZ': 504, + 'DAV': 505, + 'DB': 506, + 'DBZ': 507, + 'DBV': 508, + 'DO': 509, + 'DOV': 510, + 'DQ': 511, + 'DC': 512, + 'DCV': 513, + 'DX': 514, + + 'N': 91, + 'H': 92, + 'SuperMassiveBlackHole': 93, + + 'X': 94, + + 'RoguePlanet': 111, + 'Nebula': 112, + 'StellarRemnantNebula': 113, + }, + + // https://github.com/EDSM-NET/Alias/blob/master/Body/Planet/Type.php + planetTypes: { + 'Metal-rich body': 1, + 'Metal rich body': 1, + + 'High metal content world': 2, + 'High metal content body': 2, + + 'Rocky body': 11, + + 'Rocky Ice world': 12, + 'Rocky ice body': 12, + + 'Icy body': 21, + + 'Earth-like world': 31, + 'Earthlike body': 31, + + 'Water world': 41, + + 'Water giant': 42, + 'Water giant with life': 43, + + 'Ammonia world': 51, + + 'Gas giant with water-based life': 61, + 'Gas giant with water based life': 61, + + 'Gas giant with ammonia-based life': 62, + 'Gas giant with ammonia based life': 62, + + 'Class I gas giant': 71, + 'Class II gas giant': 72, + 'Class III gas giant': 73, + 'Class IV gas giant': 74, + 'Class V gas giant': 75, + + 'Sudarsky class I gas giant': 71, + 'Sudarsky class II gas giant': 72, + 'Sudarsky class III gas giant': 73, + 'Sudarsky class IV gas giant': 74, + 'Sudarsky class V gas giant': 75, + + 'Helium-rich gas giant': 81, + 'Helium rich gas giant': 81, + 'Helium gas giant': 82, + }, + + // https://github.com/EDSM-NET/Alias/blob/master/Body/Planet/TerraformState.php + terraformStates: { + 'Not terraformable': 0, + + 'Candidate for terraforming': 1, + 'Terraformable': 1, + + 'Terraforming completed': 2, + 'Terraformed': 2, + + 'Being terraformed': 3, + 'Terraforming': 3, + }, + + // Star categories + whiteDwarf: [51, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514], + neutronStar: [91], + blackHole: [92], + superMassiveBlackHole: [93], + + // Planet categories + metalRich: [1], + ammonia: [51], + classIGiant: [71], + highMetalContent: [2], + classIIGiant: [72], + earthLike: [31], + water: [41], +} \ No newline at end of file diff --git a/src/models/Body.ts b/src/models/Body.ts index a68a0d6..4266e6a 100644 --- a/src/models/Body.ts +++ b/src/models/Body.ts @@ -1,9 +1,12 @@ import type { valuableBody } from "../@types/edsmResponses" import type { asteroidScan, autoScan, detailedScan, planetScan, starScan } from "../@types/journalLines" +import { BodyCodes } from "../data/BodyCodes" + export interface Body extends starScan<'AutoScan'|'DetailedScan'>, asteroidScan<'AutoScan'|'DetailedScan'>, planetScan<'AutoScan'|'DetailedScan'> {} export class Body { DSSDone: boolean + mappedValue: number constructor(journalLine: autoScan|detailedScan|valuableBody|null = null, DSS: boolean = false) { this.DSSDone = DSS @@ -11,6 +14,8 @@ export class Body { if (journalLine !== null) { Object.assign(this, journalLine) } + + this.mappedValue = this.#getValue() } /* -------------------------------------------------------------------------- isAsteroid ---- */ @@ -80,4 +85,159 @@ export class Body { return typeIcon } + + /* ---------------------------------------------------------------------------- getValue ---- */ + + // https://forums.frontier.co.uk/threads/exploration-value-formulae.232000/ + // https://github.com/EDSM-NET/Component/blob/master/Body/Value.php + + #getValue(): number { + let bodyType: number|undefined = undefined // Asteroids. + + if (this.isStar()) { + // Typescript feels so stupid sometimes. + bodyType = BodyCodes.starTypes[this.StarType as keyof typeof BodyCodes.starTypes] + } else if (this.isPlanet()) { + bodyType = BodyCodes.planetTypes[this.PlanetClass as keyof typeof BodyCodes.planetTypes] + } + + // Asteroids don't have mass. + const mass = ('MassEM' in this) ? this.MassEM : 1 + + let terraformState: number|undefined = undefined // Asteroids & Stars. + + if ('TerraformState' in this) { + terraformState = + BodyCodes.terraformStates[this.TerraformState as keyof typeof BodyCodes.terraformStates] + } + + const firstDiscover = !this.WasDiscovered + const firstMap = !this.WasMapped + + if (this.isStar()) { + return this.#appraiseStar(bodyType, mass) + + } else if (this.isPlanet() || this.isAsteroid()) { + // Asteroids are treated as planets for the purpose of value calculation. + return this.#appraisePlanet(bodyType, mass, terraformState, firstDiscover, firstMap) + + } else { + return 0 + } + } + + /* ----------------------------------------------------------------------- #appraiseStar ---- */ + + #appraiseStar(bodyType: number|undefined, mass: number): number { + let value: number = 1200 + + if (bodyType) { + if (BodyCodes.whiteDwarf.includes(bodyType)) { + value = 14057 + + } else if ( + BodyCodes.neutronStar.includes(bodyType) + || BodyCodes.blackHole.includes(bodyType) + ) { + value = 22628 + + } else if (BodyCodes.superMassiveBlackHole.includes(bodyType)) { + value = 33.5678 // Not confirmed in game. + } + } + + return Math.round(value + (mass * value / 66.25)) + } + + /* --------------------------------------------------------------------- #appraisePlanet ---- */ + + #appraisePlanet( + bodyType: number|undefined, + mass: number, + terraformState: number|undefined, + firstDiscover: boolean, + firstMap: boolean + ): number { + + // Base value & terraform bonus calculation. + let value: number = 300 + let terraformBonus: number = 0 + + // Base terraform bonus. + if (terraformState && terraformState > 0) { + terraformBonus = 93328 + } + + if (bodyType) { + // Metal-rich body. + if (BodyCodes.metalRich.includes(bodyType)) { + value = 21790 + + if (terraformState && terraformState > 0) { + terraformBonus = 65631 + } + + // Ammonia world. + } else if (BodyCodes.ammonia.includes(bodyType)) { + value = 96932 + + // Class I gas giant. + } else if (BodyCodes.classIGiant.includes(bodyType)) { + value = 1656 + + // High metal content & Class II gas giant. + } else if ( + BodyCodes.highMetalContent.includes(bodyType) + || BodyCodes.classIIGiant.includes(bodyType) + ) { + value = 9654 + + if (terraformState && terraformState > 0) { + terraformBonus = 100677 + } + + // Earth-like world & water world. + } else if ( + BodyCodes.earthLike.includes(bodyType) + || BodyCodes.water.includes(bodyType) + ) { + value = 64831 + + if (terraformState && terraformState > 0) { + terraformBonus = 116295 + } + + // Earth-like always gets a bonus. + if (BodyCodes.earthLike.includes(bodyType)) { + terraformBonus = 116295 + } + } + } + + // Mapping multiplier. + let mapMultiplier: number = 3.3333333333 + + if (firstDiscover && firstMap) { + mapMultiplier = 3.699622554 + + } else if (!firstDiscover && firstMap) { + mapMultiplier = 8.0956 + } + + // Efficiency bonus. + mapMultiplier *= 1.25 + + // Final calculation. + const q = 0.56591828 + value += terraformBonus + + let finalValue = Math.max((value + (value * Math.pow(mass, 0.2) * q)) * mapMultiplier, 500) + + // First discovery bonus. + if (firstDiscover) { + finalValue *= 2.6 + } + + return Math.round(finalValue) + } } \ No newline at end of file diff --git a/src/models/UI.js b/src/models/UI.js index 857d611..e1e2c18 100644 --- a/src/models/UI.js +++ b/src/models/UI.js @@ -133,8 +133,8 @@ export class UI { row.appendChild(info) // mapped value - // TODO APPRAISAL DATA const value = $('