diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0509878a2..2dc68172e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,7 +6,6 @@ on:
jobs:
build:
- if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
container: pandoc/latex # "ubuntu" is a more generic container, using "pandoc/latex" because of dependencies, used in the specific "build.sh"
steps:
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
index af368a4fa..54eb4a360 100644
--- a/.github/workflows/playwright.yml
+++ b/.github/workflows/playwright.yml
@@ -4,6 +4,7 @@ on:
branches: [ main, beta ]
pull_request:
branches: [ main, beta ]
+ types: [ review_requested, ready_for_review ]
jobs:
test:
timeout-minutes: 60
diff --git a/docs/dev/ide-settings/webstorm-settings.zip b/docs/dev/ide-settings/webstorm-settings.zip
index 3d75c88e5..1a519697b 100644
Binary files a/docs/dev/ide-settings/webstorm-settings.zip and b/docs/dev/ide-settings/webstorm-settings.zip differ
diff --git a/eslint.config.js b/eslint.config.js
index 032bfde2b..639f6a3eb 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -45,7 +45,7 @@ const config = tseslint.config(
// As we're migrating to TS these make the process easier
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-return': 'off',
- '@typescript-eslint/no-unused-vars': 'warn',
+ '@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
},
},
diff --git a/public/locales/en/conditionals.yaml b/public/locales/en/conditionals.yaml
index 8f708d4be..f1f873b4a 100644
--- a/public/locales/en/conditionals.yaml
+++ b/public/locales/en/conditionals.yaml
@@ -496,7 +496,7 @@ Lightcones:
content: After the wearer uses Basic ATK or Skill, deals Additional DMG equal to {{Multiplier}}% of the wearer's ATK to a random enemy that has been attacked.
WoofWalkTime:
Content:
- atkBoost:
+ enemyBurnedBleeding:
text: Enemy burn / bleed DMG boost
content: Increases the wearer's DMG to enemies afflicted with Burn or Bleed by {{DmgBuff}}%. This also applies to DoT.
Adversarial:
diff --git a/src/components/CharacterPreview.jsx b/src/components/CharacterPreview.jsx
index bc4285a2e..abb5517aa 100644
--- a/src/components/CharacterPreview.jsx
+++ b/src/components/CharacterPreview.jsx
@@ -43,9 +43,11 @@ import { AppPages, DB } from 'lib/db'
import { Message } from 'lib/message'
import { calculateBuild } from 'lib/optimizer/calculateBuild'
import { OptimizerTabController } from 'lib/optimizerTabController'
+import { RelicFilters } from 'lib/relicFilters'
import { RelicModalController } from 'lib/relicModalController'
import { RelicScorer } from 'lib/relicScorerPotential'
import { SaveState } from 'lib/saveState'
+import { StatCalculator } from 'lib/statCalculator'
import { Utils } from 'lib/utils'
import PropTypes from 'prop-types'
import React, { useEffect, useRef, useState } from 'react'
@@ -184,13 +186,14 @@ export function CharacterPreview(props) {
gap={defaultGap}
id={props.id}
>
-
{/* This is a placeholder for the character portrait when no character is selected */}
@@ -251,7 +254,7 @@ export function CharacterPreview(props) {
const statCalculationRelics = Utils.clone(displayRelics)
RelicFilters.condenseRelicSubstatsForOptimizerSingle(Object.values(statCalculationRelics))
- const finalStats = calculateBuild(OptimizerTabController.fixForm(OptimizerTabController.getDisplayFormValues(character.form)), statCalculationRelics)
+ const { c: finalStats } = calculateBuild(OptimizerTabController.fixForm(OptimizerTabController.getDisplayFormValues(character.form)), statCalculationRelics)
finalStats.CV = StatCalculator.calculateCv(Object.values(statCalculationRelics))
finalStats[elementalDmgValue] = finalStats.ELEMENTAL_DMG
@@ -313,7 +316,9 @@ export function CharacterPreview(props) {
// Some APIs return empty light cone as '0'
const charCenter = DB.getMetadata().characters[character.id].imageCenter
- const lcCenter = (character.form.lightCone && character.form.lightCone != '0') ? DB.getMetadata().lightCones[character.form.lightCone].imageCenter : 0
+ const lcCenter = (character.form.lightCone && character.form.lightCone != '0')
+ ? DB.getMetadata().lightCones[character.form.lightCone].imageCenter
+ : 0
const tempLcParentW = simScoringResult ? parentW : lcParentW
diff --git a/src/components/RelicScorerTab.jsx b/src/components/RelicScorerTab.jsx
index b07334e09..9509b2edc 100644
--- a/src/components/RelicScorerTab.jsx
+++ b/src/components/RelicScorerTab.jsx
@@ -1,20 +1,27 @@
-import React, { useEffect, useMemo, useRef, useState } from 'react'
+import Icon, {
+ CameraOutlined,
+ DownloadOutlined,
+ EditOutlined,
+ ExperimentOutlined,
+ ImportOutlined,
+ LineChartOutlined,
+} from '@ant-design/icons'
import { Button, Dropdown, Flex, Form, Input, Segmented, theme, Typography } from 'antd'
+import CharacterModal from 'components/CharacterModal'
import { CharacterPreview } from 'components/CharacterPreview'
-import { SaveState } from 'lib/saveState'
-import { CharacterConverter } from 'lib/characterConverter'
+import { applySpdPreset } from 'components/optimizerTab/optimizerForm/RecommendedPresetsButton'
import { Assets } from 'lib/assets'
-import PropTypes from 'prop-types'
+import { CharacterConverter } from 'lib/characterConverter'
+import { Constants, CURRENT_DATA_VERSION, officialOnly } from 'lib/constants'
+import { SavedSessionKeys } from 'lib/constantsSession'
import DB, { AppPages, PageToRoute } from 'lib/db'
-import { Utils } from 'lib/utils'
-import Icon, { CameraOutlined, DownloadOutlined, EditOutlined, ExperimentOutlined, ImportOutlined, LineChartOutlined } from '@ant-design/icons'
import { Message } from 'lib/message'
-import CharacterModal from 'components/CharacterModal'
-import { SavedSessionKeys } from 'lib/constantsSession'
-import { applySpdPreset } from 'components/optimizerTab/optimizerForm/RecommendedPresetsButton'
import { calculateBuild } from 'lib/optimizer/calculateBuild'
import { OptimizerTabController } from 'lib/optimizerTabController'
-import { Constants, CURRENT_DATA_VERSION, officialOnly } from 'lib/constants'
+import { SaveState } from 'lib/saveState'
+import { Utils } from 'lib/utils'
+import PropTypes from 'prop-types'
+import React, { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
const { useToken } = theme
@@ -201,7 +208,9 @@ export default function RelicScorerTab() {
*/}
- {officialOnly ? t('Header.WithoutVersion') : t('Header.WithVersion', { beta_version: CURRENT_DATA_VERSION })}
+ {officialOnly
+ ? t('Header.WithoutVersion')
+ : t('Header.WithVersion', { beta_version: CURRENT_DATA_VERSION })}
{
/*
"WithVersion": "Enter your account UID to score your profile character at level 80 & maxed traces. Log out to refresh instantly. (Current version {{beta_version}} )",
@@ -264,11 +273,17 @@ function CharacterPreviewSelection(props) {
const items = [
{
- label: {t('ImportLabels.AllCharacters')/* Import all characters & all relics into optimizer */},
+ label: (
+ {t('ImportLabels.AllCharacters')/* Import all characters & all relics into optimizer */}
+
+ ),
key: 'import characters',
},
{
- label: {t('ImportLabels.SingleCharacter')/* Import selected character & all relics into optimizer */},
+ label: (
+ {t('ImportLabels.SingleCharacter')/* Import selected character & all relics into optimizer */}
+
+ ),
key: 'import single character',
},
]
@@ -458,7 +473,7 @@ function CharacterPreviewSelection(props) {
RelicFilters.condenseRelicSubstatsForOptimizer(relicsByPart)
// Calculate the build's speed value to use as a preset
- const c = calculateBuild(cleanedForm, equippedRelics)
+ const { c } = calculateBuild(cleanedForm, equippedRelics)
applySpdPreset(Utils.precisionRound(c.SPD, 3), characterId)
// Timeout to allow the form to populate before optimizing
@@ -474,10 +489,21 @@ function CharacterPreviewSelection(props) {
0) ? 'flex' : 'none', width: '100%' }}>
0) ? 'flex' : 'none' }} justify='space-between'>
-
)
}
@@ -347,7 +347,6 @@ export const CharacterScoringSummary = (props: { simScoringResult: SimulationSco
// @ts-ignore type of key is not specific enough for ts to know that t() will resolve properly
t(`CharacterPreview.BuildAnalysis.CombatResults.Abilities.${result.characterMetadata.scoringMetadata.sortOption.key}`)
}
- textWidth={72}
/>
@@ -432,8 +431,8 @@ function addOnHitStats(simulationScore: SimulationScore) {
x.ELEMENTAL_DMG += x[`${ability}_BOOST`]
if (ability != SortOption.DOT.key) {
- x[Stats.CR] += x[`${ability}_CR_BOOST`]
- x[Stats.CD] += x[`${ability}_CD_BOOST`]
+ x.CR += x[`${ability}_CR_BOOST`]
+ x.CD += x[`${ability}_CD_BOOST`]
}
}
diff --git a/src/lib/TsUtils.ts b/src/lib/TsUtils.ts
index d4de735a9..5f019890e 100644
--- a/src/lib/TsUtils.ts
+++ b/src/lib/TsUtils.ts
@@ -71,6 +71,17 @@ export const TsUtils = {
console.warn('An unknown error occurred', err)
}
},
+
+ splitFloat64ToFloat32Parts(value: number) {
+ const high = Math.floor(value / 2 ** 32)
+ const low = value % 2 ** 32
+
+ return { high, low }
+ },
+
+ reconstructFloat64FromParts(high: number, low: number) {
+ return high * 2 ** 32 + low
+ },
}
const getEmptyT = <
diff --git a/src/lib/bufferPacker.ts b/src/lib/bufferPacker.ts
index dc961972a..22f2e2c83 100644
--- a/src/lib/bufferPacker.ts
+++ b/src/lib/bufferPacker.ts
@@ -1,8 +1,8 @@
-import { BasicStatsObject } from 'lib/conditionals/conditionalConstants'
import { Stats } from 'lib/constants'
import { FixedSizePriorityQueue } from 'lib/fixedSizePriorityQueue'
+import { ComputedStatsArray, Key } from 'lib/optimizer/computedStatsArray'
-const SIZE = 38
+const SIZE = 40
export type OptimizerDisplayData = {
'id': number
@@ -45,6 +45,8 @@ export type OptimizerDisplayData = {
'xELEMENTAL_DMG': number
'relicSetIndex': number
'ornamentSetIndex': number
+ 'low': number
+ 'high': number
'statSim'?: {
key: string
@@ -93,6 +95,8 @@ export const BufferPacker = {
'xELEMENTAL_DMG': arr[offset + 35],
'relicSetIndex': arr[offset + 36],
'ornamentSetIndex': arr[offset + 37],
+ 'low': arr[offset + 38],
+ 'high': arr[offset + 39],
}
},
@@ -108,54 +112,58 @@ export const BufferPacker = {
}
},
- packCharacter: (arr: number[], offset: number, character: BasicStatsObject) => {
+ packCharacter: (arr: number[], offset: number, x: ComputedStatsArray) => {
offset = offset * SIZE
+ const c = x.c
+ const a = x.a
- arr[offset] = character.id // 0
- arr[offset + 1] = character[Stats.HP]
- arr[offset + 2] = character[Stats.ATK]
- arr[offset + 3] = character[Stats.DEF]
- arr[offset + 4] = character[Stats.SPD]
- arr[offset + 5] = character[Stats.CD]
- arr[offset + 6] = character[Stats.CR]
- arr[offset + 7] = character[Stats.EHR]
- arr[offset + 8] = character[Stats.RES]
- arr[offset + 9] = character[Stats.BE]
- arr[offset + 10] = character[Stats.ERR] // 10
- arr[offset + 11] = character[Stats.OHB]
- arr[offset + 12] = character.ELEMENTAL_DMG
- arr[offset + 13] = character.WEIGHT
- arr[offset + 14] = character.x.EHP
- arr[offset + 15] = character.x.HEAL_VALUE
- arr[offset + 16] = character.x.SHIELD_VALUE
- arr[offset + 17] = character.x.BASIC_DMG
- arr[offset + 18] = character.x.SKILL_DMG
- arr[offset + 19] = character.x.ULT_DMG
- arr[offset + 20] = character.x.FUA_DMG
- arr[offset + 21] = character.x.DOT_DMG
- arr[offset + 22] = character.x.BREAK_DMG // 22
- arr[offset + 23] = character.x.COMBO_DMG
- arr[offset + 24] = character.x[Stats.HP]
- arr[offset + 25] = character.x[Stats.ATK]
- arr[offset + 26] = character.x[Stats.DEF]
- arr[offset + 27] = character.x[Stats.SPD]
- arr[offset + 28] = character.x[Stats.CR]
- arr[offset + 29] = character.x[Stats.CD]
- arr[offset + 30] = character.x[Stats.EHR]
- arr[offset + 31] = character.x[Stats.RES]
- arr[offset + 32] = character.x[Stats.BE]
- arr[offset + 33] = character.x[Stats.ERR] // 33
- arr[offset + 34] = character.x[Stats.OHB]
- arr[offset + 35] = character.x.ELEMENTAL_DMG
- arr[offset + 36] = character.relicSetIndex
- arr[offset + 37] = character.ornamentSetIndex
+ arr[offset] = x.c.id // 0
+ arr[offset + 1] = c[Stats.HP]
+ arr[offset + 2] = c[Stats.ATK]
+ arr[offset + 3] = c[Stats.DEF]
+ arr[offset + 4] = c[Stats.SPD]
+ arr[offset + 5] = c[Stats.CR]
+ arr[offset + 6] = c[Stats.CD]
+ arr[offset + 7] = c[Stats.EHR]
+ arr[offset + 8] = c[Stats.RES]
+ arr[offset + 9] = c[Stats.BE]
+ arr[offset + 10] = c[Stats.ERR] // 10
+ arr[offset + 11] = c[Stats.OHB]
+ arr[offset + 12] = c.ELEMENTAL_DMG
+ arr[offset + 13] = c.WEIGHT
+ arr[offset + 14] = a[Key.EHP]
+ arr[offset + 15] = a[Key.HEAL_VALUE]
+ arr[offset + 16] = a[Key.SHIELD_VALUE]
+ arr[offset + 17] = a[Key.BASIC_DMG]
+ arr[offset + 18] = a[Key.SKILL_DMG]
+ arr[offset + 19] = a[Key.ULT_DMG]
+ arr[offset + 20] = a[Key.FUA_DMG]
+ arr[offset + 21] = a[Key.DOT_DMG]
+ arr[offset + 22] = a[Key.BREAK_DMG] // 22
+ arr[offset + 23] = a[Key.COMBO_DMG]
+ arr[offset + 24] = a[Key.HP]
+ arr[offset + 25] = a[Key.ATK]
+ arr[offset + 26] = a[Key.DEF]
+ arr[offset + 27] = a[Key.SPD]
+ arr[offset + 28] = a[Key.CR]
+ arr[offset + 29] = a[Key.CD]
+ arr[offset + 30] = a[Key.EHR]
+ arr[offset + 31] = a[Key.RES]
+ arr[offset + 32] = a[Key.BE]
+ arr[offset + 33] = a[Key.ERR] // 33
+ arr[offset + 34] = a[Key.OHB]
+ arr[offset + 35] = a[Key.ELEMENTAL_DMG]
+ arr[offset + 36] = c.relicSetIndex
+ arr[offset + 37] = c.ornamentSetIndex
+ arr[offset + 38] = c.low
+ arr[offset + 39] = c.high
},
cleanFloatBuffer: (buffer: ArrayBuffer) => {
- new Float64Array(buffer).fill(0)
+ new Float32Array(buffer).fill(0)
},
createFloatBuffer: (length: number) => {
- return new Float64Array(length * SIZE).buffer
+ return new Float32Array(length * SIZE).buffer
},
}
diff --git a/src/lib/characterScorer.ts b/src/lib/characterScorer.ts
index 612356f09..f3e2e0d15 100644
--- a/src/lib/characterScorer.ts
+++ b/src/lib/characterScorer.ts
@@ -9,15 +9,7 @@ import { generateContext } from 'lib/optimizer/context/calculateContext'
import { emptyRelic } from 'lib/optimizer/optimizerUtils'
import { SortOptionProperties } from 'lib/optimizer/sortOptions'
import { StatCalculator } from 'lib/statCalculator'
-import {
- calculateOrnamentSets,
- calculateRelicSets,
- convertRelicsToSimulation,
- runSimulations,
- Simulation,
- SimulationRequest,
- SimulationStats,
-} from 'lib/statSimulationController'
+import { calculateOrnamentSets, calculateRelicSets, convertRelicsToSimulation, runSimulations, Simulation, SimulationRequest, SimulationStats } from 'lib/statSimulationController'
import { TsUtils } from 'lib/TsUtils'
import { Utils } from 'lib/utils'
import { Character } from 'types/Character'
@@ -294,9 +286,21 @@ export function scoreCharacterSimulation(
// Set up default request
const simulationForm: Form = generateFullDefaultForm(characterId, lightCone, characterEidolon, lightConeSuperimposition, false)
- const simulationFormT0 = generateFullDefaultForm(metadata.teammates[0].characterId, metadata.teammates[0].lightCone, metadata.teammates[0].characterEidolon, metadata.teammates[0].lightConeSuperimposition, true)
- const simulationFormT1 = generateFullDefaultForm(metadata.teammates[1].characterId, metadata.teammates[1].lightCone, metadata.teammates[1].characterEidolon, metadata.teammates[1].lightConeSuperimposition, true)
- const simulationFormT2 = generateFullDefaultForm(metadata.teammates[2].characterId, metadata.teammates[2].lightCone, metadata.teammates[2].characterEidolon, metadata.teammates[2].lightConeSuperimposition, true)
+ const simulationFormT0 = generateFullDefaultForm(metadata.teammates[0].characterId,
+ metadata.teammates[0].lightCone,
+ metadata.teammates[0].characterEidolon,
+ metadata.teammates[0].lightConeSuperimposition,
+ true)
+ const simulationFormT1 = generateFullDefaultForm(metadata.teammates[1].characterId,
+ metadata.teammates[1].lightCone,
+ metadata.teammates[1].characterEidolon,
+ metadata.teammates[1].lightConeSuperimposition,
+ true)
+ const simulationFormT2 = generateFullDefaultForm(metadata.teammates[2].characterId,
+ metadata.teammates[2].lightCone,
+ metadata.teammates[2].characterEidolon,
+ metadata.teammates[2].lightConeSuperimposition,
+ true)
simulationForm.teammate0 = simulationFormT0
simulationForm.teammate1 = simulationFormT1
simulationForm.teammate2 = simulationFormT2
@@ -1219,7 +1223,8 @@ export function calculatePenaltyMultiplier(
newPenaltyMultiplier *= (Math.min(1, simulationResult.x[stat] / breakpoints[stat]) + 1) / 2
} else {
// Percents are penalize by half of the missing stat's breakpoint roll percentage
- newPenaltyMultiplier *= Math.min(1, 1 - (breakpoints[stat] - simulationResult.x[stat]) / StatCalculator.getMaxedSubstatValue(stat, scoringParams.quality))
+ newPenaltyMultiplier *= Math.min(1,
+ 1 - (breakpoints[stat] - simulationResult.x[stat]) / StatCalculator.getMaxedSubstatValue(stat, scoringParams.quality))
}
}
simulationResult.penaltyMultiplier = newPenaltyMultiplier
diff --git a/src/lib/conditionals/character/Acheron.ts b/src/lib/conditionals/character/Acheron.ts
index 0979c3bf5..05058b3eb 100644
--- a/src/lib/conditionals/character/Acheron.ts
+++ b/src/lib/conditionals/character/Acheron.ts
@@ -1,17 +1,11 @@
-import { BASIC_TYPE, ComputedStatsObject, SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardAtkFinalizer,
- standardAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { BASIC_TYPE, SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityResPen, buffAbilityVulnerability } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
import { NumberToNumberMap } from 'types/Common'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -36,10 +30,24 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
2: 0.60,
}
- const content: ContentItem[] = [
- {
- formItem: 'slider',
+ const defaults = {
+ crimsonKnotStacks: maxCrimsonKnotStacks,
+ nihilityTeammates: maxNihilityTeammates,
+ e1EnemyDebuffed: true,
+ thunderCoreStacks: 3,
+ stygianResurgeHitsOnTarget: 6,
+ e4UltVulnerability: true,
+ e6UltBuffs: true,
+ }
+
+ const teammateDefaults = {
+ e4UltVulnerability: true,
+ }
+
+ const content: ContentDefinition = {
+ crimsonKnotStacks: {
id: 'crimsonKnotStacks',
+ formItem: 'slider',
text: t('Content.crimsonKnotStacks.text'),
content: t('Content.crimsonKnotStacks.content', {
RainbladeScaling: TsUtils.precisionRound(100 * ultRainbladeScaling),
@@ -48,115 +56,105 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
min: 0,
max: maxCrimsonKnotStacks,
},
- {
- formItem: 'slider',
+ nihilityTeammates: {
id: 'nihilityTeammates',
+ formItem: 'slider',
text: t('Content.nihilityTeammates.text'),
content: t('Content.nihilityTeammates.content'),
min: 0,
max: maxNihilityTeammates,
},
- {
- formItem: 'slider',
+ thunderCoreStacks: {
id: 'thunderCoreStacks',
+ formItem: 'slider',
text: t('Content.thunderCoreStacks.text'),
content: t('Content.thunderCoreStacks.content'),
min: 0,
max: 3,
},
- {
- formItem: 'slider',
+ stygianResurgeHitsOnTarget: {
id: 'stygianResurgeHitsOnTarget',
+ formItem: 'slider',
text: t('Content.stygianResurgeHitsOnTarget.text'),
content: t('Content.stygianResurgeHitsOnTarget.content'),
min: 0,
max: 6,
},
- {
- formItem: 'switch',
+ e1EnemyDebuffed: {
id: 'e1EnemyDebuffed',
+ formItem: 'switch',
text: t('Content.e1EnemyDebuffed.text'),
content: t('Content.e1EnemyDebuffed.content'),
disabled: e < 1,
},
- {
- formItem: 'switch',
+ e4UltVulnerability: {
id: 'e4UltVulnerability',
+ formItem: 'switch',
text: t('Content.e4UltVulnerability.text'),
content: t('Content.e4UltVulnerability.content'),
disabled: e < 4,
},
- {
- formItem: 'switch',
+ e6UltBuffs: {
id: 'e6UltBuffs',
+ formItem: 'switch',
text: t('Content.e6UltBuffs.text'),
content: t('Content.e6UltBuffs.content'),
disabled: e < 6,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'e4UltVulnerability'),
- ]
+ const teammateContent: ContentDefinition = {
+ e4UltVulnerability: content.e4UltVulnerability,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- crimsonKnotStacks: maxCrimsonKnotStacks,
- nihilityTeammates: maxNihilityTeammates,
- e1EnemyDebuffed: true,
- thunderCoreStacks: 3,
- stygianResurgeHitsOnTarget: 6,
- e4UltVulnerability: true,
- e6UltBuffs: true,
- }),
- teammateDefaults: () => ({
- e4UltVulnerability: true,
- }),
- initializeConfigurations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ initializeConfigurations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
if (e >= 6 && r.e6UltBuffs) {
- x.BASIC_DMG_TYPE = ULT_TYPE | BASIC_TYPE
- x.SKILL_DMG_TYPE = ULT_TYPE | SKILL_TYPE
+ x.BASIC_DMG_TYPE.set(ULT_TYPE | BASIC_TYPE, Source.NONE)
+ x.SKILL_DMG_TYPE.set(ULT_TYPE | SKILL_TYPE, Source.NONE)
}
},
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- x[Stats.CR] += (e >= 1 && r.e1EnemyDebuffed) ? 0.18 : 0
+ x.CR.buff((e >= 1 && r.e1EnemyDebuffed) ? 0.18 : 0, Source.NONE)
- x.ELEMENTAL_DMG += (r.thunderCoreStacks) * 0.30
- buffAbilityResPen(x, ULT_TYPE, talentResPen)
- buffAbilityResPen(x, ULT_TYPE, 0.20, (e >= 6 && r.e6UltBuffs))
+ x.ELEMENTAL_DMG.buff((r.thunderCoreStacks) * 0.30, Source.NONE)
+ buffAbilityResPen(x, ULT_TYPE, talentResPen, Source.NONE)
+ buffAbilityResPen(x, ULT_TYPE, (e >= 6 && r.e6UltBuffs) ? 0.20 : 0, Source.NONE)
const originalDmgBoost = nihilityTeammateScaling[r.nihilityTeammates]
- x.BASIC_ORIGINAL_DMG_BOOST += originalDmgBoost
- x.SKILL_ORIGINAL_DMG_BOOST += originalDmgBoost
- x.ULT_ORIGINAL_DMG_BOOST += originalDmgBoost
+ x.BASIC_ORIGINAL_DMG_BOOST.buff(originalDmgBoost, Source.NONE)
+ x.SKILL_ORIGINAL_DMG_BOOST.buff(originalDmgBoost, Source.NONE)
+ x.ULT_ORIGINAL_DMG_BOOST.buff(originalDmgBoost, Source.NONE)
- x.BASIC_SCALING = basicScaling
- x.SKILL_SCALING = skillScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
// Each ult is 3 rainblades, 3 base crimson knots, and then 1 crimson knot per stack, then 1 stygian resurge, and 6 thunder cores from trace
- x.ULT_SCALING += 3 * ultRainbladeScaling
- x.ULT_SCALING += 3 * ultCrimsonKnotScaling
- x.ULT_SCALING += ultCrimsonKnotScaling * (r.crimsonKnotStacks)
- x.ULT_SCALING += ultStygianResurgeScaling
- x.ULT_SCALING += r.stygianResurgeHitsOnTarget * ultThunderCoreScaling
+ x.ULT_SCALING.buff(3 * ultRainbladeScaling, Source.NONE)
+ x.ULT_SCALING.buff(3 * ultCrimsonKnotScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultCrimsonKnotScaling * (r.crimsonKnotStacks), Source.NONE)
+ x.ULT_SCALING.buff(ultStygianResurgeScaling, Source.NONE)
+ x.ULT_SCALING.buff(r.stygianResurgeHitsOnTarget * ultThunderCoreScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 105
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(105, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- buffAbilityVulnerability(x, ULT_TYPE, 0.08, (e >= 4 && m.e4UltVulnerability))
+ buffAbilityVulnerability(x, ULT_TYPE, (e >= 4 && m.e4UltVulnerability) ? 0.08 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/Argenti.ts b/src/lib/conditionals/character/Argenti.ts
index b01a64aaa..2ae3fb2a7 100644
--- a/src/lib/conditionals/character/Argenti.ts
+++ b/src/lib/conditionals/character/Argenti.ts
@@ -1,12 +1,11 @@
-import { ComputedStatsObject, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ULT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityDefPen } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -22,25 +21,33 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const ultEnhancedExtraHitScaling = ult(e, 0.95, 1.026)
const talentCrStackValue = talent(e, 0.025, 0.028)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ ultEnhanced: true,
+ talentStacks: talentMaxStacks,
+ ultEnhancedExtraHits: 6,
+ e2UltAtkBuff: true,
+ enemyHp50: true,
+ }
+
+ const content: ContentDefinition = {
+ ultEnhanced: {
id: 'ultEnhanced',
+ formItem: 'switch',
text: t('Content.ultEnhanced.text'),
content: t('Content.ultEnhanced.content', {
ultEnhancedExtraHitScaling: TsUtils.precisionRound(100 * ultEnhancedExtraHitScaling),
ultEnhancedScaling: TsUtils.precisionRound(100 * ultEnhancedScaling),
}),
},
- {
- formItem: 'switch',
+ enemyHp50: {
id: 'enemyHp50',
+ formItem: 'switch',
text: t('Content.enemyHp50.text'),
content: t('Content.enemyHp50.content'),
},
- {
- formItem: 'slider',
+ talentStacks: {
id: 'talentStacks',
+ formItem: 'slider',
text: t('Content.talentStacks.text'),
content: t('Content.talentStacks.content', {
talentMaxStacks: TsUtils.precisionRound(100 * talentMaxStacks),
@@ -49,64 +56,56 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
min: 0,
max: talentMaxStacks,
},
- {
- formItem: 'slider',
+ ultEnhancedExtraHits: {
id: 'ultEnhancedExtraHits',
+ formItem: 'slider',
text: t('Content.ultEnhancedExtraHits.text'),
content: t('Content.ultEnhancedExtraHits.content', { ultEnhancedExtraHitScaling: TsUtils.precisionRound(100 * ultEnhancedExtraHitScaling) }),
min: 0,
max: 6,
},
- {
- formItem: 'switch',
+ e2UltAtkBuff: {
id: 'e2UltAtkBuff',
+ formItem: 'switch',
text: t('Content.e2UltAtkBuff.text'),
content: t('Content.e2UltAtkBuff.content'),
disabled: e < 2,
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => ({
- ultEnhanced: true,
- talentStacks: talentMaxStacks,
- ultEnhancedExtraHits: 6,
- e2UltAtkBuff: true,
- enemyHp50: true,
- }),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Skills
- x[Stats.CR] += (r.talentStacks) * talentCrStackValue
+ x.CR.buff((r.talentStacks) * talentCrStackValue, Source.NONE)
// Traces
// Eidolons
- x[Stats.CD] += (e >= 1) ? (r.talentStacks) * 0.04 : 0
- x[Stats.ATK_P] += (e >= 2 && r.e2UltAtkBuff) ? 0.40 : 0
+ x.CD.buff((e >= 1) ? (r.talentStacks) * 0.04 : 0, Source.NONE)
+ x.ATK_P.buff((e >= 2 && r.e2UltAtkBuff) ? 0.40 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += (r.ultEnhanced) ? ultEnhancedScaling : ultScaling
- x.ULT_SCALING += (r.ultEnhancedExtraHits) * ultEnhancedExtraHitScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff((r.ultEnhanced) ? ultEnhancedScaling : ultScaling, Source.NONE)
+ x.ULT_SCALING.buff((r.ultEnhancedExtraHits) * ultEnhancedExtraHitScaling, Source.NONE)
// BOOST
- x.ELEMENTAL_DMG += (r.enemyHp50) ? 0.15 : 0
+ x.ELEMENTAL_DMG.buff((r.enemyHp50) ? 0.15 : 0, Source.NONE)
// Argenti's e6 ult buff is actually a cast type buff, not dmg type but we'll do it like this anyways
- buffAbilityDefPen(x, ULT_TYPE, 0.30, (e >= 6))
+ buffAbilityDefPen(x, ULT_TYPE, (e >= 6) ? 0.30 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 30
- x.ULT_TOUGHNESS_DMG += (r.ultEnhanced) ? 60 + 15 * r.ultEnhancedExtraHits : 60
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff((r.ultEnhanced) ? 60 + 15 * r.ultEnhancedExtraHits : 60, Source.NONE)
return x
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/Arlan.ts b/src/lib/conditionals/character/Arlan.ts
index 08aee5638..384bb0918 100644
--- a/src/lib/conditionals/character/Arlan.ts
+++ b/src/lib/conditionals/character/Arlan.ts
@@ -1,11 +1,11 @@
-import { ComputedStatsObject, SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
+import { SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityDmg } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -18,47 +18,49 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const talentMissingHpDmgBoostMax = talent(e, 0.72, 0.792)
- const content: ContentItem[] = [
- {
- formItem: 'slider',
+ const defaults = {
+ selfCurrentHpPercent: 1.00,
+ }
+
+ const content: ContentDefinition = {
+ selfCurrentHpPercent: {
id: 'selfCurrentHpPercent',
+ formItem: 'slider',
text: t('Content.selfCurrentHpPercent.text'),
content: t('Content.selfCurrentHpPercent.content', { talentMissingHpDmgBoostMax: TsUtils.precisionRound(100 * talentMissingHpDmgBoostMax) }),
min: 0.01,
max: 1.0,
percent: true,
},
- ]
+ }
return {
- content: () => content,
+ content: () => Object.values(content),
teammateContent: () => [],
- defaults: () => ({
- selfCurrentHpPercent: 1.00,
- }),
+ defaults: () => defaults,
teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x.ELEMENTAL_DMG += Math.min(talentMissingHpDmgBoostMax, 1 - r.selfCurrentHpPercent)
+ x.ELEMENTAL_DMG.buff(Math.min(talentMissingHpDmgBoostMax, 1 - r.selfCurrentHpPercent), Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
// Boost
- buffAbilityDmg(x, SKILL_TYPE, 0.10, (e >= 1 && r.selfCurrentHpPercent <= 0.50))
- buffAbilityDmg(x, ULT_TYPE, 0.20, (e >= 6 && r.selfCurrentHpPercent <= 0.50))
+ buffAbilityDmg(x, SKILL_TYPE, (e >= 1 && r.selfCurrentHpPercent <= 0.50) ? 0.10 : 0, Source.NONE)
+ buffAbilityDmg(x, ULT_TYPE, (e >= 6 && r.selfCurrentHpPercent <= 0.50) ? 0.20 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 60
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
return x
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/Asta.ts b/src/lib/conditionals/character/Asta.ts
index be4aea204..8e6ea109a 100644
--- a/src/lib/conditionals/character/Asta.ts
+++ b/src/lib/conditionals/character/Asta.ts
@@ -1,16 +1,9 @@
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardAtkFinalizer,
- standardAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -27,86 +20,90 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const ultScaling = ult(e, 0, 0)
const dotScaling = basic(e, 0.50, 0.55)
- const content: ContentItem[] = [
- {
- formItem: 'slider',
+ const defaults = {
+ talentBuffStacks: 5,
+ skillExtraDmgHits: skillExtraDmgHitsMax,
+ ultSpdBuff: true,
+ fireDmgBoost: true,
+ }
+
+ const teammateDefaults = {
+ talentBuffStacks: 5,
+ ultSpdBuff: true,
+ fireDmgBoost: true,
+ }
+
+ const content: ContentDefinition = {
+ skillExtraDmgHits: {
id: 'skillExtraDmgHits',
+ formItem: 'slider',
text: t('Content.skillExtraDmgHits.text'),
content: t('Content.skillExtraDmgHits.content', { skillExtraDmgHitsMax }),
min: 0,
max: skillExtraDmgHitsMax,
},
- {
- formItem: 'slider',
+ talentBuffStacks: {
id: 'talentBuffStacks',
+ formItem: 'slider',
text: t('Content.talentBuffStacks.text'),
content: t('Content.talentBuffStacks.content', { talentStacksAtkBuff: TsUtils.precisionRound(100 * talentStacksAtkBuff) }),
min: 0,
max: 5,
},
- {
- formItem: 'switch',
+ ultSpdBuff: {
id: 'ultSpdBuff',
+ formItem: 'switch',
text: t('Content.ultSpdBuff.text'),
content: t('Content.ultSpdBuff.content', { ultSpdBuffValue }),
},
- {
- formItem: 'switch',
+ fireDmgBoost: {
id: 'fireDmgBoost',
+ formItem: 'switch',
text: t('Content.fireDmgBoost.text'),
content: t('Content.fireDmgBoost.content'),
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'talentBuffStacks'),
- findContentId(content, 'ultSpdBuff'),
- findContentId(content, 'fireDmgBoost'),
- ]
+ const teammateContent: ContentDefinition = {
+ talentBuffStacks: content.talentBuffStacks,
+ ultSpdBuff: content.ultSpdBuff,
+ fireDmgBoost: content.fireDmgBoost,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- talentBuffStacks: 5,
- skillExtraDmgHits: skillExtraDmgHitsMax,
- ultSpdBuff: true,
- fireDmgBoost: true,
- }),
- teammateDefaults: () => ({
- talentBuffStacks: 5,
- ultSpdBuff: true,
- fireDmgBoost: true,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.DEF_P] += (r.talentBuffStacks) * talentStacksDefBuff
- x[Stats.ERR] += (e >= 4 && r.talentBuffStacks >= 2) ? 0.15 : 0
+ x.DEF_P.buff((r.talentBuffStacks) * talentStacksDefBuff, Source.NONE)
+ x.ERR.buff((e >= 4 && r.talentBuffStacks >= 2) ? 0.15 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling + r.skillExtraDmgHits * skillScaling
- x.ULT_SCALING += ultScaling
- x.DOT_SCALING += dotScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling + r.skillExtraDmgHits * skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.DOT_SCALING.buff(dotScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 30 + 15 * r.skillExtraDmgHits
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(30 + 15 * r.skillExtraDmgHits, Source.NONE)
- x.DOT_CHANCE = 0.8
+ x.DOT_CHANCE.set(0.8, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.SPD] += (m.ultSpdBuff) ? ultSpdBuffValue : 0
- x[Stats.ATK_P] += (m.talentBuffStacks) * talentStacksAtkBuff
+ x.SPD.buff((m.ultSpdBuff) ? ultSpdBuffValue : 0, Source.NONE)
+ x.ATK_P.buff((m.talentBuffStacks) * talentStacksAtkBuff, Source.NONE)
- x[Stats.Fire_DMG] += (m.fireDmgBoost) ? 0.18 : 0
+ x.FIRE_DMG_BOOST.buff((m.fireDmgBoost) ? 0.18 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/Aventurine.ts b/src/lib/conditionals/character/Aventurine.ts
index 6f7171d88..07f7c0498 100644
--- a/src/lib/conditionals/character/Aventurine.ts
+++ b/src/lib/conditionals/character/Aventurine.ts
@@ -1,18 +1,20 @@
-import { ComputedStatsObject, NONE_TYPE, SKILL_TYPE } from 'lib/conditionals/conditionalConstants'
+import { NONE_TYPE, SKILL_TYPE } from 'lib/conditionals/conditionalConstants'
import {
AbilityEidolon,
- findContentId,
+ Conditionals,
+ ContentDefinition,
gpuStandardDefFinalizer,
gpuStandardDefShieldFinalizer,
standardDefFinalizer,
standardDefShieldFinalizer,
} from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
-import { AventurineConversionConditional } from 'lib/gpu/conditionals/dynamicConditionals'
+import { ConditionalActivation, ConditionalType, Stats } from 'lib/constants'
+import { conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
+import { wgslFalse } from 'lib/gpu/injection/wgslUtils'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -35,10 +37,27 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const traceShieldScaling = 0.07
const traceShieldFlat = 96
- const content: ContentItem[] = [
- {
- formItem: 'select',
+ const defaults = {
+ shieldAbility: SKILL_TYPE,
+ defToCrBoost: true,
+ fuaHitsOnTarget: fuaHits,
+ fortifiedWagerBuff: true,
+ enemyUnnervedDebuff: true,
+ e2ResShred: true,
+ e4DefBuff: true,
+ e6ShieldStacks: 3,
+ }
+
+ const teammateDefaults = {
+ fortifiedWagerBuff: true,
+ enemyUnnervedDebuff: true,
+ e2ResShred: true,
+ }
+
+ const content: ContentDefinition = {
+ shieldAbility: {
id: 'shieldAbility',
+ formItem: 'select',
text: tShield('Text'),
content: tShield('Content'),
options: [
@@ -47,121 +66,144 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
],
fullWidth: true,
},
- {
- formItem: 'switch',
+ defToCrBoost: {
id: 'defToCrBoost',
+ formItem: 'switch',
text: t('Content.defToCrBoost.text'),
content: t('Content.defToCrBoost.content'),
},
- {
- formItem: 'switch',
+ fortifiedWagerBuff: {
id: 'fortifiedWagerBuff',
+ formItem: 'switch',
text: t('Content.fortifiedWagerBuff.text'),
content: t('Content.fortifiedWagerBuff.content', { talentResScaling: TsUtils.precisionRound(100 * talentResScaling) }),
},
- {
- formItem: 'switch',
+ enemyUnnervedDebuff: {
id: 'enemyUnnervedDebuff',
+ formItem: 'switch',
text: t('Content.enemyUnnervedDebuff.text'),
content: t('Content.enemyUnnervedDebuff.content', { ultCdBoost: TsUtils.precisionRound(100 * ultCdBoost) }),
},
- {
- formItem: 'slider',
+ fuaHitsOnTarget: {
id: 'fuaHitsOnTarget',
+ formItem: 'slider',
text: t('Content.fuaHitsOnTarget.text'),
content: t('Content.fuaHitsOnTarget.content', { talentDmgScaling: TsUtils.precisionRound(100 * talentDmgScaling) }),
min: 0,
max: fuaHits,
},
- {
- formItem: 'switch',
+ e2ResShred: {
id: 'e2ResShred',
+ formItem: 'switch',
text: t('Content.e2ResShred.text'),
content: t('Content.e2ResShred.content'),
disabled: e < 2,
},
- {
- formItem: 'switch',
+ e4DefBuff: {
id: 'e4DefBuff',
+ formItem: 'switch',
text: t('Content.e4DefBuff.text'),
content: t('Content.e4DefBuff.content'),
disabled: e < 4,
},
- {
- formItem: 'slider',
+ e6ShieldStacks: {
id: 'e6ShieldStacks',
+ formItem: 'slider',
text: t('Content.e6ShieldStacks.text'),
content: t('Content.e6ShieldStacks.content'),
min: 0,
max: 3,
disabled: e < 6,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'fortifiedWagerBuff'),
- findContentId(content, 'enemyUnnervedDebuff'),
- findContentId(content, 'e2ResShred'),
- ]
+ const teammateContent: ContentDefinition = {
+ fortifiedWagerBuff: content.fortifiedWagerBuff,
+ enemyUnnervedDebuff: content.enemyUnnervedDebuff,
+ e2ResShred: content.e2ResShred,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- shieldAbility: SKILL_TYPE,
- defToCrBoost: true,
- fuaHitsOnTarget: fuaHits,
- fortifiedWagerBuff: true,
- enemyUnnervedDebuff: true,
- e2ResShred: true,
- e4DefBuff: true,
- e6ShieldStacks: 3,
- }),
- teammateDefaults: () => ({
- fortifiedWagerBuff: true,
- enemyUnnervedDebuff: true,
- e2ResShred: true,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
-
- x[Stats.DEF_P] += (e >= 4 && r.e4DefBuff) ? 0.40 : 0
- x.ELEMENTAL_DMG += (e >= 6) ? Math.min(1.50, 0.50 * r.e6ShieldStacks) : 0
-
- x.BASIC_SCALING += basicScaling
- x.ULT_SCALING += ultScaling
- x.FUA_SCALING += talentDmgScaling * r.fuaHitsOnTarget
-
- x.BASIC_TOUGHNESS_DMG += 30
- x.ULT_TOUGHNESS_DMG += 90
- x.FUA_TOUGHNESS_DMG += 10 * r.fuaHitsOnTarget
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
+
+ x.DEF_P.buff((e >= 4 && r.e4DefBuff) ? 0.40 : 0, Source.NONE)
+ x.ELEMENTAL_DMG.buff((e >= 6) ? Math.min(1.50, 0.50 * r.e6ShieldStacks) : 0, Source.NONE)
+
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.FUA_SCALING.buff(talentDmgScaling * r.fuaHitsOnTarget, Source.NONE)
+
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(90, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff(10 * r.fuaHitsOnTarget, Source.NONE)
if (r.shieldAbility == SKILL_TYPE) {
- x.SHIELD_SCALING += skillShieldScaling
- x.SHIELD_FLAT += skillShieldFlat
+ x.SHIELD_SCALING.buff(skillShieldScaling, Source.NONE)
+ x.SHIELD_FLAT.buff(skillShieldFlat, Source.NONE)
}
if (r.shieldAbility == 0) {
- x.SHIELD_SCALING += traceShieldScaling
- x.SHIELD_FLAT += traceShieldFlat
+ x.SHIELD_SCALING.buff(traceShieldScaling, Source.NONE)
+ x.SHIELD_FLAT.buff(traceShieldFlat, Source.NONE)
}
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.RES] += (m.fortifiedWagerBuff) ? talentResScaling : 0
- x[Stats.CD] += (m.enemyUnnervedDebuff) ? ultCdBoost : 0
- x[Stats.CD] += (e >= 1 && m.fortifiedWagerBuff) ? 0.20 : 0
- x.RES_PEN += (e >= 2 && m.e2ResShred) ? 0.12 : 0
+ x.RES.buff((m.fortifiedWagerBuff) ? talentResScaling : 0, Source.NONE)
+ x.CD.buff((m.enemyUnnervedDebuff) ? ultCdBoost : 0, Source.NONE)
+ x.CD.buff((e >= 1 && m.fortifiedWagerBuff) ? 0.20 : 0, Source.NONE)
+ x.RES_PEN.buff((e >= 2 && m.e2ResShred) ? 0.12 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardDefFinalizer(x)
standardDefShieldFinalizer(x)
},
gpuFinalizeCalculations: () => {
return gpuStandardDefFinalizer() + gpuStandardDefShieldFinalizer()
},
- dynamicConditionals: [AventurineConversionConditional],
+ dynamicConditionals: [{
+ id: 'AventurineConversionConditional',
+ type: ConditionalType.ABILITY,
+ activation: ConditionalActivation.CONTINUOUS,
+ dependsOn: [Stats.DEF],
+ condition: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
+ const r: Conditionals = action.characterConditionals
+ return r.defToCrBoost && x.a[Key.DEF] > 1600
+ },
+ effect: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
+ const stateValue = action.conditionalState[this.id] || 0
+ const buffValue = Math.min(0.48, 0.02 * Math.floor((x.a[Key.DEF] - 1600) / 100))
+
+ action.conditionalState[this.id] = buffValue
+ x.CR.buffDynamic(buffValue - stateValue, Source.NONE, action, context)
+
+ return buffValue
+ },
+ gpu: function (action: OptimizerAction, context: OptimizerContext) {
+ const r: Conditionals = action.characterConditionals
+
+ return conditionalWgslWrapper(this, `
+if (${wgslFalse(r.defToCrBoost)}) {
+ return;
+}
+let def = (*p_x).DEF;
+let stateValue: f32 = (*p_state).AventurineConversionConditional;
+
+if (def > 1600) {
+ let buffValue: f32 = min(0.48, 0.02 * floor((def - 1600) / 100));
+
+ (*p_state).AventurineConversionConditional = buffValue;
+ buffDynamicCR(buffValue - stateValue, p_x, p_state);
+}
+ `)
+ },
+ }],
}
}
diff --git a/src/lib/conditionals/character/Bailu.ts b/src/lib/conditionals/character/Bailu.ts
index 9543c9920..d638f32b5 100644
--- a/src/lib/conditionals/character/Bailu.ts
+++ b/src/lib/conditionals/character/Bailu.ts
@@ -1,18 +1,18 @@
-import { ComputedStatsObject, NONE_TYPE, SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { NONE_TYPE, SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
import {
AbilityEidolon,
- findContentId,
+ Conditionals,
+ ContentDefinition,
gpuStandardAtkFinalizer,
gpuStandardHpHealFinalizer,
standardAtkFinalizer,
standardHpHealFinalizer,
} from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -33,10 +33,24 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const talentHealScaling = talent(e, 0.054, 0.0576)
const talentHealFlat = talent(e, 144, 160.2)
- const content: ContentItem[] = [
- {
- formItem: 'select',
+ const defaults = {
+ healAbility: ULT_TYPE,
+ healingMaxHpBuff: true,
+ talentDmgReductionBuff: true,
+ e2UltHealingBuff: true,
+ e4SkillHealingDmgBuffStacks: 0,
+ }
+
+ const teammateDefaults = {
+ healingMaxHpBuff: true,
+ talentDmgReductionBuff: true,
+ e4SkillHealingDmgBuffStacks: 3,
+ }
+
+ const content: ContentDefinition = {
+ healAbility: {
id: 'healAbility',
+ formItem: 'select',
text: tHeal('Text'),
content: tHeal('Content'),
options: [
@@ -46,97 +60,87 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
],
fullWidth: true,
},
- {
- formItem: 'switch',
+ healingMaxHpBuff: {
id: 'healingMaxHpBuff',
+ formItem: 'switch',
text: t('Content.healingMaxHpBuff.text'),
content: t('Content.healingMaxHpBuff.content'),
},
- {
- formItem: 'switch',
+ talentDmgReductionBuff: {
id: 'talentDmgReductionBuff',
+ formItem: 'switch',
text: t('Content.talentDmgReductionBuff.text'),
content: t('Content.talentDmgReductionBuff.content'),
},
- {
- formItem: 'switch',
+ e2UltHealingBuff: {
id: 'e2UltHealingBuff',
+ formItem: 'switch',
text: t('Content.e2UltHealingBuff.text'),
content: t('Content.e2UltHealingBuff.content'),
disabled: e < 2,
},
- {
- formItem: 'slider',
+ e4SkillHealingDmgBuffStacks: {
id: 'e4SkillHealingDmgBuffStacks',
+ formItem: 'slider',
text: t('Content.e4SkillHealingDmgBuffStacks.text'),
content: t('Content.e4SkillHealingDmgBuffStacks.content'),
min: 0,
max: 3,
disabled: e < 4,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'healingMaxHpBuff'),
- findContentId(content, 'talentDmgReductionBuff'),
- findContentId(content, 'e4SkillHealingDmgBuffStacks'),
- ]
+ const teammateContent: ContentDefinition = {
+ healingMaxHpBuff: content.healingMaxHpBuff,
+ talentDmgReductionBuff: content.talentDmgReductionBuff,
+ e4SkillHealingDmgBuffStacks: content.e4SkillHealingDmgBuffStacks,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- healAbility: ULT_TYPE,
- healingMaxHpBuff: true,
- talentDmgReductionBuff: true,
- e2UltHealingBuff: true,
- e4SkillHealingDmgBuffStacks: 0,
- }),
- teammateDefaults: () => ({
- healingMaxHpBuff: true,
- talentDmgReductionBuff: true,
- e4SkillHealingDmgBuffStacks: 3,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.OHB] += (e >= 2 && r.e2UltHealingBuff) ? 0.15 : 0
+ x.OHB.buff((e >= 2 && r.e2UltHealingBuff) ? 0.15 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
if (r.healAbility == SKILL_TYPE) {
- x.HEAL_TYPE = SKILL_TYPE
- x.HEAL_SCALING += skillHealScaling
- x.HEAL_FLAT += skillHealFlat
+ x.HEAL_TYPE.set(SKILL_TYPE, Source.NONE)
+ x.HEAL_SCALING.buff(skillHealScaling, Source.NONE)
+ x.HEAL_FLAT.buff(skillHealFlat, Source.NONE)
}
if (r.healAbility == ULT_TYPE) {
- x.HEAL_TYPE = ULT_TYPE
- x.HEAL_SCALING += ultHealScaling
- x.HEAL_FLAT += ultHealFlat
+ x.HEAL_TYPE.set(ULT_TYPE, Source.NONE)
+ x.HEAL_SCALING.buff(ultHealScaling, Source.NONE)
+ x.HEAL_FLAT.buff(ultHealFlat, Source.NONE)
}
if (r.healAbility == NONE_TYPE) {
- x.HEAL_TYPE = NONE_TYPE
- x.HEAL_SCALING += talentHealScaling
- x.HEAL_FLAT += talentHealFlat
+ x.HEAL_TYPE.set(NONE_TYPE, Source.NONE)
+ x.HEAL_SCALING.buff(talentHealScaling, Source.NONE)
+ x.HEAL_FLAT.buff(talentHealFlat, Source.NONE)
}
- x.BASIC_TOUGHNESS_DMG += 30
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.HP_P] += (m.healingMaxHpBuff) ? 0.10 : 0
+ x.HP_P.buff((m.healingMaxHpBuff) ? 0.10 : 0, Source.NONE)
- x.ELEMENTAL_DMG += (e >= 4) ? m.e4SkillHealingDmgBuffStacks * 0.10 : 0
- x.DMG_RED_MULTI *= (m.talentDmgReductionBuff) ? (1 - 0.10) : 1
+ x.ELEMENTAL_DMG.buff((e >= 4) ? m.e4SkillHealingDmgBuffStacks * 0.10 : 0, Source.NONE)
+ x.DMG_RED_MULTI.multiply((m.talentDmgReductionBuff) ? (1 - 0.10) : 1, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject) => {
+ finalizeCalculations: (x: ComputedStatsArray) => {
standardAtkFinalizer(x)
standardHpHealFinalizer(x)
},
diff --git a/src/lib/conditionals/character/BlackSwan.ts b/src/lib/conditionals/character/BlackSwan.ts
index 24a9833f5..8e85302e0 100644
--- a/src/lib/conditionals/character/BlackSwan.ts
+++ b/src/lib/conditionals/character/BlackSwan.ts
@@ -1,17 +1,12 @@
-import { ComputedStatsObject, DOT_TYPE } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardAtkFinalizer,
- standardAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
+import { DOT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { BlackSwanConversionConditional } from 'lib/gpu/conditionals/dynamicConditionals'
import { buffAbilityDefPen, buffAbilityVulnerability } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -29,28 +24,41 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const dotChance = talent(e, 0.65, 0.68)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ ehrToDmgBoost: true,
+ epiphanyDebuff: true,
+ defDecreaseDebuff: true,
+ arcanaStacks: 7,
+ e1ResReduction: true,
+ }
+ const teammateDefaults = {
+ epiphanyDebuff: true,
+ defDecreaseDebuff: true,
+ e1ResReduction: true,
+ }
+
+ const content: ContentDefinition = {
+ ehrToDmgBoost: {
id: 'ehrToDmgBoost',
+ formItem: 'switch',
text: t('Content.ehrToDmgBoost.text'),
content: t('Content.ehrToDmgBoost.content'),
},
- {
- formItem: 'switch',
+ epiphanyDebuff: {
id: 'epiphanyDebuff',
+ formItem: 'switch',
text: t('Content.epiphanyDebuff.text'),
content: t('Content.epiphanyDebuff.content', { epiphanyDmgTakenBoost: TsUtils.precisionRound(100 * epiphanyDmgTakenBoost) }),
},
- {
- formItem: 'switch',
+ defDecreaseDebuff: {
id: 'defDecreaseDebuff',
+ formItem: 'switch',
text: t('Content.defDecreaseDebuff.text'),
content: t('Content.defDecreaseDebuff.content', { defShredValue: TsUtils.precisionRound(100 * defShredValue) }),
},
- {
- formItem: 'slider',
+ arcanaStacks: {
id: 'arcanaStacks',
+ formItem: 'slider',
text: t('Content.arcanaStacks.text'),
content: t('Content.arcanaStacks.content', {
dotScaling: TsUtils.precisionRound(100 * dotScaling),
@@ -59,69 +67,57 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
min: 1,
max: 50,
},
- {
- formItem: 'switch',
+ e1ResReduction: {
id: 'e1ResReduction',
+ formItem: 'switch',
text: t('Content.e1ResReduction.text'),
content: t('Content.e1ResReduction.content'),
disabled: e < 1,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'epiphanyDebuff'),
- findContentId(content, 'defDecreaseDebuff'),
- findContentId(content, 'e1ResReduction'),
- ]
+ const teammateContent: ContentDefinition = {
+ epiphanyDebuff: content.epiphanyDebuff,
+ defDecreaseDebuff: content.defDecreaseDebuff,
+ e1ResReduction: content.e1ResReduction,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- ehrToDmgBoost: true,
- epiphanyDebuff: true,
- defDecreaseDebuff: true,
- arcanaStacks: 7,
- e1ResReduction: true,
- }),
- teammateDefaults: () => ({
- epiphanyDebuff: true,
- defDecreaseDebuff: true,
- e1ResReduction: true,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
-
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
- x.DOT_SCALING += dotScaling + arcanaStackMultiplier * r.arcanaStacks
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- buffAbilityDefPen(x, DOT_TYPE, 0.20, (r.arcanaStacks >= 7))
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.DOT_SCALING.buff(dotScaling + arcanaStackMultiplier * r.arcanaStacks, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 60
+ buffAbilityDefPen(x, DOT_TYPE, (r.arcanaStacks >= 7) ? 0.20 : 0, Source.NONE)
- x.DOT_CHANCE = dotChance
- x.DOT_SPLIT = 0.05
- x.DOT_STACKS = r.arcanaStacks
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
- return x
+ x.DOT_CHANCE.set(dotChance, Source.NONE)
+ x.DOT_SPLIT.set(0.05, Source.NONE)
+ x.DOT_STACKS.set(r.arcanaStacks, Source.NONE)
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
// TODO: Technically this isnt a DoT vulnerability but rather vulnerability to damage on the enemy's turn which includes ults/etc.
- buffAbilityVulnerability(x, DOT_TYPE, epiphanyDmgTakenBoost, (m.epiphanyDebuff))
+ buffAbilityVulnerability(x, DOT_TYPE, (m.epiphanyDebuff) ? epiphanyDmgTakenBoost : 0, Source.NONE)
- x.DEF_PEN += (m.defDecreaseDebuff) ? defShredValue : 0
- x.WIND_RES_PEN += (e >= 1 && m.e1ResReduction) ? 0.25 : 0
- x.FIRE_RES_PEN += (e >= 1 && m.e1ResReduction) ? 0.25 : 0
- x.PHYSICAL_RES_PEN += (e >= 1 && m.e1ResReduction) ? 0.25 : 0
- x.LIGHTNING_RES_PEN += (e >= 1 && m.e1ResReduction) ? 0.25 : 0
+ x.DEF_PEN.buff((m.defDecreaseDebuff) ? defShredValue : 0, Source.NONE)
+ x.WIND_RES_PEN.buff((e >= 1 && m.e1ResReduction) ? 0.25 : 0, Source.NONE)
+ x.FIRE_RES_PEN.buff((e >= 1 && m.e1ResReduction) ? 0.25 : 0, Source.NONE)
+ x.PHYSICAL_RES_PEN.buff((e >= 1 && m.e1ResReduction) ? 0.25 : 0, Source.NONE)
+ x.LIGHTNING_RES_PEN.buff((e >= 1 && m.e1ResReduction) ? 0.25 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
dynamicConditionals: [BlackSwanConversionConditional],
}
diff --git a/src/lib/conditionals/character/Blade.ts b/src/lib/conditionals/character/Blade.ts
index 1406c644a..3cedff18f 100644
--- a/src/lib/conditionals/character/Blade.ts
+++ b/src/lib/conditionals/character/Blade.ts
@@ -1,14 +1,13 @@
-import { ASHBLAZING_ATK_STACK, ComputedStatsObject, FUA_TYPE } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, calculateAshblazingSet } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ASHBLAZING_ATK_STACK, FUA_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, calculateAshblazingSet, Conditionals, ContentDefinition } from 'lib/conditionals/conditionalUtils'
import { wgslTrue } from 'lib/gpu/injection/wgslUtils'
import { buffAbilityDmg } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
import { NumberToNumberMap } from 'types/Common'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -33,88 +32,89 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
5: ASHBLAZING_ATK_STACK * (3 * 0.33 + 8 * 0.33 + 8 * 0.34),
}
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ enhancedStateActive: true,
+ hpPercentLostTotal: hpPercentLostTotalMax,
+ e4MaxHpIncreaseStacks: 2,
+ }
+
+ const content: ContentDefinition = {
+ enhancedStateActive: {
id: 'enhancedStateActive',
+ formItem: 'switch',
text: t('Content.enhancedStateActive.text'),
content: t('Content.enhancedStateActive.content', { enhancedStateDmgBoost: TsUtils.precisionRound(100 * enhancedStateDmgBoost) }),
},
- {
- formItem: 'slider',
+ hpPercentLostTotal: {
id: 'hpPercentLostTotal',
+ formItem: 'slider',
text: t('Content.hpPercentLostTotal.text'),
content: t('Content.hpPercentLostTotal.content', { hpPercentLostTotalMax: TsUtils.precisionRound(100 * hpPercentLostTotalMax) }),
min: 0,
max: hpPercentLostTotalMax,
percent: true,
},
- {
- formItem: 'slider',
+ e4MaxHpIncreaseStacks: {
id: 'e4MaxHpIncreaseStacks',
+ formItem: 'slider',
text: t('Content.e4MaxHpIncreaseStacks.text'),
content: t('Content.e4MaxHpIncreaseStacks.content'),
min: 0,
max: 2,
disabled: e < 4,
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => ({
- enhancedStateActive: true,
- hpPercentLostTotal: hpPercentLostTotalMax,
- e4MaxHpIncreaseStacks: 2,
- }),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.CR] += (e >= 2 && r.enhancedStateActive) ? 0.15 : 0
- x[Stats.HP_P] += (e >= 4) ? r.e4MaxHpIncreaseStacks * 0.20 : 0
+ x.CR.buff((e >= 2 && r.enhancedStateActive) ? 0.15 : 0, Source.NONE)
+ x.HP_P.buff((e >= 4) ? r.e4MaxHpIncreaseStacks * 0.20 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
// Boost
- x.ELEMENTAL_DMG += r.enhancedStateActive ? enhancedStateDmgBoost : 0
- buffAbilityDmg(x, FUA_TYPE, 0.20)
+ x.ELEMENTAL_DMG.buff(r.enhancedStateActive ? enhancedStateDmgBoost : 0, Source.NONE)
+ buffAbilityDmg(x, FUA_TYPE, 0.20, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += (r.enhancedStateActive) ? 60 : 30
- x.ULT_TOUGHNESS_DMG += 60
- x.FUA_TOUGHNESS_DMG += 30
+ x.BASIC_TOUGHNESS_DMG.buff((r.enhancedStateActive) ? 60 : 30, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff(30, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
+ const a = x.a
if (r.enhancedStateActive) {
- x.BASIC_DMG += basicEnhancedAtkScaling * x[Stats.ATK]
- x.BASIC_DMG += basicEnhancedHpScaling * x[Stats.HP]
+ x.BASIC_DMG.buff(basicEnhancedAtkScaling * a[Key.ATK], Source.NONE)
+ x.BASIC_DMG.buff(basicEnhancedHpScaling * a[Key.HP], Source.NONE)
} else {
- x.BASIC_DMG += x.BASIC_SCALING * x[Stats.ATK]
+ x.BASIC_DMG.buff(a[Key.BASIC_SCALING] * a[Key.ATK], Source.NONE)
}
- x.ULT_DMG += ultAtkScaling * x[Stats.ATK]
- x.ULT_DMG += ultHpScaling * x[Stats.HP]
- x.ULT_DMG += ultLostHpScaling * r.hpPercentLostTotal * x[Stats.HP]
- x.ULT_DMG += (e >= 1 && context.enemyCount == 1) ? 1.50 * r.hpPercentLostTotal * x[Stats.HP] : 0
+ x.ULT_DMG.buff(ultAtkScaling * a[Key.ATK], Source.NONE)
+ x.ULT_DMG.buff(ultHpScaling * a[Key.HP], Source.NONE)
+ x.ULT_DMG.buff(ultLostHpScaling * r.hpPercentLostTotal * a[Key.HP], Source.NONE)
+ x.ULT_DMG.buff((e >= 1 && context.enemyCount == 1) ? 1.50 * r.hpPercentLostTotal * a[Key.HP] : 0, Source.NONE)
const hitMulti = hitMultiByTargets[context.enemyCount]
const ashblazingAtk = calculateAshblazingSet(x, action, context, hitMulti)
- x.FUA_DMG += fuaAtkScaling * (x[Stats.ATK] + ashblazingAtk)
+ x.FUA_DMG.buff(fuaAtkScaling * (a[Key.ATK] + ashblazingAtk), Source.NONE)
- x.FUA_DMG += fuaHpScaling * x[Stats.HP]
- x.FUA_DMG += (e >= 6) ? 0.50 * x[Stats.HP] : 0
+ x.FUA_DMG.buff(fuaHpScaling * a[Key.HP], Source.NONE)
+ x.FUA_DMG.buff((e >= 6) ? 0.50 * a[Key.HP] : 0, Source.NONE)
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
return `
if (${wgslTrue(r.enhancedStateActive)}) {
diff --git a/src/lib/conditionals/character/Boothill.ts b/src/lib/conditionals/character/Boothill.ts
index 9952285b4..85d6da801 100644
--- a/src/lib/conditionals/character/Boothill.ts
+++ b/src/lib/conditionals/character/Boothill.ts
@@ -1,13 +1,13 @@
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
-import { BoothillConversionConditional } from 'lib/gpu/conditionals/dynamicConditionals'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
+import { ConditionalActivation, ConditionalType, Stats } from 'lib/constants'
+import { conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
+import { wgslFalse } from 'lib/gpu/injection/wgslUtils'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
import { NumberToNumberMap } from 'types/Common'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -27,104 +27,100 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
3: talent(e, 1.70, 1.87),
}
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ standoffActive: true,
+ pocketTrickshotStacks: 3,
+ beToCritBoost: true,
+ talentBreakDmgScaling: true,
+ e1DefShred: true,
+ e2BeBuff: true,
+ e4TargetStandoffVulnerability: true,
+ e6AdditionalBreakDmg: true,
+ }
+
+ const content: ContentDefinition = {
+ standoffActive: {
id: 'standoffActive',
+ formItem: 'switch',
text: t('Content.standoffActive.text'),
content: t('Content.standoffActive.content', { standoffVulnerabilityBoost: TsUtils.precisionRound(100 * standoffVulnerabilityBoost) }),
},
- {
- formItem: 'slider',
+ pocketTrickshotStacks: {
id: 'pocketTrickshotStacks',
+ formItem: 'slider',
text: t('Content.pocketTrickshotStacks.text'),
content: t('Content.pocketTrickshotStacks.content'),
min: 0,
max: 3,
},
- {
- formItem: 'switch',
+ beToCritBoost: {
id: 'beToCritBoost',
+ formItem: 'switch',
text: t('Content.beToCritBoost.text'),
content: t('Content.beToCritBoost.content'),
},
- {
- formItem: 'switch',
+ talentBreakDmgScaling: {
id: 'talentBreakDmgScaling',
+ formItem: 'switch',
text: t('Content.talentBreakDmgScaling.text'),
content: t('Content.talentBreakDmgScaling.content'),
},
- {
- formItem: 'switch',
+ e1DefShred: {
id: 'e1DefShred',
+ formItem: 'switch',
text: t('Content.e1DefShred.text'),
content: t('Content.e1DefShred.content'),
disabled: e < 1,
},
- {
- formItem: 'switch',
+ e2BeBuff: {
id: 'e2BeBuff',
+ formItem: 'switch',
text: t('Content.e2BeBuff.text'),
content: t('Content.e2BeBuff.content'),
disabled: e < 2,
},
- {
- formItem: 'switch',
+ e4TargetStandoffVulnerability: {
id: 'e4TargetStandoffVulnerability',
+ formItem: 'switch',
text: t('Content.e4TargetStandoffVulnerability.text'),
content: t('Content.e4TargetStandoffVulnerability.content'),
disabled: e < 4,
},
- {
- formItem: 'switch',
+ e6AdditionalBreakDmg: {
id: 'e6AdditionalBreakDmg',
+ formItem: 'switch',
text: t('Content.e6AdditionalBreakDmg.text'),
content: t('Content.e6AdditionalBreakDmg.content'),
disabled: e < 6,
},
- ]
-
- const teammateContent: ContentItem[] = []
-
- const defaults = {
- standoffActive: true,
- pocketTrickshotStacks: 3,
- beToCritBoost: true,
- talentBreakDmgScaling: true,
- e1DefShred: true,
- e2BeBuff: true,
- e4TargetStandoffVulnerability: true,
- e6AdditionalBreakDmg: true,
}
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => (defaults),
- teammateDefaults: () => ({}),
- initializeConfigurations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ initializeConfigurations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
if (r.talentBreakDmgScaling) {
- x.ENEMY_WEAKNESS_BROKEN = 1
+ x.ENEMY_WEAKNESS_BROKEN.set(1, Source.NONE)
}
},
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- x[Stats.BE] += (e >= 2 && r.e2BeBuff) ? 0.30 : 0
- x.VULNERABILITY += (r.standoffActive) ? standoffVulnerabilityBoost : 0
+ x.BE.buff((e >= 2 && r.e2BeBuff) ? 0.30 : 0, Source.NONE)
+ x.VULNERABILITY.buff((r.standoffActive) ? standoffVulnerabilityBoost : 0, Source.NONE)
- x.DEF_PEN += (e >= 1 && r.e1DefShred) ? 0.16 : 0
- x.VULNERABILITY += (e >= 4 && r.standoffActive && r.e4TargetStandoffVulnerability) ? 0.12 : 0
+ x.DEF_PEN.buff((e >= 1 && r.e1DefShred) ? 0.16 : 0, Source.NONE)
+ x.VULNERABILITY.buff((e >= 4 && r.standoffActive && r.e4TargetStandoffVulnerability) ? 0.12 : 0, Source.NONE)
- x.BASIC_SCALING += (r.standoffActive) ? basicEnhancedScaling : basicScaling
- x.BASIC_BREAK_EFFICIENCY_BOOST += (r.standoffActive) ? r.pocketTrickshotStacks * 0.50 : 0
+ x.BASIC_SCALING.buff((r.standoffActive) ? basicEnhancedScaling : basicScaling, Source.NONE)
+ x.BASIC_BREAK_EFFICIENCY_BOOST.buff((r.standoffActive) ? r.pocketTrickshotStacks * 0.50 : 0, Source.NONE)
- x.ULT_SCALING += ultScaling
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += (r.standoffActive) ? 60 : 30
- x.ULT_TOUGHNESS_DMG += 90
+ x.BASIC_TOUGHNESS_DMG.buff((r.standoffActive) ? 60 : 30, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(90, Source.NONE)
// Since his toughness scaling is capped at 1600% x 30, we invert the toughness scaling on the original break dmg and apply the new scaling
const newMaxToughness = Math.min(16.00 * 30, context.enemyMaxToughness)
@@ -132,14 +128,63 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const newBreakToughnessMultiplier = (0.5 + newMaxToughness / 120)
let talentBreakDmgScaling = pocketTrickshotsToTalentBreakDmg[r.pocketTrickshotStacks]
talentBreakDmgScaling += (e >= 6 && r.e6AdditionalBreakDmg) ? 0.40 : 0
- x.BASIC_BREAK_DMG_MODIFIER += (r.talentBreakDmgScaling && r.standoffActive)
- ? inverseBreakToughnessMultiplier * newBreakToughnessMultiplier * talentBreakDmgScaling
- : 0
+ x.BASIC_BREAK_DMG_MODIFIER.buff(
+ (r.talentBreakDmgScaling && r.standoffActive)
+ ? inverseBreakToughnessMultiplier * newBreakToughnessMultiplier * talentBreakDmgScaling
+ : 0
+ , Source.NONE)
return x
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
- dynamicConditionals: [BoothillConversionConditional],
+ dynamicConditionals: [{
+ id: 'BoothillConversionConditional',
+ type: ConditionalType.ABILITY,
+ activation: ConditionalActivation.CONTINUOUS,
+ dependsOn: [Stats.BE],
+ condition: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
+ const r = action.characterConditionals
+
+ return r.beToCritBoost
+ },
+ effect: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
+ const stateValue = action.conditionalState[this.id] || 0
+
+ const stateCrBuffValue = Math.min(0.30, 0.10 * stateValue)
+ const stateCdBuffValue = Math.min(1.50, 0.50 * stateValue)
+
+ const crBuffValue = Math.min(0.30, 0.10 * x.a[Key.BE])
+ const cdBuffValue = Math.min(1.50, 0.50 * x.a[Key.BE])
+
+ action.conditionalState[this.id] = x.a[Key.BE]
+
+ x.CR.buffDynamic(crBuffValue - stateCrBuffValue, Source.NONE, action, context)
+ x.CD.buffDynamic(cdBuffValue - stateCdBuffValue, Source.NONE, action, context)
+ },
+ gpu: function (action: OptimizerAction, context: OptimizerContext) {
+ const r = action.characterConditionals
+
+ return conditionalWgslWrapper(this, `
+if (${wgslFalse(r.beToCritBoost)}) {
+ return;
+}
+
+let be = (*p_x).BE;
+let stateValue = (*p_state).BoothillConversionConditional;
+
+let stateCrBuffValue = min(0.30, 0.10 * stateValue);
+let stateCdBuffValue = min(1.50, 0.50 * stateValue);
+
+let crBuffValue = min(0.30, 0.10 * be);
+let cdBuffValue = min(1.50, 0.50 * be);
+
+(*p_state).BoothillConversionConditional = be;
+
+buffDynamicCR(crBuffValue - stateCrBuffValue, p_x, p_state);
+buffDynamicCD(cdBuffValue - stateCdBuffValue, p_x, p_state);
+ `)
+ },
+ }],
}
}
diff --git a/src/lib/conditionals/character/Bronya.ts b/src/lib/conditionals/character/Bronya.ts
index 1614ecd75..fb757d5d8 100644
--- a/src/lib/conditionals/character/Bronya.ts
+++ b/src/lib/conditionals/character/Bronya.ts
@@ -1,19 +1,14 @@
-import { ASHBLAZING_ATK_STACK, BASIC_TYPE, ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardFuaAtkFinalizer,
- standardFuaAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
+import { ASHBLAZING_ATK_STACK, BASIC_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { ConditionalActivation, ConditionalType, Stats } from 'lib/constants'
-import { buffStat, conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
+import { conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
import { wgslFalse } from 'lib/gpu/injection/wgslUtils'
import { buffAbilityCr } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -30,22 +25,38 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const hitMulti = ASHBLAZING_ATK_STACK * (1 * 1 / 1)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ teamDmgBuff: true,
+ skillBuff: true,
+ ultBuff: true,
+ battleStartDefBuff: false,
+ techniqueBuff: false,
+ e2SkillSpdBuff: false,
+ }
+
+ const teammateDefaults = {
+ ...defaults,
+ ...{
+ teammateCDValue: 2.50,
+ },
+ }
+
+ const content: ContentDefinition = {
+ teamDmgBuff: {
id: 'teamDmgBuff',
+ formItem: 'switch',
text: t('Content.teamDmgBuff.text'),
content: t('Content.teamDmgBuff.content'),
},
- {
- formItem: 'switch',
+ skillBuff: {
id: 'skillBuff',
+ formItem: 'switch',
text: t('Content.skillBuff.text'),
content: t('Content.skillBuff.content', { skillDmgBoostValue: TsUtils.precisionRound(100 * skillDmgBoostValue) }),
},
- {
- formItem: 'switch',
+ ultBuff: {
id: 'ultBuff',
+ formItem: 'switch',
text: t('Content.ultBuff.text'),
content: t('Content.ultBuff.content', {
ultAtkBoostValue: TsUtils.precisionRound(100 * ultAtkBoostValue),
@@ -53,36 +64,36 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
ultCdBoostBaseValue: TsUtils.precisionRound(100 * ultCdBoostBaseValue),
}),
},
- {
- formItem: 'switch',
+ battleStartDefBuff: {
id: 'battleStartDefBuff',
+ formItem: 'switch',
text: t('Content.battleStartDefBuff.text'),
content: t('Content.battleStartDefBuff.content'),
},
- {
- formItem: 'switch',
+ techniqueBuff: {
id: 'techniqueBuff',
+ formItem: 'switch',
text: t('Content.techniqueBuff.text'),
content: t('Content.techniqueBuff.content'),
},
- {
- formItem: 'switch',
+ e2SkillSpdBuff: {
id: 'e2SkillSpdBuff',
+ formItem: 'switch',
text: t('Content.e2SkillSpdBuff.text'),
content: t('Content.e2SkillSpdBuff.content'),
disabled: e < 2,
},
- ]
-
- const teammateContent: ContentItem[] = [
- findContentId(content, 'teamDmgBuff'),
- findContentId(content, 'skillBuff'),
- findContentId(content, 'ultBuff'),
- findContentId(content, 'battleStartDefBuff'),
- findContentId(content, 'techniqueBuff'),
- {
- formItem: 'slider',
+ }
+
+ const teammateContent: ContentDefinition = {
+ teamDmgBuff: content.teamDmgBuff,
+ skillBuff: content.skillBuff,
+ ultBuff: content.ultBuff,
+ battleStartDefBuff: content.battleStartDefBuff,
+ techniqueBuff: content.techniqueBuff,
+ teammateCDValue: {
id: 'teammateCDValue',
+ formItem: 'slider',
text: t('TeammateContent.teammateCDValue.text'),
content: t('TeammateContent.teammateCDValue.content', {
ultAtkBoostValue: TsUtils.precisionRound(100 * ultAtkBoostValue),
@@ -93,57 +104,41 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
max: 3.00,
percent: true,
},
- findContentId(content, 'e2SkillSpdBuff'),
- ]
-
- const defaults = {
- teamDmgBuff: true,
- skillBuff: true,
- ultBuff: true,
- battleStartDefBuff: false,
- techniqueBuff: false,
- e2SkillSpdBuff: false,
+ e2SkillSpdBuff: content.e2SkillSpdBuff,
}
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => (defaults),
- teammateDefaults: () => ({
- ...defaults,
- ...{
- teammateCDValue: 2.50,
- },
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- // Stats
- buffAbilityCr(x, BASIC_TYPE, 1.00)
-
- // Scaling
- x.BASIC_SCALING += basicScaling
- x.FUA_SCALING += (e >= 4) ? fuaScaling : 0
-
- x.BASIC_TOUGHNESS_DMG += 30
- x.FUA_TOUGHNESS_DMG += (e >= 4) ? 30 : 0
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ buffAbilityCr(x, BASIC_TYPE, 1.00, Source.NONE)
+
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.FUA_SCALING.buff((e >= 4) ? fuaScaling : 0, Source.NONE)
+
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff((e >= 4) ? 30 : 0, Source.NONE)
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.DEF_P] += (m.battleStartDefBuff) ? 0.20 : 0
- x[Stats.SPD_P] += (m.e2SkillSpdBuff) ? 0.30 : 0
- x[Stats.ATK_P] += (m.techniqueBuff) ? 0.15 : 0
- x[Stats.ATK_P] += (m.ultBuff) ? ultAtkBoostValue : 0
+ x.DEF_P.buff((m.battleStartDefBuff) ? 0.20 : 0, Source.NONE)
+ x.SPD_P.buff((m.e2SkillSpdBuff) ? 0.30 : 0, Source.NONE)
+ x.ATK_P.buff((m.techniqueBuff) ? 0.15 : 0, Source.NONE)
+ x.ATK_P.buff((m.ultBuff) ? ultAtkBoostValue : 0, Source.NONE)
- x.ELEMENTAL_DMG += (m.teamDmgBuff) ? 0.10 : 0
- x.ELEMENTAL_DMG += (m.skillBuff) ? skillDmgBoostValue : 0
+ x.ELEMENTAL_DMG.buff((m.teamDmgBuff) ? 0.10 : 0, Source.NONE)
+ x.ELEMENTAL_DMG.buff((m.skillBuff) ? skillDmgBoostValue : 0, Source.NONE)
},
- precomputeTeammateEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const t = action.characterConditionals
+ precomputeTeammateEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const t: Conditionals = action.characterConditionals
- x[Stats.CD] += (t.ultBuff) ? ultCdBoostValue * t.teammateCDValue : 0
- x[Stats.CD] += (t.ultBuff) ? ultCdBoostBaseValue : 0
+ x.CD.buff((t.ultBuff) ? ultCdBoostValue * t.teammateCDValue : 0, Source.NONE)
+ x.CD.buff((t.ultBuff) ? ultCdBoostBaseValue : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardFuaAtkFinalizer(x, action, context, hitMulti)
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
@@ -156,30 +151,30 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
activation: ConditionalActivation.CONTINUOUS,
dependsOn: [Stats.CD],
ratioConversion: true,
- condition: function (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) {
+ condition: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
return true
},
- effect: function (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ effect: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
+ const r: Conditionals = action.characterConditionals
if (!r.ultBuff) {
return
}
const stateValue = action.conditionalState[this.id] || 0
- const convertibleCdValue = x[Stats.CD] - x.RATIO_BASED_CD_BUFF
+ const convertibleCdValue = x.a[Key.CD] - x.a[Key.RATIO_BASED_CD_BUFF]
const buffCD = ultCdBoostValue * convertibleCdValue + ultCdBoostBaseValue
const stateBuffCD = ultCdBoostValue * stateValue + ultCdBoostBaseValue
- action.conditionalState[this.id] = x[Stats.CD]
+ action.conditionalState[this.id] = x.a[Key.CD]
const finalBuffCd = buffCD - (stateValue ? stateBuffCD : 0)
- x.RATIO_BASED_CD_BUFF += finalBuffCd
+ x.RATIO_BASED_CD_BUFF.buff(finalBuffCd, Source.NONE)
- buffStat(x, Stats.CD, finalBuffCd, action, context)
+ x.CD.buffDynamic(finalBuffCd, Source.NONE, action, context)
},
gpu: function (action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
return conditionalWgslWrapper(this, `
if (${wgslFalse(r.ultBuff)}) {
diff --git a/src/lib/conditionals/character/Clara.ts b/src/lib/conditionals/character/Clara.ts
index facac74c5..5f1343b3a 100644
--- a/src/lib/conditionals/character/Clara.ts
+++ b/src/lib/conditionals/character/Clara.ts
@@ -1,13 +1,12 @@
-import { ASHBLAZING_ATK_STACK, ComputedStatsObject, FUA_TYPE } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ASHBLAZING_ATK_STACK, FUA_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityDmg } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
import { NumberToNumberMap } from 'types/Common'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -29,81 +28,81 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const hitMultiSingle = ASHBLAZING_ATK_STACK * (1 * 1 / 1)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ ultBuff: true,
+ talentEnemyMarked: true,
+ e2UltAtkBuff: true,
+ e4DmgReductionBuff: true,
+ }
+
+ const content: ContentDefinition = {
+ ultBuff: {
id: 'ultBuff',
+ formItem: 'switch',
text: t('Content.ultBuff.text'),
content: t('Content.ultBuff.content', {
ultFuaExtraScaling: TsUtils.precisionRound(100 * ultFuaExtraScaling),
ultDmgReductionValue: TsUtils.precisionRound((100 * ultDmgReductionValue)),
}),
},
- {
- formItem: 'switch',
+ talentEnemyMarked: {
id: 'talentEnemyMarked',
+ formItem: 'switch',
text: t('Content.talentEnemyMarked.text'),
content: t('Content.talentEnemyMarked.content', { skillScaling: TsUtils.precisionRound(100 * skillScaling) }),
},
- {
- formItem: 'switch',
+ e2UltAtkBuff: {
id: 'e2UltAtkBuff',
+ formItem: 'switch',
text: t('Content.e2UltAtkBuff.text'),
content: t('Content.e2UltAtkBuff.content'),
disabled: e < 2,
},
- {
- formItem: 'switch',
+ e4DmgReductionBuff: {
id: 'e4DmgReductionBuff',
+ formItem: 'switch',
text: t('Content.e4DmgReductionBuff.text'),
content: t('Content.e4DmgReductionBuff.content'),
disabled: e < 4,
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => ({
- ultBuff: true,
- talentEnemyMarked: true,
- e2UltAtkBuff: true,
- e4DmgReductionBuff: true,
- }),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.ATK_P] += (e >= 2 && r.e2UltAtkBuff) ? 0.30 : 0
+ x.ATK_P.buff((e >= 2 && r.e2UltAtkBuff) ? 0.30 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.SKILL_SCALING += r.talentEnemyMarked ? skillScaling : 0
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.SKILL_SCALING.buff(r.talentEnemyMarked ? skillScaling : 0, Source.NONE)
- x.FUA_SCALING += fuaScaling
- x.FUA_SCALING += r.ultBuff ? ultFuaExtraScaling : 0
+ x.FUA_SCALING.buff(fuaScaling, Source.NONE)
+ x.FUA_SCALING.buff(r.ultBuff ? ultFuaExtraScaling : 0, Source.NONE)
// Boost
- x.DMG_RED_MULTI *= (1 - 0.10)
- x.DMG_RED_MULTI *= r.ultBuff ? (1 - ultDmgReductionValue) : 1
- x.DMG_RED_MULTI *= (e >= 4 && r.e4DmgReductionBuff) ? (1 - 0.30) : 1
- buffAbilityDmg(x, FUA_TYPE, 0.30)
+ x.DMG_RED_MULTI.multiply((1 - 0.10), Source.NONE)
+ x.DMG_RED_MULTI.multiply((r.ultBuff) ? (1 - ultDmgReductionValue) : 1, Source.NONE)
+ x.DMG_RED_MULTI.multiply((e >= 4 && r.e4DmgReductionBuff) ? (1 - 0.30) : 1, Source.NONE)
+ buffAbilityDmg(x, FUA_TYPE, 0.30, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 30
- x.FUA_TOUGHNESS_DMG += 30
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff(30, Source.NONE)
return x
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
const hitMulti = r.ultBuff ? hitMultiByTargetsBlast[context.enemyCount] : hitMultiSingle
standardFuaAtkFinalizer(x, action, context, hitMulti)
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
const hitMulti = r.ultBuff ? hitMultiByTargetsBlast[context.enemyCount] : hitMultiSingle
return gpuStandardFuaAtkFinalizer(hitMulti)
},
diff --git a/src/lib/conditionals/character/DanHeng.ts b/src/lib/conditionals/character/DanHeng.ts
index 679b916ab..aece4e46e 100644
--- a/src/lib/conditionals/character/DanHeng.ts
+++ b/src/lib/conditionals/character/DanHeng.ts
@@ -1,12 +1,11 @@
-import { BASIC_TYPE, ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { BASIC_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityDmg } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
// TODO: missing A4 SPD buff
@@ -21,60 +20,60 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const ultScaling = ult(e, 4.00, 4.32)
const ultExtraScaling = ult(e, 1.20, 1.296)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ talentPenBuff: true,
+ enemySlowed: true,
+ e1EnemyHp50: true,
+ }
+
+ const content: ContentDefinition = {
+ talentPenBuff: {
id: 'talentPenBuff',
+ formItem: 'switch',
text: t('Content.talentPenBuff.text'),
content: t('Content.talentPenBuff.content', { extraPenValue: TsUtils.precisionRound(100 * extraPenValue) }),
},
- {
- formItem: 'switch',
+ enemySlowed: {
id: 'enemySlowed',
+ formItem: 'switch',
text: t('Content.enemySlowed.text'),
content: t('Content.enemySlowed.content'),
},
- {
- formItem: 'switch',
+ e1EnemyHp50: {
id: 'e1EnemyHp50',
+ formItem: 'switch',
text: t('Content.e1EnemyHp50.text'),
content: t('Content.e1EnemyHp50.content'),
disabled: e < 1,
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => ({
- talentPenBuff: true,
- enemySlowed: true,
- e1EnemyHp50: true,
- }),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.CR] += (e >= 1 && r.e1EnemyHp50) ? 0.12 : 0
+ x.CR.buff((e >= 1 && r.e1EnemyHp50) ? 0.12 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
- x.ULT_SCALING += (r.enemySlowed) ? ultExtraScaling : 0
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.ULT_SCALING.buff((r.enemySlowed) ? ultExtraScaling : 0, Source.NONE)
// Boost
- x.RES_PEN += (r.talentPenBuff) ? extraPenValue : 0
- buffAbilityDmg(x, BASIC_TYPE, 0.40, (r.enemySlowed))
+ x.RES_PEN.buff((r.talentPenBuff) ? extraPenValue : 0, Source.NONE)
+ buffAbilityDmg(x, BASIC_TYPE, (r.enemySlowed) ? 0.40 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 90
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(90, Source.NONE)
return x
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/DrRatio.ts b/src/lib/conditionals/character/DrRatio.ts
index c319daef9..6d9a6ea91 100644
--- a/src/lib/conditionals/character/DrRatio.ts
+++ b/src/lib/conditionals/character/DrRatio.ts
@@ -1,13 +1,12 @@
-import { ASHBLAZING_ATK_STACK, ComputedStatsObject, FUA_TYPE } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ASHBLAZING_ATK_STACK, FUA_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityDmg } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
import { NumberToNumberMap } from 'types/Common'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -38,14 +37,19 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
}
function getHitMulti(action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
return e >= 2
? fuaMultiByDebuffs[Math.min(4, r.enemyDebuffStacks)]
: baseHitMulti
}
- const content: ContentItem[] = [
- {
+ const defaults = {
+ enemyDebuffStacks: debuffStacksMax,
+ summationStacks: summationStacksMax,
+ }
+
+ const content: ContentDefinition = {
+ summationStacks: {
id: 'summationStacks',
formItem: 'slider',
text: t('Content.summationStacks.text'),
@@ -53,7 +57,7 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
min: 0,
max: summationStacksMax,
},
- {
+ enemyDebuffStacks: {
id: 'enemyDebuffStacks',
formItem: 'slider',
text: t('Content.enemyDebuffStacks.text'),
@@ -61,41 +65,36 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
min: 0,
max: debuffStacksMax,
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => ({
- enemyDebuffStacks: debuffStacksMax,
- summationStacks: summationStacksMax,
- }),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.CR] += r.summationStacks * 0.025
- x[Stats.CD] += r.summationStacks * 0.05
+ x.CR.buff(r.summationStacks * 0.025, Source.NONE)
+ x.CD.buff(r.summationStacks * 0.05, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
- x.FUA_SCALING += fuaScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.FUA_SCALING.buff(fuaScaling, Source.NONE)
// Boost
- x.ELEMENTAL_DMG += (r.enemyDebuffStacks >= 3) ? Math.min(0.50, r.enemyDebuffStacks * 0.10) : 0
- buffAbilityDmg(x, FUA_TYPE, 0.50, (e >= 6))
+ x.ELEMENTAL_DMG.buff((r.enemyDebuffStacks >= 3) ? Math.min(0.50, r.enemyDebuffStacks * 0.10) : 0, Source.NONE)
+ buffAbilityDmg(x, FUA_TYPE, (e >= 6) ? 0.50 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 90
- x.FUA_TOUGHNESS_DMG += 30
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(90, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff(30, Source.NONE)
return x
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardFuaAtkFinalizer(x, action, context, getHitMulti(action, context))
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
diff --git a/src/lib/conditionals/character/Feixiao.ts b/src/lib/conditionals/character/Feixiao.ts
index 7e5472a08..8b954f2f8 100644
--- a/src/lib/conditionals/character/Feixiao.ts
+++ b/src/lib/conditionals/character/Feixiao.ts
@@ -1,12 +1,11 @@
-import { ASHBLAZING_ATK_STACK, ComputedStatsObject, FUA_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, calculateAshblazingSet } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ASHBLAZING_ATK_STACK, FUA_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, calculateAshblazingSet, Conditionals, ContentDefinition } from 'lib/conditionals/conditionalUtils'
import { buffAbilityCd } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -34,132 +33,127 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
+ 8 * 0.2285)
function getUltHitMulti(action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
return r.weaknessBrokenUlt
? ASHBLAZING_ATK_STACK * ultBrokenHitCountMulti
: ASHBLAZING_ATK_STACK * ultHitCountMulti
}
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ weaknessBrokenUlt: true,
+ talentDmgBuff: true,
+ skillAtkBuff: true,
+ e1OriginalDmgBoost: true,
+ e4Buffs: true,
+ e6Buffs: true,
+ }
+
+ const content: ContentDefinition = {
+ weaknessBrokenUlt: {
id: 'weaknessBrokenUlt',
+ formItem: 'switch',
text: t('Content.weaknessBrokenUlt.text'),
content: t('Content.weaknessBrokenUlt.content'),
},
- {
- formItem: 'switch',
+ talentDmgBuff: {
id: 'talentDmgBuff',
+ formItem: 'switch',
text: t('Content.talentDmgBuff.text'),
content: t('Content.talentDmgBuff.content', {
FuaMultiplier: TsUtils.precisionRound(100 * fuaScaling),
DmgBuff: TsUtils.precisionRound(100 * talentDmgBuff),
}),
},
- {
- formItem: 'switch',
+ skillAtkBuff: {
id: 'skillAtkBuff',
+ formItem: 'switch',
text: t('Content.skillAtkBuff.text'),
content: t('Content.skillAtkBuff.content'),
},
- {
- formItem: 'switch',
+ e1OriginalDmgBoost: {
id: 'e1OriginalDmgBoost',
+ formItem: 'switch',
text: t('Content.e1OriginalDmgBoost.text'),
content: t('Content.e1OriginalDmgBoost.content'),
disabled: e < 1,
},
- {
- formItem: 'switch',
+ e4Buffs: {
id: 'e4Buffs',
+ formItem: 'switch',
text: t('Content.e4Buffs.text'),
content: t('Content.e4Buffs.content'),
disabled: e < 4,
},
- {
- formItem: 'switch',
+ e6Buffs: {
id: 'e6Buffs',
+ formItem: 'switch',
text: t('Content.e6Buffs.text'),
content: t('Content.e6Buffs.content'),
disabled: e < 6,
},
- ]
-
- const teammateContent: ContentItem[] = []
-
- const defaults = {
- ultStacks: 6,
- weaknessBrokenUlt: true,
- talentDmgBuff: true,
- skillAtkBuff: true,
- e1OriginalDmgBoost: true,
- e4Buffs: true,
- e6Buffs: true,
}
return {
- content: () => content,
- teammateContent: () => teammateContent,
+ content: () => Object.values(content),
defaults: () => defaults,
- teammateDefaults: () => ({}),
- initializeConfigurations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ initializeConfigurations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- x.ULT_DMG_TYPE = ULT_TYPE | FUA_TYPE
+ x.ULT_DMG_TYPE.set(ULT_TYPE | FUA_TYPE, Source.NONE)
if (r.weaknessBrokenUlt) {
- x.ENEMY_WEAKNESS_BROKEN = 1
+ x.ENEMY_WEAKNESS_BROKEN.set(1, Source.NONE)
}
if (e >= 6 && r.e6Buffs) {
- x.FUA_DMG_TYPE = ULT_TYPE | FUA_TYPE
+ x.FUA_DMG_TYPE.set(ULT_TYPE | FUA_TYPE, Source.NONE)
}
},
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Special case where we force the weakness break on if the ult break option is enabled
if (!r.weaknessBrokenUlt) {
- x.ULT_BREAK_EFFICIENCY_BOOST += 1.00
+ x.ULT_BREAK_EFFICIENCY_BOOST.buff(1.00, Source.NONE)
}
- buffAbilityCd(x, FUA_TYPE, 0.36)
+ buffAbilityCd(x, FUA_TYPE, 0.36, Source.NONE)
- x[Stats.ATK_P] += (r.skillAtkBuff) ? 0.48 : 0
- x.ELEMENTAL_DMG += (r.talentDmgBuff) ? talentDmgBuff : 0
+ x.ATK_P.buff((r.skillAtkBuff) ? 0.48 : 0, Source.NONE)
+ x.ELEMENTAL_DMG.buff((r.talentDmgBuff) ? talentDmgBuff : 0, Source.NONE)
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.FUA_SCALING += fuaScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.FUA_SCALING.buff(fuaScaling, Source.NONE)
- x.ULT_SCALING += 6 * (ultScaling + ultBrokenScaling) + ultFinalScaling
+ x.ULT_SCALING.buff(6 * (ultScaling + ultBrokenScaling) + ultFinalScaling, Source.NONE)
- x.ULT_ORIGINAL_DMG_BOOST += (e >= 1 && r.e1OriginalDmgBoost) ? 0.3071 : 0
+ x.ULT_ORIGINAL_DMG_BOOST.buff((e >= 1 && r.e1OriginalDmgBoost) ? 0.3071 : 0, Source.NONE)
if (e >= 4) {
- x[Stats.SPD_P] += 0.08
- x.FUA_TOUGHNESS_DMG += 15
+ x.SPD_P.buff(0.08, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff(15, Source.NONE)
}
if (e >= 6 && r.e6Buffs) {
- x.RES_PEN += 0.20
- x.FUA_SCALING += 1.40
+ x.RES_PEN.buff(0.20, Source.NONE)
+ x.FUA_SCALING.buff(1.40, Source.NONE)
}
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 90
- x.FUA_TOUGHNESS_DMG += 15
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(90, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff(15, Source.NONE)
return x
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- x.BASIC_DMG += x.BASIC_SCALING * x[Stats.ATK]
- x.SKILL_DMG += x.SKILL_SCALING * x[Stats.ATK]
- x.ULT_DMG += x.ULT_SCALING * (x[Stats.ATK] + calculateAshblazingSet(x, action, context, getUltHitMulti(action, context)))
- x.FUA_DMG += x.FUA_SCALING * (x[Stats.ATK] + calculateAshblazingSet(x, action, context, ASHBLAZING_ATK_STACK * (1 * 1.00)))
- x.DOT_DMG += x.DOT_SCALING * x[Stats.ATK]
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ x.BASIC_DMG.buff(x.a[Key.BASIC_SCALING] * x.a[Key.ATK], Source.NONE)
+ x.SKILL_DMG.buff(x.a[Key.SKILL_SCALING] * x.a[Key.ATK], Source.NONE)
+ x.ULT_DMG.buff(x.a[Key.ULT_SCALING] * (x.a[Key.ATK] + calculateAshblazingSet(x, action, context, getUltHitMulti(action, context))), Source.NONE)
+ x.FUA_DMG.buff(x.a[Key.FUA_SCALING] * (x.a[Key.ATK] + calculateAshblazingSet(x, action, context, ASHBLAZING_ATK_STACK * (1 * 1.00))), Source.NONE)
+ x.DOT_DMG.buff(x.a[Key.DOT_SCALING] * x.a[Key.ATK], Source.NONE)
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
return `
diff --git a/src/lib/conditionals/character/Firefly.ts b/src/lib/conditionals/character/Firefly.ts
index c7e937e7f..ec9529da1 100644
--- a/src/lib/conditionals/character/Firefly.ts
+++ b/src/lib/conditionals/character/Firefly.ts
@@ -1,14 +1,13 @@
-import { BREAK_TYPE, ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { BREAK_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition } from 'lib/conditionals/conditionalUtils'
import { FireflyConversionConditional } from 'lib/gpu/conditionals/dynamicConditionals'
import { wgslTrue } from 'lib/gpu/injection/wgslUtils'
import { buffAbilityVulnerability } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -26,126 +25,124 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const talentResBuff = talent(e, 0.30, 0.34)
const talentDmgReductionBuff = talent(e, 0.40, 0.44)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ enhancedStateActive: true,
+ enhancedStateSpdBuff: true,
+ superBreakDmg: true,
+ atkToBeConversion: true,
+ talentDmgReductionBuff: true,
+ e1DefShred: true,
+ e4ResBuff: true,
+ e6Buffs: true,
+ }
+
+ const content: ContentDefinition = {
+ enhancedStateActive: {
id: 'enhancedStateActive',
+ formItem: 'switch',
text: t('Content.enhancedStateActive.text'),
content: t('Content.enhancedStateActive.content'),
},
- {
- formItem: 'switch',
+ enhancedStateSpdBuff: {
id: 'enhancedStateSpdBuff',
+ formItem: 'switch',
text: t('Content.enhancedStateSpdBuff.text'),
content: t('Content.enhancedStateSpdBuff.content', { ultSpdBuff }),
},
- {
- formItem: 'switch',
+ superBreakDmg: {
id: 'superBreakDmg',
+ formItem: 'switch',
text: t('Content.superBreakDmg.text'),
content: t('Content.superBreakDmg.content'),
},
- {
- formItem: 'switch',
+ atkToBeConversion: {
id: 'atkToBeConversion',
+ formItem: 'switch',
text: t('Content.atkToBeConversion.text'),
content: t('Content.atkToBeConversion.content'),
},
- {
- formItem: 'switch',
+ talentDmgReductionBuff: {
id: 'talentDmgReductionBuff',
+ formItem: 'switch',
text: t('Content.talentDmgReductionBuff.text'),
content: t('Content.talentDmgReductionBuff.content', {
talentResBuff: TsUtils.precisionRound(100 * talentResBuff),
talentDmgReductionBuff: TsUtils.precisionRound(100 * talentDmgReductionBuff),
}),
},
- {
- formItem: 'switch',
+ e1DefShred: {
id: 'e1DefShred',
+ formItem: 'switch',
text: t('Content.e1DefShred.text'),
content: t('Content.e1DefShred.content'),
disabled: e < 1,
},
- {
- formItem: 'switch',
+ e4ResBuff: {
id: 'e4ResBuff',
+ formItem: 'switch',
text: t('Content.e4ResBuff.text'),
content: t('Content.e4ResBuff.content'),
disabled: e < 4,
},
- {
- formItem: 'switch',
+ e6Buffs: {
id: 'e6Buffs',
+ formItem: 'switch',
text: t('Content.e6Buffs.text'),
content: t('Content.e6Buffs.content'),
disabled: e < 6,
},
- ]
-
- const teammateContent: ContentItem[] = []
-
- const defaults = {
- enhancedStateActive: true,
- enhancedStateSpdBuff: true,
- superBreakDmg: true,
- atkToBeConversion: true,
- talentDmgReductionBuff: true,
- e1DefShred: true,
- e4ResBuff: true,
- e6Buffs: true,
}
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => (defaults),
- teammateDefaults: () => ({}),
- initializeConfigurations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ initializeConfigurations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
if (r.superBreakDmg) {
- x.ENEMY_WEAKNESS_BROKEN = 1
+ x.ENEMY_WEAKNESS_BROKEN.set(1, Source.NONE)
}
},
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- x[Stats.RES] += (r.enhancedStateActive) ? talentResBuff : 0
- x[Stats.SPD] += (r.enhancedStateActive && r.enhancedStateSpdBuff) ? ultSpdBuff : 0
- x.BREAK_EFFICIENCY_BOOST += (r.enhancedStateActive) ? 0.50 : 0
- x.DMG_RED_MULTI *= (r.enhancedStateActive && r.talentDmgReductionBuff) ? (1 - talentDmgReductionBuff) : 1
+ x.RES.buff((r.enhancedStateActive) ? talentResBuff : 0, Source.NONE)
+ x.SPD.buff((r.enhancedStateActive && r.enhancedStateSpdBuff) ? ultSpdBuff : 0, Source.NONE)
+ x.BREAK_EFFICIENCY_BOOST.buff((r.enhancedStateActive) ? 0.50 : 0, Source.NONE)
+ x.DMG_RED_MULTI.multiply((r.enhancedStateActive && r.talentDmgReductionBuff) ? (1 - talentDmgReductionBuff) : 1, Source.NONE)
// Should be skill def pen but skill doesnt apply to super break
- x.DEF_PEN += (e >= 1 && r.e1DefShred && r.enhancedStateActive) ? 0.15 : 0
- x[Stats.RES] += (e >= 4 && r.e4ResBuff && r.enhancedStateActive) ? 0.50 : 0
- x.FIRE_RES_PEN += (e >= 6 && r.e6Buffs && r.enhancedStateActive) ? 0.20 : 0
- x.BREAK_EFFICIENCY_BOOST += (e >= 6 && r.e6Buffs && r.enhancedStateActive) ? 0.50 : 0
+ x.DEF_PEN.buff((e >= 1 && r.e1DefShred && r.enhancedStateActive) ? 0.15 : 0, Source.NONE)
+ x.RES.buff((e >= 4 && r.e4ResBuff && r.enhancedStateActive) ? 0.50 : 0, Source.NONE)
+ x.FIRE_RES_PEN.buff((e >= 6 && r.e6Buffs && r.enhancedStateActive) ? 0.20 : 0, Source.NONE)
+ x.BREAK_EFFICIENCY_BOOST.buff((e >= 6 && r.e6Buffs && r.enhancedStateActive) ? 0.50 : 0, Source.NONE)
- x.BASIC_SCALING += (r.enhancedStateActive) ? basicEnhancedScaling : basicScaling
+ x.BASIC_SCALING.buff((r.enhancedStateActive) ? basicEnhancedScaling : basicScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += (r.enhancedStateActive) ? 45 : 30
- x.SKILL_TOUGHNESS_DMG += (r.enhancedStateActive) ? 90 : 60
+ x.BASIC_TOUGHNESS_DMG.buff((r.enhancedStateActive) ? 45 : 30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff((r.enhancedStateActive) ? 90 : 60, Source.NONE)
return x
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- buffAbilityVulnerability(x, BREAK_TYPE, ultWeaknessBrokenBreakVulnerability, (r.enhancedStateActive && x.ENEMY_WEAKNESS_BROKEN))
+ buffAbilityVulnerability(x, BREAK_TYPE, (r.enhancedStateActive && x.a[Key.ENEMY_WEAKNESS_BROKEN]) ? ultWeaknessBrokenBreakVulnerability : 0, Source.NONE)
- x.SUPER_BREAK_MODIFIER += (r.superBreakDmg && r.enhancedStateActive && x[Stats.BE] >= 2.00) ? 0.35 : 0
- x.SUPER_BREAK_MODIFIER += (r.superBreakDmg && r.enhancedStateActive && x[Stats.BE] >= 3.60) ? 0.15 : 0
+ x.SUPER_BREAK_MODIFIER.buff((r.superBreakDmg && r.enhancedStateActive && x.a[Key.BE] >= 2.00) ? 0.35 : 0, Source.NONE)
+ x.SUPER_BREAK_MODIFIER.buff((r.superBreakDmg && r.enhancedStateActive && x.a[Key.BE] >= 3.60) ? 0.15 : 0, Source.NONE)
- x.SKILL_SCALING += (r.enhancedStateActive)
- ? (0.2 * Math.min(3.60, x[Stats.BE]) + skillEnhancedAtkScaling)
- : skillScaling
+ x.SKILL_SCALING.buff(
+ (r.enhancedStateActive)
+ ? (0.2 * Math.min(3.60, x.a[Key.BE]) + skillEnhancedAtkScaling)
+ : skillScaling
+ , Source.NONE)
- x.BASIC_DMG += x.BASIC_SCALING * x[Stats.ATK]
- x.SKILL_DMG += x.SKILL_SCALING * x[Stats.ATK]
+ x.BASIC_DMG.buff(x.a[Key.BASIC_SCALING] * x.a[Key.ATK], Source.NONE)
+ x.SKILL_DMG.buff(x.a[Key.SKILL_SCALING] * x.a[Key.ATK], Source.NONE)
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
return `
buffAbilityVulnerability(p_x, BREAK_TYPE, ${ultWeaknessBrokenBreakVulnerability}, select(0, 1, ${wgslTrue(r.enhancedStateActive)} && x.ENEMY_WEAKNESS_BROKEN >= 1));
diff --git a/src/lib/conditionals/character/FuXuan.ts b/src/lib/conditionals/character/FuXuan.ts
index 1d02147e1..4e56f60f4 100644
--- a/src/lib/conditionals/character/FuXuan.ts
+++ b/src/lib/conditionals/character/FuXuan.ts
@@ -1,20 +1,21 @@
-import { ComputedStatsObject, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { ULT_TYPE } from 'lib/conditionals/conditionalConstants'
import {
AbilityEidolon,
- findContentId,
+ Conditionals,
+ ContentDefinition,
gpuStandardHpFinalizer,
gpuStandardHpHealFinalizer,
standardHpFinalizer,
standardHpHealFinalizer,
} from 'lib/conditionals/conditionalUtils'
import { ConditionalActivation, ConditionalType, Stats } from 'lib/constants'
-import { buffStat, conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
+import { conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
import { wgslFalse } from 'lib/gpu/injection/wgslUtils'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -32,25 +33,37 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const ultHealScaling = 0.05
const ultHealFlat = 133
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ skillActive: true,
+ talentActive: true,
+ e6TeamHpLostPercent: 1.2,
+ }
+
+ const teammateDefaults = {
+ skillActive: true,
+ talentActive: true,
+ teammateHPValue: 8000,
+ }
+
+ const content: ContentDefinition = {
+ talentActive: {
id: 'talentActive',
+ formItem: 'switch',
text: t('Content.talentActive.text'),
content: t('Content.talentActive.content', { talentDmgReductionValue: TsUtils.precisionRound(100 * talentDmgReductionValue) }),
},
- {
- formItem: 'switch',
+ skillActive: {
id: 'skillActive',
+ formItem: 'switch',
text: t('Content.skillActive.text'),
content: t('Content.skillActive.content', {
skillHpBuffValue: TsUtils.precisionRound(100 * skillHpBuffValue),
skillCrBuffValue: TsUtils.precisionRound(100 * skillCrBuffValue),
}),
},
- {
- formItem: 'slider',
+ e6TeamHpLostPercent: {
id: 'e6TeamHpLostPercent',
+ formItem: 'slider',
text: t('Content.e6TeamHpLostPercent.text'),
content: t('Content.e6TeamHpLostPercent.content'),
min: 0,
@@ -58,69 +71,61 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
percent: true,
disabled: e < 6,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'talentActive'),
- findContentId(content, 'skillActive'),
- {
- formItem: 'slider',
+ const teammateContent: ContentDefinition = {
+ talentActive: content.talentActive,
+ skillActive: content.skillActive,
+ teammateHPValue: {
id: 'teammateHPValue',
+ formItem: 'slider',
text: t('TeammateContent.teammateHPValue.text'),
content: t('TeammateContent.teammateHPValue.content', { skillHpBuffValue: TsUtils.precisionRound(100 * skillHpBuffValue) }),
min: 0,
max: 10000,
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- skillActive: true,
- talentActive: true,
- e6TeamHpLostPercent: 1.2,
- }),
- teammateDefaults: () => ({
- skillActive: true,
- talentActive: true,
- teammateHPValue: 8000,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling + ((e >= 6) ? 2.00 * r.e6TeamHpLostPercent : 0)
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling + ((e >= 6) ? 2.00 * r.e6TeamHpLostPercent : 0), Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.ULT_TOUGHNESS_DMG += 60
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
- x.HEAL_TYPE = ULT_TYPE
- x.HEAL_SCALING += ultHealScaling
- x.HEAL_FLAT += ultHealFlat
+ x.HEAL_TYPE.set(ULT_TYPE, Source.NONE)
+ x.HEAL_SCALING.buff(ultHealScaling, Source.NONE)
+ x.HEAL_FLAT.buff(ultHealFlat, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.CR] += (m.skillActive) ? skillCrBuffValue : 0
- x[Stats.CD] += (e >= 1 && m.skillActive) ? 0.30 : 0
+ x.CR.buff((m.skillActive) ? skillCrBuffValue : 0, Source.NONE)
+ x.CD.buff((e >= 1 && m.skillActive) ? 0.30 : 0, Source.NONE)
// Talent ehp buff is shared
- x.DMG_RED_MULTI *= (m.talentActive) ? (1 - talentDmgReductionValue) : 1
+ x.DMG_RED_MULTI.multiply((m.talentActive) ? (1 - talentDmgReductionValue) : 1, Source.NONE)
},
- precomputeTeammateEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const t = action.characterConditionals
+ precomputeTeammateEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const t: Conditionals = action.characterConditionals
- x[Stats.HP] += (t.skillActive) ? skillHpBuffValue * t.teammateHPValue : 0
+ x.HP.buff((t.skillActive) ? skillHpBuffValue * t.teammateHPValue : 0, Source.NONE)
// Skill ehp buff only applies to teammates
- x.DMG_RED_MULTI *= (t.skillActive) ? (1 - 0.65) : 1
+ x.DMG_RED_MULTI.multiply((t.skillActive) ? (1 - 0.65) : 1, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardHpFinalizer(x)
standardHpHealFinalizer(x)
},
@@ -137,27 +142,27 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
condition: function () {
return true
},
- effect: function (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ effect: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
+ const r: Conditionals = action.characterConditionals
if (!r.skillActive) {
return
}
const stateValue = action.conditionalState[this.id] || 0
- const convertibleHpValue = x[Stats.HP] - x.RATIO_BASED_HP_BUFF
+ const convertibleHpValue = x.a[Key.HP] - x.a[Key.RATIO_BASED_HP_BUFF]
const buffHP = skillHpBuffValue * convertibleHpValue
const stateBuffHP = skillHpBuffValue * stateValue
- action.conditionalState[this.id] = x[Stats.HP]
+ action.conditionalState[this.id] = x.a[Key.HP]
const finalBuffHp = buffHP - (stateValue ? stateBuffHP : 0)
- x.RATIO_BASED_HP_BUFF += finalBuffHp
+ x.a[Key.RATIO_BASED_HP_BUFF] += finalBuffHp
- buffStat(x, Stats.HP, finalBuffHp, action, context)
+ x.HP.buffDynamic(finalBuffHp, Source.NONE, action, context)
},
gpu: function (action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
return conditionalWgslWrapper(this, `
if (${wgslFalse(r.skillActive)}) {
diff --git a/src/lib/conditionals/character/Fugue.ts b/src/lib/conditionals/character/Fugue.ts
index 3f642bace..c317ad4fb 100644
--- a/src/lib/conditionals/character/Fugue.ts
+++ b/src/lib/conditionals/character/Fugue.ts
@@ -1,12 +1,10 @@
import i18next from 'i18next'
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-
-import { AbilityEidolon, findContentId, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { CURRENT_DATA_VERSION, Stats } from 'lib/constants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
+import { CURRENT_DATA_VERSION } from 'lib/constants'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -20,128 +18,128 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const ultScaling = ult(e, 2.00, 2.20)
const superBreakScaling = talent(e, 1.00, 1.10)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ torridScorch: true,
+ foxianPrayer: false,
+ defReduction: true,
+ superBreakDmg: true,
+ e4BreakDmg: true,
+ e6BreakEfficiency: true,
+ }
+
+ const teammateDefaults = {
+ foxianPrayer: true,
+ be250Buff: true,
+ weaknessBreakBeStacks: 2,
+ defReduction: true,
+ superBreakDmg: true,
+ e4BreakDmg: true,
+ }
+
+ const content: ContentDefinition = {
+ torridScorch: {
id: 'torridScorch',
+ formItem: 'switch',
text: 'Torrid Scorch state',
content: i18next.t('BetaMessage', { ns: 'conditionals', Version: CURRENT_DATA_VERSION }),
},
- {
- formItem: 'switch',
+ foxianPrayer: {
id: 'foxianPrayer',
+ formItem: 'switch',
text: 'Foxian Prayer BE buff',
content: i18next.t('BetaMessage', { ns: 'conditionals', Version: CURRENT_DATA_VERSION }),
},
- {
- formItem: 'switch',
+ defReduction: {
id: 'defReduction',
+ formItem: 'switch',
text: 'Skill DEF shred',
content: i18next.t('BetaMessage', { ns: 'conditionals', Version: CURRENT_DATA_VERSION }),
},
- {
- formItem: 'switch',
+ superBreakDmg: {
id: 'superBreakDmg',
+ formItem: 'switch',
text: 'Super Break DMG (force weakness break)',
content: i18next.t('BetaMessage', { ns: 'conditionals', Version: CURRENT_DATA_VERSION }),
},
- {
- formItem: 'switch',
+ e4BreakDmg: {
id: 'e4BreakDmg',
+ formItem: 'switch',
text: 'E4 Break DMG boost',
content: i18next.t('BetaMessage', { ns: 'conditionals', Version: CURRENT_DATA_VERSION }),
disabled: e < 4,
},
- {
- formItem: 'switch',
+ e6BreakEfficiency: {
id: 'e6BreakEfficiency',
+ formItem: 'switch',
text: 'E6 break efficiency boost',
content: i18next.t('BetaMessage', { ns: 'conditionals', Version: CURRENT_DATA_VERSION }),
disabled: e < 6,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'foxianPrayer'),
- {
- formItem: 'switch',
+ const teammateContent: ContentDefinition = {
+ foxianPrayer: content.foxianPrayer,
+ be250Buff: {
id: 'be250Buff',
+ formItem: 'switch',
text: 'BE ≥ 250%',
content: i18next.t('BetaMessage', { ns: 'conditionals', Version: CURRENT_DATA_VERSION }),
},
- {
- formItem: 'slider',
+ weaknessBreakBeStacks: {
id: 'weaknessBreakBeStacks',
+ formItem: 'slider',
text: 'Enemy broken BE stacks',
content: i18next.t('BetaMessage', { ns: 'conditionals', Version: CURRENT_DATA_VERSION }),
min: 0,
max: 2,
},
- findContentId(content, 'defReduction'),
- findContentId(content, 'superBreakDmg'),
- findContentId(content, 'e4BreakDmg'),
- ]
-
- const defaults = {
- torridScorch: true,
- foxianPrayer: false,
- defReduction: true,
- superBreakDmg: true,
- e4BreakDmg: true,
- e6BreakEfficiency: true,
- }
-
- const teammateDefaults = {
- foxianPrayer: true,
- be250Buff: true,
- weaknessBreakBeStacks: 2,
- defReduction: true,
- superBreakDmg: true,
- e4BreakDmg: true,
+ defReduction: content.defReduction,
+ superBreakDmg: content.superBreakDmg,
+ e4BreakDmg: content.e4BreakDmg,
}
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => (defaults),
- teammateDefaults: () => (teammateDefaults),
- initializeConfigurations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ initializeConfigurations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
if (r.superBreakDmg) {
- x.ENEMY_WEAKNESS_BROKEN = 1
+ x.ENEMY_WEAKNESS_BROKEN.set(1, Source.NONE)
}
},
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- x[Stats.BE] += 0.30
+ x.BE.buff(0.30, Source.NONE)
- x.BREAK_EFFICIENCY_BOOST += (e >= 6 && r.e6BreakEfficiency) ? 0.50 : 0
+ x.BREAK_EFFICIENCY_BOOST.buff((e >= 6 && r.e6BreakEfficiency) ? 0.50 : 0, Source.NONE)
- x.BASIC_SCALING += basicScaling
- x.ULT_SCALING += ultScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG = 30
- x.ULT_TOUGHNESS_DMG = 60
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.BE] += (m.foxianPrayer) ? skillBeValue : 0
+ x.BE.buff((m.foxianPrayer) ? skillBeValue : 0, Source.NONE)
- x.SUPER_BREAK_MODIFIER += (m.superBreakDmg) ? superBreakScaling : 0
- x.DEF_PEN += (m.defReduction) ? skillDefPenValue : 0
+ x.SUPER_BREAK_MODIFIER.buff((m.superBreakDmg) ? superBreakScaling : 0, Source.NONE)
+ x.DEF_PEN.buff((m.defReduction) ? skillDefPenValue : 0, Source.NONE)
- x.BREAK_EFFICIENCY_BOOST += (e >= 1 && m.foxianPrayer) ? 0.50 : 0
- x.BREAK_VULNERABILITY += (e >= 4 && m.e4BreakDmg) ? 0.20 : 0
+ x.BREAK_EFFICIENCY_BOOST.buff((e >= 1 && m.foxianPrayer) ? 0.50 : 0, Source.NONE)
+ x.BREAK_VULNERABILITY.buff((e >= 4 && m.e4BreakDmg) ? 0.20 : 0, Source.NONE)
},
- precomputeTeammateEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const t = action.characterConditionals
+ precomputeTeammateEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const t: Conditionals = action.characterConditionals
- x[Stats.BE] += t.weaknessBreakBeStacks * (0.08 + (t.be250Buff ? 0.16 : 0))
+ x.BE.buff(t.weaknessBreakBeStacks * (0.08 + (t.be250Buff ? 0.16 : 0)), Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardAtkFinalizer(x)
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
diff --git a/src/lib/conditionals/character/Gallagher.ts b/src/lib/conditionals/character/Gallagher.ts
index 0f54d8d71..784fc877f 100644
--- a/src/lib/conditionals/character/Gallagher.ts
+++ b/src/lib/conditionals/character/Gallagher.ts
@@ -1,19 +1,19 @@
-import { BREAK_TYPE, ComputedStatsObject, NONE_TYPE, SKILL_TYPE } from 'lib/conditionals/conditionalConstants'
+import { BREAK_TYPE, NONE_TYPE, SKILL_TYPE } from 'lib/conditionals/conditionalConstants'
import {
AbilityEidolon,
- findContentId,
+ Conditionals,
+ ContentDefinition,
gpuStandardAtkFinalizer,
gpuStandardFlatHealFinalizer,
standardAtkFinalizer,
standardFlatHealFinalizer,
} from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
import { GallagherConversionConditional } from 'lib/gpu/conditionals/dynamicConditionals'
import { buffAbilityVulnerability } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -29,10 +29,24 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const skillHealFlat = skill(e, 1600, 1768)
const talentHealFlat = talent(e, 640, 707.2)
- const content: ContentItem[] = [
- {
- formItem: 'select',
+ const defaults = {
+ healAbility: NONE_TYPE,
+ basicEnhanced: true,
+ breakEffectToOhbBoost: true,
+ e1ResBuff: true,
+ e2ResBuff: true,
+ e6BeBuff: true,
+ targetBesotted: true,
+ }
+
+ const teammateDefaults = {
+ targetBesotted: true,
+ }
+
+ const content: ContentDefinition = {
+ healAbility: {
id: 'healAbility',
+ formItem: 'select',
text: tHeal('Text'),
content: tHeal('Content'),
options: [
@@ -41,98 +55,88 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
],
fullWidth: true,
},
- {
- formItem: 'switch',
+ basicEnhanced: {
id: 'basicEnhanced',
+ formItem: 'switch',
text: t('Content.basicEnhanced.text'),
content: t('Content.basicEnhanced.content'),
},
- {
- formItem: 'switch',
+ breakEffectToOhbBoost: {
id: 'breakEffectToOhbBoost',
+ formItem: 'switch',
text: t('Content.breakEffectToOhbBoost.text'),
content: t('Content.breakEffectToOhbBoost.content'),
},
- {
- formItem: 'switch',
+ targetBesotted: {
id: 'targetBesotted',
+ formItem: 'switch',
text: t('Content.targetBesotted.text'),
content: t('Content.targetBesotted.content', { talentBesottedScaling: TsUtils.precisionRound(100 * talentBesottedScaling) }),
},
- {
- formItem: 'switch',
+ e1ResBuff: {
id: 'e1ResBuff',
+ formItem: 'switch',
text: t('Content.e1ResBuff.text'),
content: t('Content.e1ResBuff.content'),
disabled: e < 1,
},
- {
- formItem: 'switch',
+ e2ResBuff: {
id: 'e2ResBuff',
+ formItem: 'switch',
text: t('Content.e2ResBuff.text'),
content: t('Content.e2ResBuff.content'),
disabled: e < 2,
},
- {
- formItem: 'switch',
+ e6BeBuff: {
id: 'e6BeBuff',
+ formItem: 'switch',
text: t('Content.e6BeBuff.text'),
content: t('Content.e6BeBuff.content'),
disabled: e < 6,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'targetBesotted'),
- ]
+ const teammateContent: ContentDefinition = {
+ targetBesotted: content.targetBesotted,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- healAbility: NONE_TYPE,
- basicEnhanced: true,
- breakEffectToOhbBoost: true,
- e1ResBuff: true,
- e2ResBuff: true,
- e6BeBuff: true,
- targetBesotted: true,
- }),
- teammateDefaults: () => ({
- targetBesotted: true,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- x[Stats.RES] += (e >= 1 && r.e1ResBuff) ? 0.50 : 0
- x[Stats.RES] += (e >= 2 && r.e2ResBuff) ? 0.30 : 0
- x[Stats.BE] += (e >= 6) ? 0.20 : 0
+ x.RES.buff((e >= 1 && r.e1ResBuff) ? 0.50 : 0, Source.NONE)
+ x.RES.buff((e >= 2 && r.e2ResBuff) ? 0.30 : 0, Source.NONE)
+ x.BE.buff((e >= 6) ? 0.20 : 0, Source.NONE)
- x.BREAK_EFFICIENCY_BOOST += (e >= 6) ? 0.20 : 0
+ x.BREAK_EFFICIENCY_BOOST.buff((e >= 6) ? 0.20 : 0, Source.NONE)
- x.BASIC_SCALING += (r.basicEnhanced) ? basicEnhancedScaling : basicScaling
- x.ULT_SCALING += ultScaling
+ x.BASIC_SCALING.buff((r.basicEnhanced) ? basicEnhancedScaling : basicScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += (r.basicEnhanced) ? 90 : 30
- x.ULT_TOUGHNESS_DMG += 60
+ x.BASIC_TOUGHNESS_DMG.buff((r.basicEnhanced) ? 90 : 30, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
if (r.healAbility == SKILL_TYPE) {
- x.HEAL_TYPE = SKILL_TYPE
- x.HEAL_FLAT += skillHealFlat
+ x.HEAL_TYPE.set(SKILL_TYPE, Source.NONE)
+ x.HEAL_FLAT.buff(skillHealFlat, Source.NONE)
}
if (r.healAbility == NONE_TYPE) {
- x.HEAL_TYPE = NONE_TYPE
- x.HEAL_FLAT += talentHealFlat
+ x.HEAL_TYPE.set(NONE_TYPE, Source.NONE)
+ x.HEAL_FLAT.buff(talentHealFlat, Source.NONE)
}
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- buffAbilityVulnerability(x, BREAK_TYPE, talentBesottedScaling, (m.targetBesotted))
+ buffAbilityVulnerability(x, BREAK_TYPE, (m.targetBesotted) ? talentBesottedScaling : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject) => {
+ finalizeCalculations: (x: ComputedStatsArray) => {
standardAtkFinalizer(x)
standardFlatHealFinalizer(x)
},
diff --git a/src/lib/conditionals/character/Gepard.ts b/src/lib/conditionals/character/Gepard.ts
index a2b776588..43a0d87e4 100644
--- a/src/lib/conditionals/character/Gepard.ts
+++ b/src/lib/conditionals/character/Gepard.ts
@@ -1,17 +1,18 @@
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
import {
AbilityEidolon,
+ Conditionals,
+ ContentDefinition,
gpuStandardAtkFinalizer,
gpuStandardDefShieldFinalizer,
standardAtkFinalizer,
standardDefShieldFinalizer,
} from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
-import { GepardConversionConditional } from 'lib/gpu/conditionals/dynamicConditionals'
+import { ConditionalActivation, ConditionalType, Stats } from 'lib/constants'
+import { conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -24,47 +25,81 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const ultShieldScaling = ult(e, 0.45, 0.48)
const ultShieldFlat = ult(e, 600, 667.5)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ e4TeamResBuff: true,
+ }
+
+ const teammateDefaults = {
+ e4TeamResBuff: true,
+ }
+
+ const content: ContentDefinition = {
+ e4TeamResBuff: {
id: 'e4TeamResBuff',
+ formItem: 'switch',
text: t('Content.e4TeamResBuff.text'),
content: t('Content.e4TeamResBuff.content'),
disabled: e < 4,
},
- ]
+ }
+ const teammateContent: ContentDefinition = {
+ e4TeamResBuff: content.e4TeamResBuff,
+ }
return {
- content: () => content,
- teammateContent: () => content,
- defaults: () => ({
- e4TeamResBuff: true,
- }),
- teammateDefaults: () => ({
- e4TeamResBuff: true,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
- x.SHIELD_SCALING += ultShieldScaling
- x.SHIELD_FLAT += ultShieldFlat
+ x.SHIELD_SCALING.buff(ultShieldScaling, Source.NONE)
+ x.SHIELD_FLAT.buff(ultShieldFlat, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.RES] += (e >= 4 && m.e4TeamResBuff) ? 0.20 : 0
+ x.RES.buff((e >= 4 && m.e4TeamResBuff) ? 0.20 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject) => {
+ finalizeCalculations: (x: ComputedStatsArray) => {
standardAtkFinalizer(x)
standardDefShieldFinalizer(x)
},
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer() + gpuStandardDefShieldFinalizer(),
- dynamicConditionals: [GepardConversionConditional],
+ dynamicConditionals: [
+ {
+ id: 'GepardConversionConditional',
+ type: ConditionalType.ABILITY,
+ activation: ConditionalActivation.CONTINUOUS,
+ dependsOn: [Stats.DEF],
+ condition: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
+ return true
+ },
+ effect: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
+ const stateValue = action.conditionalState[this.id] || 0
+ const buffValue = 0.35 * x.a[Key.DEF]
+
+ action.conditionalState[this.id] = buffValue
+ x.ATK.buffDynamic(buffValue - stateValue, Source.NONE, action, context)
+ },
+ gpu: function () {
+ return conditionalWgslWrapper(this, `
+let def = (*p_x).DEF;
+let stateValue: f32 = (*p_state).GepardConversionConditional;
+let buffValue: f32 = 0.35 * def;
+
+(*p_state).GepardConversionConditional = buffValue;
+buffDynamicATK(buffValue - stateValue, p_x, p_state);
+ `)
+ },
+ },
+ ],
}
}
diff --git a/src/lib/conditionals/character/Guinaifen.ts b/src/lib/conditionals/character/Guinaifen.ts
index ae27b54aa..a0e251590 100644
--- a/src/lib/conditionals/character/Guinaifen.ts
+++ b/src/lib/conditionals/character/Guinaifen.ts
@@ -1,15 +1,9 @@
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardAtkFinalizer,
- standardAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -24,10 +18,23 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const ultScaling = ult(e, 1.20, 1.296)
const dotScaling = skill(e, 2.182, 2.40)
- const content: ContentItem[] = [
- {
- formItem: 'slider',
+ const defaults = {
+ talentDebuffStacks: talentDebuffMax,
+ enemyBurned: true,
+ skillDot: true,
+ e1EffectResShred: true,
+ e2BurnMultiBoost: true,
+ }
+
+ const teammateDefaults = {
+ talentDebuffStacks: talentDebuffMax,
+ e1EffectResShred: true,
+ }
+
+ const content: ContentDefinition = {
+ talentDebuffStacks: {
id: 'talentDebuffStacks',
+ formItem: 'slider',
text: t('Content.talentDebuffStacks.text'),
content: t('Content.talentDebuffStacks.content', {
talentDebuffDmgIncreaseValue: TsUtils.precisionRound(talentDebuffDmgIncreaseValue),
@@ -36,81 +43,72 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
min: 0,
max: talentDebuffMax,
},
- {
- formItem: 'switch',
+ enemyBurned: {
id: 'enemyBurned',
+ formItem: 'switch',
text: t('Content.enemyBurned.text'),
content: t('Content.enemyBurned.content'),
},
- {
- formItem: 'switch',
+ skillDot: {
id: 'skillDot',
+ formItem: 'switch',
text: t('Content.skillDot.text'),
content: t('Content.skillDot.content'),
},
- {
- formItem: 'switch',
+ e1EffectResShred: {
id: 'e1EffectResShred',
+ formItem: 'switch',
text: t('Content.e1EffectResShred.text'),
content: t('Content.e1EffectResShred.content'),
disabled: e < 1,
},
- {
- formItem: 'switch',
+ e2BurnMultiBoost: {
id: 'e2BurnMultiBoost',
+ formItem: 'switch',
text: t('Content.e2BurnMultiBoost.text'),
content: t('Content.e2BurnMultiBoost.content'),
disabled: e < 2,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'talentDebuffStacks'),
- findContentId(content, 'e1EffectResShred'),
- ]
+ const teammateContent: ContentDefinition = {
+ talentDebuffStacks: content.talentDebuffStacks,
+ e1EffectResShred: content.e1EffectResShred,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- talentDebuffStacks: talentDebuffMax,
- enemyBurned: true,
- skillDot: true,
- e1EffectResShred: true,
- e2BurnMultiBoost: true,
- }),
- teammateDefaults: () => ({
- talentDebuffStacks: talentDebuffMax,
- e1EffectResShred: true,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
- x.DOT_SCALING += dotScaling
- x.DOT_SCALING += (e >= 2 && r.e2BurnMultiBoost) ? 0.40 : 0
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.DOT_SCALING.buff(dotScaling, Source.NONE)
+ x.DOT_SCALING.buff((e >= 2 && r.e2BurnMultiBoost) ? 0.40 : 0, Source.NONE)
// Boost
- x.ELEMENTAL_DMG += (r.enemyBurned) ? 0.20 : 0
+ x.ELEMENTAL_DMG.buff((r.enemyBurned) ? 0.20 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 60
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
- x.DOT_CHANCE = r.skillDot ? 1.00 : 0.80
+ x.DOT_CHANCE.set(r.skillDot ? 1.00 : 0.80, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x.VULNERABILITY += m.talentDebuffStacks * talentDebuffDmgIncreaseValue
- x.EFFECT_RES_PEN += m.e1EffectResShred ? 0.10 : 0
+ x.VULNERABILITY.buff(m.talentDebuffStacks * talentDebuffDmgIncreaseValue, Source.NONE)
+ x.EFFECT_RES_PEN.buff(m.e1EffectResShred ? 0.10 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/Hanya.ts b/src/lib/conditionals/character/Hanya.ts
index 1e8de1402..bd9c9574d 100644
--- a/src/lib/conditionals/character/Hanya.ts
+++ b/src/lib/conditionals/character/Hanya.ts
@@ -1,18 +1,12 @@
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardAtkFinalizer,
- standardAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { ConditionalActivation, ConditionalType, Stats } from 'lib/constants'
-import { buffStat, conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
+import { conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
import { wgslFalse } from 'lib/gpu/injection/wgslUtils'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -29,42 +23,56 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const skillScaling = skill(e, 2.40, 2.64)
const ultScaling = ult(e, 0, 0)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ ultBuff: true,
+ targetBurdenActive: true,
+ burdenAtkBuff: true,
+ e2SkillSpdBuff: false,
+ }
+
+ const teammateDefaults = {
+ ultBuff: true,
+ targetBurdenActive: true,
+ burdenAtkBuff: true,
+ teammateSPDValue: 160,
+ }
+
+ const content: ContentDefinition = {
+ ultBuff: {
id: 'ultBuff',
+ formItem: 'switch',
text: t('Content.ultBuff.text'),
content: t('Content.ultBuff.content', {
ultSpdBuffValue: TsUtils.precisionRound(100 * ultSpdBuffValue),
ultAtkBuffValue: TsUtils.precisionRound(100 * ultAtkBuffValue),
}),
},
- {
- formItem: 'switch',
+ targetBurdenActive: {
id: 'targetBurdenActive',
+ formItem: 'switch',
text: t('Content.targetBurdenActive.text'),
content: t('Content.targetBurdenActive.content', { talentDmgBoostValue: TsUtils.precisionRound(100 * talentDmgBoostValue) }),
},
- {
- formItem: 'switch',
+ burdenAtkBuff: {
id: 'burdenAtkBuff',
+ formItem: 'switch',
text: t('Content.burdenAtkBuff.text'),
content: t('Content.burdenAtkBuff.content'),
},
- {
- formItem: 'switch',
+ e2SkillSpdBuff: {
id: 'e2SkillSpdBuff',
+ formItem: 'switch',
text: t('Content.e2SkillSpdBuff.text'),
content: t('Content.e2SkillSpdBuff.content'),
disabled: e < 2,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'ultBuff'),
- {
- formItem: 'slider',
+ const teammateContent: ContentDefinition = {
+ ultBuff: content.ultBuff,
+ teammateSPDValue: {
id: 'teammateSPDValue',
+ formItem: 'slider',
text: t('TeammateContent.teammateSPDValue.text'),
content: t('TeammateContent.teammateSPDValue.content', {
ultSpdBuffValue: TsUtils.precisionRound(100 * ultSpdBuffValue),
@@ -73,56 +81,46 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
min: 0,
max: 200,
},
- findContentId(content, 'targetBurdenActive'),
- findContentId(content, 'burdenAtkBuff'),
- ]
+ targetBurdenActive: content.targetBurdenActive,
+ burdenAtkBuff: content.burdenAtkBuff,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- ultBuff: true,
- targetBurdenActive: true,
- burdenAtkBuff: true,
- e2SkillSpdBuff: false,
- }),
- teammateDefaults: () => ({
- ultBuff: true,
- targetBurdenActive: true,
- burdenAtkBuff: true,
- teammateSPDValue: 160,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
- x[Stats.SPD_P] += (e >= 2 && r.e2SkillSpdBuff) ? 0.20 : 0
+ x.SPD_P.buff((e >= 2 && r.e2SkillSpdBuff) ? 0.20 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.ATK_P] += (m.ultBuff) ? ultAtkBuffValue : 0
- x[Stats.ATK_P] += (m.burdenAtkBuff) ? 0.10 : 0
+ x.ATK_P.buff((m.ultBuff) ? ultAtkBuffValue : 0, Source.NONE)
+ x.ATK_P.buff((m.burdenAtkBuff) ? 0.10 : 0, Source.NONE)
- x.ELEMENTAL_DMG += (m.targetBurdenActive) ? talentDmgBoostValue : 0
+ x.ELEMENTAL_DMG.buff((m.targetBurdenActive) ? talentDmgBoostValue : 0, Source.NONE)
},
- precomputeTeammateEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const t = action.characterConditionals
+ precomputeTeammateEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const t: Conditionals = action.characterConditionals
- x[Stats.SPD] += (t.ultBuff) ? ultSpdBuffValue * t.teammateSPDValue : 0
+ x.SPD.buff((t.ultBuff) ? ultSpdBuffValue * t.teammateSPDValue : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
dynamicConditionals: [
{
@@ -134,27 +132,27 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
condition: function () {
return true
},
- effect: function (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ effect: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
+ const r: Conditionals = action.characterConditionals
if (!r.ultBuff) {
return
}
const stateValue = action.conditionalState[this.id] || 0
- const convertibleSpdValue = x[Stats.SPD] - x.RATIO_BASED_SPD_BUFF
+ const convertibleSpdValue = x.a[Key.SPD] - x.a[Key.RATIO_BASED_SPD_BUFF]
const buffSPD = ultSpdBuffValue * convertibleSpdValue
const stateBuffSPD = ultSpdBuffValue * stateValue
- action.conditionalState[this.id] = x[Stats.SPD]
+ action.conditionalState[this.id] = x.a[Key.SPD]
const finalBuffSpd = buffSPD - (stateValue ? stateBuffSPD : 0)
- x.RATIO_BASED_SPD_BUFF += finalBuffSpd
+ x.RATIO_BASED_SPD_BUFF.buff(finalBuffSpd, Source.NONE)
- buffStat(x, Stats.SPD, finalBuffSpd, action, context)
+ x.SPD.buffDynamic(finalBuffSpd, Source.NONE, action, context)
},
gpu: function (action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
return conditionalWgslWrapper(this, `
if (${wgslFalse(r.ultBuff)}) {
diff --git a/src/lib/conditionals/character/Herta.ts b/src/lib/conditionals/character/Herta.ts
index 3a0accea3..e0a9dfcaa 100644
--- a/src/lib/conditionals/character/Herta.ts
+++ b/src/lib/conditionals/character/Herta.ts
@@ -1,19 +1,12 @@
-import {
- ASHBLAZING_ATK_STACK,
- ComputedStatsObject,
- FUA_TYPE,
- SKILL_TYPE,
- ULT_TYPE,
-} from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ASHBLAZING_ATK_STACK, FUA_TYPE, SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityDmg } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
import { NumberToNumberMap } from 'types/Common'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -62,7 +55,7 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
}
function getHitMulti(action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
const hitMultiStacks = getHitMultiByTargetsAndHits(r.fuaStacks, context)
const hitMultiByTargets: NumberToNumberMap = {
@@ -74,100 +67,100 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
return hitMultiByTargets[context.enemyCount]
}
- const content: ContentItem[] = [
- {
- formItem: 'slider',
+ const defaults = {
+ fuaStacks: 5,
+ techniqueBuff: false,
+ targetFrozen: true,
+ e2TalentCritStacks: 5,
+ e6UltAtkBuff: true,
+ enemyHpGte50: true,
+ enemyHpLte50: false,
+ }
+
+ const content: ContentDefinition = {
+ fuaStacks: {
id: 'fuaStacks',
+ formItem: 'slider',
text: t('Content.fuaStacks.text'),
content: t('Content.fuaStacks.content'),
min: 1,
max: 5,
},
- {
- formItem: 'switch',
+ targetFrozen: {
id: 'targetFrozen',
+ formItem: 'switch',
text: t('Content.targetFrozen.text'),
content: t('Content.targetFrozen.content'),
},
- {
- formItem: 'switch',
+ enemyHpGte50: {
id: 'enemyHpGte50',
+ formItem: 'switch',
text: t('Content.enemyHpGte50.text'),
content: t('Content.enemyHpGte50.content'),
},
- {
- formItem: 'switch',
+ techniqueBuff: {
id: 'techniqueBuff',
+ formItem: 'switch',
text: t('Content.techniqueBuff.text'),
content: t('Content.techniqueBuff.content'),
},
- {
- formItem: 'switch',
+ enemyHpLte50: {
id: 'enemyHpLte50',
+ formItem: 'switch',
text: t('Content.enemyHpLte50.text'),
content: t('Content.enemyHpLte50.content'),
disabled: e < 1,
},
- {
- formItem: 'slider',
+ e2TalentCritStacks: {
id: 'e2TalentCritStacks',
+ formItem: 'slider',
text: t('Content.e2TalentCritStacks.text'),
content: t('Content.e2TalentCritStacks.content'),
min: 0,
max: 5,
disabled: e < 2,
},
- {
- formItem: 'switch',
+ e6UltAtkBuff: {
id: 'e6UltAtkBuff',
+ formItem: 'switch',
text: t('Content.e6UltAtkBuff.text'),
content: t('Content.e6UltAtkBuff.content'),
disabled: e < 6,
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => ({
- fuaStacks: 5,
- techniqueBuff: false,
- targetFrozen: true,
- e2TalentCritStacks: 5,
- e6UltAtkBuff: true,
- enemyHpGte50: true,
- enemyHpLte50: false,
- }),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.ATK_P] += (r.techniqueBuff) ? 0.40 : 0
- x[Stats.CR] += (e >= 2) ? r.e2TalentCritStacks * 0.03 : 0
- x[Stats.ATK_P] += (e >= 6 && r.e6UltAtkBuff) ? 0.25 : 0
+ x.ATK_P.buff((r.techniqueBuff) ? 0.40 : 0, Source.NONE)
+ x.CR.buff((e >= 2) ? r.e2TalentCritStacks * 0.03 : 0, Source.NONE)
+ x.ATK_P.buff((e >= 6 && r.e6UltAtkBuff) ? 0.25 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.BASIC_SCALING += (e >= 1 && r.enemyHpLte50) ? 0.40 : 0
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
- x.FUA_SCALING += fuaScaling * r.fuaStacks
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.BASIC_SCALING.buff((e >= 1 && r.enemyHpLte50) ? 0.40 : 0, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.FUA_SCALING.buff(fuaScaling * r.fuaStacks, Source.NONE)
- buffAbilityDmg(x, SKILL_TYPE, 0.20, (r.enemyHpGte50))
+ buffAbilityDmg(x, SKILL_TYPE, (r.enemyHpGte50) ? 0.20 : 0, Source.NONE)
// Boost
- buffAbilityDmg(x, ULT_TYPE, 0.20, (r.targetFrozen))
- buffAbilityDmg(x, FUA_TYPE, 0.10, (e >= 4))
+ buffAbilityDmg(x, ULT_TYPE, (r.targetFrozen) ? 0.20 : 0, Source.NONE)
+ buffAbilityDmg(x, FUA_TYPE, (e >= 4) ? 0.10 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 30
- x.ULT_TOUGHNESS_DMG += 60
- x.FUA_TOUGHNESS_DMG += 15 // TODO: * spin count
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff(15 * r.fuaStacks, Source.NONE)
return x
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardFuaAtkFinalizer(x, action, context, getHitMulti(action, context))
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
diff --git a/src/lib/conditionals/character/Himeko.ts b/src/lib/conditionals/character/Himeko.ts
index 1d827e753..0a919effb 100644
--- a/src/lib/conditionals/character/Himeko.ts
+++ b/src/lib/conditionals/character/Himeko.ts
@@ -1,13 +1,11 @@
-import { ASHBLAZING_ATK_STACK, ComputedStatsObject, SKILL_TYPE } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ASHBLAZING_ATK_STACK, SKILL_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityDmg } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
-
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
import { NumberToNumberMap } from 'types/Common'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -26,85 +24,86 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
5: ASHBLAZING_ATK_STACK * (3 * 0.20 + 8 * 0.20 + 8 * 0.20 + 8 * 0.40), // 0.42
}
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ targetBurned: true,
+ selfCurrentHp80Percent: true,
+ e1TalentSpdBuff: false,
+ e2EnemyHp50DmgBoost: true,
+ e6UltExtraHits: 2,
+ }
+
+ const content: ContentDefinition = {
+ targetBurned: {
id: 'targetBurned',
+ formItem: 'switch',
text: t('Content.targetBurned.text'),
content: t('Content.targetBurned.content'),
},
- {
- formItem: 'switch',
+ selfCurrentHp80Percent: {
id: 'selfCurrentHp80Percent',
+ formItem: 'switch',
text: t('Content.selfCurrentHp80Percent.text'),
content: t('Content.selfCurrentHp80Percent.content'),
},
- {
- formItem: 'switch',
+ e1TalentSpdBuff: {
id: 'e1TalentSpdBuff',
+ formItem: 'switch',
text: t('Content.e1TalentSpdBuff.text'),
content: t('Content.e1TalentSpdBuff.content'),
disabled: e < 1,
},
- {
- formItem: 'switch',
+ e2EnemyHp50DmgBoost: {
id: 'e2EnemyHp50DmgBoost',
+ formItem: 'switch',
text: t('Content.e2EnemyHp50DmgBoost.text'),
content: t('Content.e2EnemyHp50DmgBoost.content'),
disabled: e < 2,
},
- {
- formItem: 'slider',
+ e6UltExtraHits: {
id: 'e6UltExtraHits',
+ formItem: 'slider',
text: t('Content.e6UltExtraHits.text'),
content: t('Content.e6UltExtraHits.content'),
min: 0,
max: 2,
disabled: e < 6,
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => ({
- targetBurned: true,
- selfCurrentHp80Percent: true,
- e1TalentSpdBuff: false,
- e6UltExtraHits: 2,
- }),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.CR] += (r.selfCurrentHp80Percent) ? 0.15 : 0
- x[Stats.SPD_P] += (e >= 1 && r.e1TalentSpdBuff) ? 0.20 : 0
+ x.CR.buff((r.selfCurrentHp80Percent) ? 0.15 : 0, Source.NONE)
+ x.SPD_P.buff((e >= 1 && r.e1TalentSpdBuff) ? 0.20 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
- x.ULT_SCALING += (e >= 6) ? r.e6UltExtraHits * ultScaling * 0.40 : 0
- x.FUA_SCALING += fuaScaling
- x.DOT_SCALING += dotScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.ULT_SCALING.buff((e >= 6) ? r.e6UltExtraHits * ultScaling * 0.40 : 0, Source.NONE)
+ x.FUA_SCALING.buff(fuaScaling, Source.NONE)
+ x.DOT_SCALING.buff(dotScaling, Source.NONE)
// Boost
- buffAbilityDmg(x, SKILL_TYPE, 0.20, (r.targetBurned))
- x.ELEMENTAL_DMG += (e >= 2 && r.e2EnemyHp50DmgBoost) ? 0.15 : 0
+ buffAbilityDmg(x, SKILL_TYPE, (r.targetBurned) ? 0.20 : 0, Source.NONE)
+ x.ELEMENTAL_DMG.buff((e >= 2 && r.e2EnemyHp50DmgBoost) ? 0.15 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 60
- x.FUA_TOUGHNESS_DMG += 30
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff(30, Source.NONE)
- x.DOT_CHANCE = 0.50
+ x.DOT_CHANCE.set(0.50, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardFuaAtkFinalizer(x, action, context, hitMultiByTargets[context.enemyCount])
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
diff --git a/src/lib/conditionals/character/Hook.ts b/src/lib/conditionals/character/Hook.ts
index 9aac6cd7a..c0e897393 100644
--- a/src/lib/conditionals/character/Hook.ts
+++ b/src/lib/conditionals/character/Hook.ts
@@ -1,11 +1,11 @@
-import { ComputedStatsObject, SKILL_TYPE } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
+import { SKILL_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityDmg } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -20,56 +20,56 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const ultScaling = ult(e, 4.00, 4.32)
const dotScaling = skill(e, 0.65, 0.715)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ enhancedSkill: true,
+ targetBurned: true,
+ }
+
+ const content: ContentDefinition = {
+ enhancedSkill: {
id: 'enhancedSkill',
+ formItem: 'switch',
text: t('Content.enhancedSkill.text'),
content: t('Content.enhancedSkill.content', { skillEnhancedScaling: TsUtils.precisionRound(100 * skillEnhancedScaling) }),
},
- {
- formItem: 'switch',
+ targetBurned: {
id: 'targetBurned',
+ formItem: 'switch',
text: t('Content.targetBurned.text'),
content: t('Content.targetBurned.content', { targetBurnedExtraScaling: TsUtils.precisionRound(100 * targetBurnedExtraScaling) }),
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => ({
- enhancedSkill: true,
- targetBurned: true,
- }),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += (r.enhancedSkill) ? skillEnhancedScaling : skillScaling
- x.ULT_SCALING += ultScaling
- x.BASIC_SCALING += (r.targetBurned) ? targetBurnedExtraScaling : 0
- x.SKILL_SCALING += (r.targetBurned) ? targetBurnedExtraScaling : 0
- x.ULT_SCALING += (r.targetBurned) ? targetBurnedExtraScaling : 0
- x.DOT_SCALING += dotScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff((r.enhancedSkill) ? skillEnhancedScaling : skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.BASIC_SCALING.buff((r.targetBurned) ? targetBurnedExtraScaling : 0, Source.NONE)
+ x.SKILL_SCALING.buff((r.targetBurned) ? targetBurnedExtraScaling : 0, Source.NONE)
+ x.ULT_SCALING.buff((r.targetBurned) ? targetBurnedExtraScaling : 0, Source.NONE)
+ x.DOT_SCALING.buff(dotScaling, Source.NONE)
// Boost
- buffAbilityDmg(x, SKILL_TYPE, 0.20, (e >= 1 && r.enhancedSkill))
- x.ELEMENTAL_DMG += (e >= 6 && r.targetBurned) ? 0.20 : 0
+ buffAbilityDmg(x, SKILL_TYPE, (e >= 1 && r.enhancedSkill) ? 0.20 : 0, Source.NONE)
+ x.ELEMENTAL_DMG.buff((e >= 6 && r.targetBurned) ? 0.20 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 90
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(90, Source.NONE)
- x.DOT_CHANCE = 1.00
+ x.DOT_CHANCE.set(1.00, Source.NONE)
return x
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/Huohuo.ts b/src/lib/conditionals/character/Huohuo.ts
index c90b0e0d0..d31573ae5 100644
--- a/src/lib/conditionals/character/Huohuo.ts
+++ b/src/lib/conditionals/character/Huohuo.ts
@@ -1,18 +1,18 @@
-import { ComputedStatsObject, NONE_TYPE, SKILL_TYPE } from 'lib/conditionals/conditionalConstants'
+import { NONE_TYPE, SKILL_TYPE } from 'lib/conditionals/conditionalConstants'
import {
AbilityEidolon,
- findContentId,
+ Conditionals,
+ ContentDefinition,
gpuStandardHpFinalizer,
gpuStandardHpHealFinalizer,
standardHpFinalizer,
standardHpHealFinalizer,
} from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -29,10 +29,23 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const talentHealScaling = skill(e, 0.045, 0.048)
const talentHealFlat = skill(e, 120, 133.5)
- const content: ContentItem[] = [
- {
- formItem: 'select',
+ const defaults = {
+ healAbility: NONE_TYPE,
+ ultBuff: true,
+ skillBuff: true,
+ e6DmgBuff: true,
+ }
+
+ const teammateDefaults = {
+ ultBuff: true,
+ skillBuff: true,
+ e6DmgBuff: true,
+ }
+
+ const content: ContentDefinition = {
+ healAbility: {
id: 'healAbility',
+ formItem: 'select',
text: tHeal('Text'),
content: tHeal('Content'),
options: [
@@ -49,78 +62,69 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
],
fullWidth: true,
},
- {
- formItem: 'switch',
+ ultBuff: {
id: 'ultBuff',
+ formItem: 'switch',
text: t('Content.ultBuff.text'),
content: t('Content.ultBuff.content', { ultBuffValue: TsUtils.precisionRound(100 * ultBuffValue) }),
},
- {
- formItem: 'switch',
+ skillBuff: {
id: 'skillBuff',
+ formItem: 'switch',
text: t('Content.skillBuff.text'),
content: t('Content.skillBuff.content'),
disabled: e < 1,
},
- {
- formItem: 'switch',
+ e6DmgBuff: {
id: 'e6DmgBuff',
+ formItem: 'switch',
text: t('Content.e6DmgBuff.text'),
content: t('Content.e6DmgBuff.content'),
disabled: e < 6,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'ultBuff'),
- findContentId(content, 'skillBuff'),
- findContentId(content, 'e6DmgBuff'),
- ]
+ const teammateContent: ContentDefinition = {
+ ultBuff: content.ultBuff,
+ skillBuff: content.skillBuff,
+ e6DmgBuff: content.e6DmgBuff,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- healAbility: NONE_TYPE,
- ultBuff: true,
- skillBuff: true,
- e6DmgBuff: true,
- }),
- teammateDefaults: () => ({
- ultBuff: true,
- skillBuff: true,
- e6DmgBuff: true,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Scaling
- x.BASIC_SCALING += basicScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
if (r.healAbility == SKILL_TYPE) {
- x.HEAL_TYPE = SKILL_TYPE
- x.HEAL_SCALING += skillHealScaling
- x.HEAL_FLAT += skillHealFlat
+ x.HEAL_TYPE.set(SKILL_TYPE, Source.NONE)
+ x.HEAL_SCALING.buff(skillHealScaling, Source.NONE)
+ x.HEAL_FLAT.buff(skillHealFlat, Source.NONE)
}
if (r.healAbility == NONE_TYPE) {
- x.HEAL_TYPE = NONE_TYPE
- x.HEAL_SCALING += talentHealScaling
- x.HEAL_FLAT += talentHealFlat
+ x.HEAL_TYPE.set(NONE_TYPE, Source.NONE)
+ x.HEAL_SCALING.buff(talentHealScaling, Source.NONE)
+ x.HEAL_FLAT.buff(talentHealFlat, Source.NONE)
}
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.ATK_P] += (m.ultBuff) ? ultBuffValue : 0
- x[Stats.SPD_P] += (e >= 1 && m.skillBuff) ? 0.12 : 0
+ x.ATK_P.buff((m.ultBuff) ? ultBuffValue : 0, Source.NONE)
+ x.SPD_P.buff((e >= 1 && m.skillBuff) ? 0.12 : 0, Source.NONE)
- x.ELEMENTAL_DMG += (e >= 6 && m.e6DmgBuff) ? 0.50 : 0
+ x.ELEMENTAL_DMG.buff((e >= 6 && m.e6DmgBuff) ? 0.50 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardHpFinalizer(x)
standardHpHealFinalizer(x)
},
diff --git a/src/lib/conditionals/character/ImbibitorLunae.ts b/src/lib/conditionals/character/ImbibitorLunae.ts
index c361ae354..564cf7db3 100644
--- a/src/lib/conditionals/character/ImbibitorLunae.ts
+++ b/src/lib/conditionals/character/ImbibitorLunae.ts
@@ -1,12 +1,11 @@
-import { BASIC_TYPE, ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { BASIC_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityResPen } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -24,10 +23,17 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const skillScaling = skill(e, 0, 0)
const ultScaling = ult(e, 3.00, 3.24)
- const content: ContentItem[] = [
- {
- formItem: 'slider',
+ const defaults = {
+ basicEnhanced: 3,
+ skillOutroarStacks: 4,
+ talentRighteousHeartStacks: righteousHeartStackMax,
+ e6ResPenStacks: 3,
+ }
+
+ const content: ContentDefinition = {
+ basicEnhanced: {
id: 'basicEnhanced',
+ formItem: 'slider',
text: t('Content.basicEnhanced.text'),
content: t('Content.basicEnhanced.content', {
basicScaling: TsUtils.precisionRound(100 * basicScaling),
@@ -38,72 +44,66 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
min: 0,
max: 3,
},
- {
- formItem: 'slider',
+ skillOutroarStacks: {
id: 'skillOutroarStacks',
+ formItem: 'slider',
text: t('Content.skillOutroarStacks.text'),
content: t('Content.skillOutroarStacks.content', { outroarStackCdValue: TsUtils.precisionRound(100 * outroarStackCdValue) }),
min: 0,
max: 4,
},
- {
- formItem: 'slider',
+ talentRighteousHeartStacks: {
id: 'talentRighteousHeartStacks',
+ formItem: 'slider',
text: t('Content.talentRighteousHeartStacks.text'),
content: t('Content.talentRighteousHeartStacks.content', { righteousHeartDmgValue: TsUtils.precisionRound(100 * righteousHeartDmgValue) }),
min: 0,
max: righteousHeartStackMax,
},
- {
- formItem: 'slider',
+ e6ResPenStacks: {
id: 'e6ResPenStacks',
+ formItem: 'slider',
text: t('Content.e6ResPenStacks.text'),
content: t('Content.e6ResPenStacks.content'),
min: 0,
max: 3,
disabled: e < 6,
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => ({
- basicEnhanced: 3,
- skillOutroarStacks: 4,
- talentRighteousHeartStacks: righteousHeartStackMax,
- e6ResPenStacks: 3,
- }),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.CD] += (context.enemyElementalWeak) ? 0.24 : 0
- x[Stats.CD] += r.skillOutroarStacks * outroarStackCdValue
+ x.CD.buff((context.enemyElementalWeak) ? 0.24 : 0, Source.NONE)
+ x.CD.buff(r.skillOutroarStacks * outroarStackCdValue, Source.NONE)
// Scaling
- x.BASIC_SCALING += {
+ const basicScalingValue = {
0: basicScaling,
1: basicEnhanced1Scaling,
2: basicEnhanced2Scaling,
3: basicEnhanced3Scaling,
}[r.basicEnhanced] ?? 0
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
+ x.BASIC_SCALING.buff(basicScalingValue, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
// Boost
- x.ELEMENTAL_DMG += r.talentRighteousHeartStacks * righteousHeartDmgValue
- buffAbilityResPen(x, BASIC_TYPE, 0.20 * r.e6ResPenStacks, (e >= 6 && r.basicEnhanced == 3))
+ x.ELEMENTAL_DMG.buff(r.talentRighteousHeartStacks * righteousHeartDmgValue, Source.NONE)
+ buffAbilityResPen(x, BASIC_TYPE, (e >= 6 && r.basicEnhanced == 3) ? 0.20 * r.e6ResPenStacks : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30 + 30 * r.basicEnhanced
- x.ULT_TOUGHNESS_DMG += 60
+ x.BASIC_TOUGHNESS_DMG.buff(30 + 30 * r.basicEnhanced, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/Jade.ts b/src/lib/conditionals/character/Jade.ts
index 6cf75ca1b..32473adac 100644
--- a/src/lib/conditionals/character/Jade.ts
+++ b/src/lib/conditionals/character/Jade.ts
@@ -1,13 +1,12 @@
-import { ASHBLAZING_ATK_STACK, ComputedStatsObject, FUA_TYPE } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ASHBLAZING_ATK_STACK, FUA_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityDmg } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
import { NumberToNumberMap } from 'types/Common'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -34,116 +33,114 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
}
function getHitMulti(action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
return r.enhancedFollowUp
? enhancedHitMultiByTargets[context.enemyCount]
: unenhancedHitMultiByTargets[context.enemyCount]
}
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ enhancedFollowUp: true,
+ pawnedAssetStacks: 50,
+ e1FuaDmgBoost: true,
+ e2CrBuff: true,
+ e4DefShredBuff: true,
+ e6ResShredBuff: true,
+ }
+
+ const teammateDefaults = {
+ debtCollectorSpdBuff: true,
+ }
+
+ const content: ContentDefinition = {
+ enhancedFollowUp: {
id: 'enhancedFollowUp',
+ formItem: 'switch',
text: t('Content.enhancedFollowUp.text'),
content: t('Content.enhancedFollowUp.content', { ultFuaScalingBuff: TsUtils.precisionRound(100 * ultFuaScalingBuff) }),
},
- {
- formItem: 'slider',
+ pawnedAssetStacks: {
id: 'pawnedAssetStacks',
+ formItem: 'slider',
text: t('Content.pawnedAssetStacks.text'),
content: t('Content.pawnedAssetStacks.content', { pawnedAssetCdScaling: TsUtils.precisionRound(100 * pawnedAssetCdScaling) }),
min: 0,
max: 50,
},
- {
- formItem: 'switch',
+ e1FuaDmgBoost: {
id: 'e1FuaDmgBoost',
+ formItem: 'switch',
text: t('Content.e1FuaDmgBoost.text'),
content: t('Content.e1FuaDmgBoost.content'),
disabled: e < 1,
},
- {
-
- formItem: 'switch',
+ e2CrBuff: {
id: 'e2CrBuff',
+ formItem: 'switch',
text: t('Content.e2CrBuff.text'),
content: t('Content.e2CrBuff.content'),
disabled: e < 2,
},
- {
-
- formItem: 'switch',
+ e4DefShredBuff: {
id: 'e4DefShredBuff',
+ formItem: 'switch',
text: t('Content.e4DefShredBuff.text'),
content: t('Content.e4DefShredBuff.content'),
disabled: e < 4,
},
- {
- formItem: 'switch',
+ e6ResShredBuff: {
id: 'e6ResShredBuff',
+ formItem: 'switch',
text: t('Content.e6ResShredBuff.text'),
content: t('Content.e6ResShredBuff.content'),
disabled: e < 6,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- {
- formItem: 'switch',
+ const teammateContent: ContentDefinition = {
+ debtCollectorSpdBuff: {
id: 'debtCollectorSpdBuff',
+ formItem: 'switch',
text: t('TeammateContent.debtCollectorSpdBuff.text'),
content: t('TeammateContent.debtCollectorSpdBuff.content'),
},
- ]
-
- const defaults = {
- enhancedFollowUp: true,
- pawnedAssetStacks: 50,
- e1FuaDmgBoost: true,
- e2CrBuff: true,
- e4DefShredBuff: true,
- e6ResShredBuff: true,
- }
-
- const teammateDefaults = {
- debtCollectorSpdBuff: true,
}
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => (defaults),
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
teammateDefaults: () => (teammateDefaults),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- x[Stats.CD] += r.pawnedAssetStacks * pawnedAssetCdScaling
- x[Stats.ATK_P] += r.pawnedAssetStacks * 0.005
- x[Stats.CR] += (e >= 2 && r.e2CrBuff && r.pawnedAssetStacks >= 15) ? 0.18 : 0
+ x.CD.buff(r.pawnedAssetStacks * pawnedAssetCdScaling, Source.NONE)
+ x.ATK_P.buff(r.pawnedAssetStacks * 0.005, Source.NONE)
+ x.CR.buff((e >= 2 && r.e2CrBuff && r.pawnedAssetStacks >= 15) ? 0.18 : 0, Source.NONE)
- x.BASIC_SCALING += basicScaling
- x.ULT_SCALING += ultScaling
- x.FUA_SCALING += fuaScaling
- x.FUA_SCALING += (r.enhancedFollowUp) ? ultFuaScalingBuff : 0
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.FUA_SCALING.buff(fuaScaling, Source.NONE)
+ x.FUA_SCALING.buff((r.enhancedFollowUp) ? ultFuaScalingBuff : 0, Source.NONE)
- buffAbilityDmg(x, FUA_TYPE, 0.32, (e >= 1 && r.e1FuaDmgBoost))
- x.DEF_PEN += (e >= 4 && r.e4DefShredBuff) ? 0.12 : 0
- x.QUANTUM_RES_PEN += (e >= 6 && r.e6ResShredBuff) ? 0.20 : 0
+ buffAbilityDmg(x, FUA_TYPE, (e >= 1 && r.e1FuaDmgBoost) ? 0.32 : 0, Source.NONE)
+ x.DEF_PEN.buff((e >= 4 && r.e4DefShredBuff) ? 0.12 : 0, Source.NONE)
+ x.QUANTUM_RES_PEN.buff((e >= 6 && r.e6ResShredBuff) ? 0.20 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.ULT_TOUGHNESS_DMG += 60
- x.FUA_TOUGHNESS_DMG += 30
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff(30, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
},
- precomputeTeammateEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const t = action.characterConditionals
+ precomputeTeammateEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const t: Conditionals = action.characterConditionals
- x[Stats.SPD] += (t.debtCollectorSpdBuff) ? 30 : 0
+ x.SPD.buff((t.debtCollectorSpdBuff) ? 30 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardFuaAtkFinalizer(x, action, context, getHitMulti(action, context))
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
diff --git a/src/lib/conditionals/character/Jiaoqiu.ts b/src/lib/conditionals/character/Jiaoqiu.ts
index 88959595a..2880bd622 100644
--- a/src/lib/conditionals/character/Jiaoqiu.ts
+++ b/src/lib/conditionals/character/Jiaoqiu.ts
@@ -1,17 +1,12 @@
-import { ComputedStatsObject, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardAtkFinalizer,
- standardAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
+import { ULT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { JiaoqiuConversionConditional } from 'lib/gpu/conditionals/dynamicConditionals'
import { buffAbilityVulnerability } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -31,10 +26,26 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const maxAshenRoastStacks = e >= 6 ? 9 : 5
- const content: ContentItem[] = [
- {
- formItem: 'slider',
+ const defaults = {
+ ashenRoastStacks: maxAshenRoastStacks,
+ ultFieldActive: true,
+ ehrToAtkBoost: true,
+ e1DmgBoost: true,
+ e2Dot: true,
+ e6ResShred: true,
+ }
+
+ const teammateDefaults = {
+ ashenRoastStacks: maxAshenRoastStacks,
+ ultFieldActive: true,
+ e1DmgBoost: true,
+ e6ResShred: true,
+ }
+
+ const content: ContentDefinition = {
+ ashenRoastStacks: {
id: 'ashenRoastStacks',
+ formItem: 'slider',
text: t('Content.ashenRoastStacks.text'),
content: t('Content.ashenRoastStacks.content', {
AshenRoastInitialVulnerability: TsUtils.precisionRound(100 * talentVulnerabilityBase),
@@ -44,9 +55,9 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
min: 0,
max: maxAshenRoastStacks,
},
- {
- formItem: 'switch',
+ ultFieldActive: {
id: 'ultFieldActive',
+ formItem: 'switch',
text: t('Content.ultFieldActive.text'),
content: t('Content.ultFieldActive.content', {
UltScaling: TsUtils.precisionRound(100 * ultScaling),
@@ -54,94 +65,78 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
ZoneDebuffChance: TsUtils.precisionRound(100 * ult(e, 0.6, 0.62)),
}),
},
- {
- formItem: 'switch',
+ ehrToAtkBoost: {
id: 'ehrToAtkBoost',
+ formItem: 'switch',
text: t('Content.ehrToAtkBoost.text'),
content: t('Content.ehrToAtkBoost.content'),
},
- {
- formItem: 'switch',
+ e1DmgBoost: {
id: 'e1DmgBoost',
+ formItem: 'switch',
text: t('Content.e1DmgBoost.text'),
content: t('Content.e1DmgBoost.content'),
disabled: e < 1,
},
- {
- formItem: 'switch',
+ e2Dot: {
id: 'e2Dot',
+ formItem: 'switch',
text: t('Content.e2Dot.text'),
content: t('Content.e2Dot.content'),
disabled: e < 2,
},
- {
- formItem: 'switch',
+ e6ResShred: {
id: 'e6ResShred',
+ formItem: 'switch',
text: t('Content.e6ResShred.text'),
content: t('Content.e6ResShred.content'),
disabled: e < 6,
},
- ]
-
- const teammateContent: ContentItem[] = [
- findContentId(content, 'ashenRoastStacks'),
- findContentId(content, 'ultFieldActive'),
- findContentId(content, 'e1DmgBoost'),
- findContentId(content, 'e6ResShred'),
- ]
-
- const defaults = {
- ashenRoastStacks: maxAshenRoastStacks,
- ultFieldActive: true,
- ehrToAtkBoost: true,
- e1DmgBoost: true,
- e2Dot: true,
- e6ResShred: true,
}
- const teammateDefaults = {
- ashenRoastStacks: maxAshenRoastStacks,
- ultFieldActive: true,
- e1DmgBoost: true,
- e6ResShred: true,
+ const teammateContent: ContentDefinition = {
+ ashenRoastStacks: content.ashenRoastStacks,
+ ultFieldActive: content.ultFieldActive,
+ e1DmgBoost: content.e1DmgBoost,
+ e6ResShred: content.e6ResShred,
}
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => (defaults),
- teammateDefaults: () => (teammateDefaults),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
-
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
- x.DOT_SCALING += (r.ashenRoastStacks > 0) ? talentDotScaling : 0
- x.DOT_SCALING += (e >= 2 && r.e2Dot && r.ashenRoastStacks > 0) ? 3.00 : 0
- x.DOT_CHANCE = 100
-
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 60
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
+
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.DOT_SCALING.buff((r.ashenRoastStacks > 0) ? talentDotScaling : 0, Source.NONE)
+ x.DOT_SCALING.buff((e >= 2 && r.e2Dot && r.ashenRoastStacks > 0) ? 3.00 : 0, Source.NONE)
+ x.DOT_CHANCE.set(100, Source.NONE)
+
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- buffAbilityVulnerability(x, ULT_TYPE, ultVulnerabilityScaling, (m.ultFieldActive))
+ buffAbilityVulnerability(x, ULT_TYPE, (m.ultFieldActive) ? ultVulnerabilityScaling : 0, Source.NONE)
- x.VULNERABILITY += (m.ashenRoastStacks > 0) ? talentVulnerabilityBase : 0
- x.VULNERABILITY += Math.max(0, m.ashenRoastStacks - 1) * talentVulnerabilityScaling
+ x.VULNERABILITY.buff((m.ashenRoastStacks > 0) ? talentVulnerabilityBase : 0, Source.NONE)
+ x.VULNERABILITY.buff(Math.max(0, m.ashenRoastStacks - 1) * talentVulnerabilityScaling, Source.NONE)
- x.ELEMENTAL_DMG += (e >= 1 && m.e1DmgBoost && m.ashenRoastStacks > 0) ? 0.40 : 0
+ x.ELEMENTAL_DMG.buff((e >= 1 && m.e1DmgBoost && m.ashenRoastStacks > 0) ? 0.40 : 0, Source.NONE)
- x.RES_PEN += (e >= 6 && m.e6ResShred) ? m.ashenRoastStacks * 0.03 : 0
+ x.RES_PEN.buff((e >= 6 && m.e6ResShred) ? m.ashenRoastStacks * 0.03 : 0, Source.NONE)
},
- precomputeTeammateEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ precomputeTeammateEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
dynamicConditionals: [JiaoqiuConversionConditional],
}
diff --git a/src/lib/conditionals/character/JingYuan.ts b/src/lib/conditionals/character/JingYuan.ts
index 1466dc0db..ca773e17f 100644
--- a/src/lib/conditionals/character/JingYuan.ts
+++ b/src/lib/conditionals/character/JingYuan.ts
@@ -1,19 +1,11 @@
-import {
- ASHBLAZING_ATK_STACK,
- BASIC_TYPE,
- ComputedStatsObject,
- FUA_TYPE,
- SKILL_TYPE,
- ULT_TYPE,
-} from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ASHBLAZING_ATK_STACK, BASIC_TYPE, FUA_TYPE, SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityCd, buffAbilityDmg, buffAbilityVulnerability } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -26,7 +18,7 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const fuaScaling = talent(e, 0.66, 0.726)
function getHitMulti(action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
let hitMulti = 0
const stacks = r.talentHitsPerAction
@@ -51,96 +43,96 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
return hitMulti
}
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ skillCritBuff: true,
+ talentHitsPerAction: 10,
+ talentAttacks: 10,
+ e2DmgBuff: true,
+ e6FuaVulnerabilityStacks: 3,
+ }
+
+ const content: ContentDefinition = {
+ skillCritBuff: {
id: 'skillCritBuff',
+ formItem: 'switch',
text: t('Content.skillCritBuff.text'),
content: t('Content.skillCritBuff.content'),
},
- {
- formItem: 'slider',
+ talentHitsPerAction: {
id: 'talentHitsPerAction',
+ formItem: 'slider',
text: t('Content.talentHitsPerAction.text'),
content: t('Content.talentHitsPerAction.content'),
min: 3,
max: 10,
},
- {
- formItem: 'slider',
+ talentAttacks: {
id: 'talentAttacks',
+ formItem: 'slider',
text: t('Content.talentAttacks.text'),
content: t('Content.talentAttacks.content'),
min: 0,
max: 10,
},
- {
- formItem: 'switch',
+ e2DmgBuff: {
id: 'e2DmgBuff',
+ formItem: 'switch',
text: t('Content.e2DmgBuff.text'),
content: t('Content.e2DmgBuff.content'),
disabled: e < 2,
},
- {
- formItem: 'slider',
+ e6FuaVulnerabilityStacks: {
id: 'e6FuaVulnerabilityStacks',
+ formItem: 'slider',
text: t('Content.e6FuaVulnerabilityStacks.text'),
content: t('Content.e6FuaVulnerabilityStacks.content'),
min: 0,
max: 3,
disabled: e < 6,
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => ({
- skillCritBuff: true,
- talentHitsPerAction: 10,
- talentAttacks: 10,
- e2DmgBuff: true,
- e6FuaVulnerabilityStacks: 3,
- }),
- teammateDefaults: () => ({}),
- initializeConfigurations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
-
- x.SUMMONS = 1
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ initializeConfigurations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
+
+ x.SUMMONS.set(1, Source.NONE)
},
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
r.talentHitsPerAction = Math.max(r.talentHitsPerAction, r.talentAttacks)
// Stats
- x[Stats.CR] += (r.skillCritBuff) ? 0.10 : 0
+ x.CR.buff((r.skillCritBuff) ? 0.10 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
- x.FUA_SCALING += fuaScaling * r.talentAttacks
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.FUA_SCALING.buff(fuaScaling * r.talentAttacks, Source.NONE)
// Boost
- buffAbilityCd(x, FUA_TYPE, 0.25, (r.talentHitsPerAction >= 6))
- buffAbilityDmg(x, BASIC_TYPE | SKILL_TYPE | ULT_TYPE, 0.20, (e >= 2 && r.e2DmgBuff))
- buffAbilityVulnerability(x, FUA_TYPE, r.e6FuaVulnerabilityStacks * 0.12, (e >= 6))
+ buffAbilityCd(x, FUA_TYPE, (r.talentHitsPerAction >= 6) ? 0.25 : 0, Source.NONE)
+ buffAbilityDmg(x, BASIC_TYPE | SKILL_TYPE | ULT_TYPE, (e >= 2 && r.e2DmgBuff) ? 0.20 : 0, Source.NONE)
+ buffAbilityVulnerability(x, FUA_TYPE, (e >= 6) ? r.e6FuaVulnerabilityStacks * 0.12 : 0, Source.NONE)
// Lightning lord calcs
const hits = r.talentAttacks
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 30
- x.ULT_TOUGHNESS_DMG += 60
- x.FUA_TOUGHNESS_DMG += 15 * hits
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff(15 * hits, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
// TODO: Technically E6 has a vulnerability but its kinda hard to calc
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardFuaAtkFinalizer(x, action, context, getHitMulti(action, context))
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
diff --git a/src/lib/conditionals/character/Jingliu.ts b/src/lib/conditionals/character/Jingliu.ts
index 93e5fb950..57c1639d4 100644
--- a/src/lib/conditionals/character/Jingliu.ts
+++ b/src/lib/conditionals/character/Jingliu.ts
@@ -1,33 +1,41 @@
-import { ComputedStatsObject, SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityDmg } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const t = TsUtils.wrappedFixedT(withContent).get(null, 'conditionals', 'Characters.Jingliu')
+ const { SOURCE_SKILL, SOURCE_ULT } = Source.character('Jingliu')
const { basic, skill, ult, talent } = AbilityEidolon.ULT_TALENT_3_SKILL_BASIC_5
const talentCrBuff = talent(e, 0.50, 0.52)
- let talentHpDrainAtkBuffMax = talent(e, 1.80, 1.98)
- talentHpDrainAtkBuffMax += (e >= 4) ? 0.30 : 0
const basicScaling = basic(e, 1.00, 1.10)
const skillScaling = skill(e, 2.00, 2.20)
const skillEnhancedScaling = skill(e, 2.50, 2.75)
const ultScaling = ult(e, 3.00, 3.24)
- const content: ContentItem[] = [
- {
+ let talentHpDrainAtkBuffMax = talent(e, 1.80, 1.98)
+ talentHpDrainAtkBuffMax += (e >= 4) ? 0.30 : 0
+
+ const defaults = {
+ talentEnhancedState: true,
+ talentHpDrainAtkBuff: talentHpDrainAtkBuffMax,
+ e1CdBuff: true,
+ e2SkillDmgBuff: true,
+ }
+
+ const content: ContentDefinition = {
+ talentEnhancedState: {
id: 'talentEnhancedState',
formItem: 'switch',
text: t('Content.talentEnhancedState.text'),
content: t('Content.talentEnhancedState.content', { talentCrBuff: TsUtils.precisionRound(100 * talentCrBuff) }),
},
- {
+ talentHpDrainAtkBuff: {
id: 'talentHpDrainAtkBuff',
formItem: 'slider',
text: t('Content.talentHpDrainAtkBuff.text'),
@@ -36,68 +44,62 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
max: talentHpDrainAtkBuffMax,
percent: true,
},
- {
+ e1CdBuff: {
id: 'e1CdBuff',
formItem: 'switch',
text: t('Content.e1CdBuff.text'),
content: t('Content.e1CdBuff.content'),
disabled: e < 1,
},
- {
+ e2SkillDmgBuff: {
id: 'e2SkillDmgBuff',
formItem: 'switch',
text: t('Content.e2SkillDmgBuff.text'),
content: t('Content.e2SkillDmgBuff.content'),
disabled: e < 2,
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => ({
- talentEnhancedState: true,
- talentHpDrainAtkBuff: talentHpDrainAtkBuffMax,
- e1CdBuff: true,
- e2SkillDmgBuff: true,
- }),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Skills
- x[Stats.CR] += (r.talentEnhancedState) ? talentCrBuff : 0
- x[Stats.ATK_P] += ((r.talentEnhancedState) ? r.talentHpDrainAtkBuff : 0)
+ x.CR.buff((r.talentEnhancedState) ? talentCrBuff : 0, Source.NONE)
+ x.ATK_P.buff((r.talentEnhancedState) ? r.talentHpDrainAtkBuff : 0, Source.NONE)
// Traces
- x[Stats.RES] += (r.talentEnhancedState) ? 0.35 : 0
- buffAbilityDmg(x, ULT_TYPE, 0.20, (r.talentEnhancedState))
+ x.RES.buff((r.talentEnhancedState) ? 0.35 : 0, Source.NONE)
+
+ r.talentEnhancedState && buffAbilityDmg(x, ULT_TYPE, 0.20, Source.NONE)
// Eidolons
- x[Stats.CD] += (e >= 1 && r.e1CdBuff) ? 0.24 : 0
- x[Stats.CD] += (e >= 6 && r.talentEnhancedState) ? 0.50 : 0
+ x.CD.buff((e >= 1 && r.e1CdBuff) ? 0.24 : 0, Source.NONE)
+ x.CD.buff((e >= 6 && r.talentEnhancedState) ? 0.50 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
- x.SKILL_SCALING += (r.talentEnhancedState) ? skillEnhancedScaling : skillScaling
- x.SKILL_SCALING += (e >= 1 && r.talentEnhancedState && (context.enemyCount ?? context.enemyCount) == 1) ? 1 : 0
+ x.SKILL_SCALING.buff((r.talentEnhancedState) ? skillEnhancedScaling : skillScaling, Source.NONE)
+ x.SKILL_SCALING.buff((e >= 1 && r.talentEnhancedState && (context.enemyCount ?? context.enemyCount) == 1) ? 1 : 0, Source.NONE)
- x.ULT_SCALING += ultScaling
- x.ULT_SCALING += (e >= 1 && (context.enemyCount ?? context.enemyCount) == 1) ? 1 : 0
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.ULT_SCALING.buff((e >= 1 && (context.enemyCount ?? context.enemyCount) == 1) ? 1 : 0, Source.NONE)
- x.FUA_SCALING += 0
+ x.FUA_SCALING.buff(0, Source.NONE)
// BOOST
- buffAbilityDmg(x, SKILL_TYPE, 0.80, (e >= 2 && r.talentEnhancedState && r.e2SkillDmgBuff))
+ buffAbilityDmg(x, SKILL_TYPE, (e >= 2 && r.talentEnhancedState && r.e2SkillDmgBuff) ? 0.80 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 60
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
return x
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/Kafka.ts b/src/lib/conditionals/character/Kafka.ts
index 19cd647d4..312a92185 100644
--- a/src/lib/conditionals/character/Kafka.ts
+++ b/src/lib/conditionals/character/Kafka.ts
@@ -1,16 +1,11 @@
-import { ASHBLAZING_ATK_STACK, ComputedStatsObject, DOT_TYPE } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardFuaAtkFinalizer,
- standardFuaAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
+import { ASHBLAZING_ATK_STACK, DOT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityDmg, buffAbilityVulnerability } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -26,66 +21,66 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const hitMulti = ASHBLAZING_ATK_STACK
* (1 * 0.15 + 2 * 0.15 + 3 * 0.15 + 4 * 0.15 + 5 * 0.15 + 6 * 0.25)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ e1DotDmgReceivedDebuff: true,
+ e2TeamDotBoost: true,
+ }
+
+ const teammateDefaults = {
+ e1DotDmgReceivedDebuff: true,
+ e2TeamDotBoost: true,
+ }
+
+ const content: ContentDefinition = {
+ e1DotDmgReceivedDebuff: {
id: 'e1DotDmgReceivedDebuff',
+ formItem: 'switch',
text: t('Content.e1DotDmgReceivedDebuff.text'),
content: t('Content.e1DotDmgReceivedDebuff.content'),
disabled: e < 1,
},
- {
- formItem: 'switch',
+ e2TeamDotBoost: {
id: 'e2TeamDotBoost',
+ formItem: 'switch',
text: t('Content.e2TeamDotBoost.text'),
content: t('Content.e2TeamDotBoost.content'),
disabled: e < 2,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'e1DotDmgReceivedDebuff'),
- findContentId(content, 'e2TeamDotBoost'),
- ]
+ const teammateContent: ContentDefinition = {
+ e1DotDmgReceivedDebuff: content.e1DotDmgReceivedDebuff,
+ e2TeamDotBoost: content.e2TeamDotBoost,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- e1DotDmgReceivedDebuff: true,
- e2TeamDotBoost: true,
- }),
- teammateDefaults: () => ({
- e1DotDmgReceivedDebuff: true,
- e2TeamDotBoost: true,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- // Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
- x.FUA_SCALING += fuaScaling
- x.DOT_SCALING += dotScaling
-
- // Boost
- x.DOT_SCALING += (e >= 6) ? 1.56 : 0
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.FUA_SCALING.buff(fuaScaling, Source.NONE)
+ x.DOT_SCALING.buff(dotScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 60
- x.FUA_TOUGHNESS_DMG += 30
+ x.DOT_SCALING.buff((e >= 6) ? 1.56 : 0, Source.NONE)
- x.DOT_CHANCE = 1.30
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff(30, Source.NONE)
- return x
+ x.DOT_CHANCE.set(1.30, Source.NONE)
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- buffAbilityVulnerability(x, DOT_TYPE, 0.30, (e >= 1 && m.e1DotDmgReceivedDebuff))
- buffAbilityDmg(x, DOT_TYPE, 0.25, (e >= 2 && m.e2TeamDotBoost))
+ buffAbilityVulnerability(x, DOT_TYPE, (e >= 1 && m.e1DotDmgReceivedDebuff) ? 0.30 : 0, Source.NONE)
+ buffAbilityDmg(x, DOT_TYPE, (e >= 2 && m.e2TeamDotBoost) ? 0.25 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardFuaAtkFinalizer(x, action, context, hitMulti)
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
diff --git a/src/lib/conditionals/character/Lingsha.ts b/src/lib/conditionals/character/Lingsha.ts
index e6b2bcd42..686645bee 100644
--- a/src/lib/conditionals/character/Lingsha.ts
+++ b/src/lib/conditionals/character/Lingsha.ts
@@ -1,28 +1,23 @@
-import {
- ASHBLAZING_ATK_STACK,
- BREAK_TYPE,
- ComputedStatsObject,
- NONE_TYPE,
- SKILL_TYPE,
- ULT_TYPE,
-} from 'lib/conditionals/conditionalConstants'
+import { ASHBLAZING_ATK_STACK, BREAK_TYPE, NONE_TYPE, SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
import {
AbilityEidolon,
+ Conditionals,
+ ContentDefinition,
gpuStandardAtkHealFinalizer,
gpuStandardFuaAtkFinalizer,
standardAtkHealFinalizer,
standardFuaAtkFinalizer,
} from 'lib/conditionals/conditionalUtils'
import { ConditionalActivation, ConditionalType, Stats } from 'lib/constants'
-import { buffStat, conditionalWgslWrapper, DynamicConditional } from 'lib/gpu/conditionals/dynamicConditionals'
+import { conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
import { wgslFalse } from 'lib/gpu/injection/wgslUtils'
import { buffAbilityVulnerability } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
import { NumberToNumberMap } from 'types/Common'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -67,10 +62,10 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
e6ResShred: true,
}
- const characterContent: ContentDefinition = {
+ const content: ContentDefinition = {
healAbility: {
- formItem: 'select',
id: 'healAbility',
+ formItem: 'select',
text: tHeal('Text'),
content: tHeal('Content'),
options: [
@@ -81,36 +76,36 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
fullWidth: true,
},
beConversion: {
- formItem: 'switch',
id: 'beConversion',
+ formItem: 'switch',
text: t('Content.beConversion.text'),
content: t('Content.beConversion.content'),
},
befogState: {
- formItem: 'switch',
id: 'befogState',
+ formItem: 'switch',
text: t('Content.befogState.text'),
content: t('Content.befogState.content', {
BefogVulnerability: TsUtils.precisionRound(100 * ultBreakVulnerability),
}),
},
e1DefShred: {
- formItem: 'switch',
id: 'e1DefShred',
+ formItem: 'switch',
text: t('Content.e1DefShred.text'),
content: t('Content.e1DefShred.content'),
disabled: e < 1,
},
e2BeBuff: {
- formItem: 'switch',
id: 'e2BeBuff',
+ formItem: 'switch',
text: t('Content.e2BeBuff.text'),
content: t('Content.e2BeBuff.content'),
disabled: e < 2,
},
e6ResShred: {
- formItem: 'switch',
id: 'e6ResShred',
+ formItem: 'switch',
text: t('Content.e6ResShred.text'),
content: t('Content.e6ResShred.content'),
disabled: e < 6,
@@ -118,113 +113,107 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
}
const teammateContent: ContentDefinition = {
- befogState: characterContent.befogState,
- e1DefShred: characterContent.e1DefShred,
- e2BeBuff: characterContent.e2BeBuff,
- e6ResShred: characterContent.e6ResShred,
+ befogState: content.befogState,
+ e1DefShred: content.e1DefShred,
+ e2BeBuff: content.e2BeBuff,
+ e6ResShred: content.e6ResShred,
}
return {
- content: () => Object.values(characterContent),
+ content: () => Object.values(content),
teammateContent: () => Object.values(teammateContent),
defaults: () => defaults,
teammateDefaults: () => teammateDefaults,
- initializeConfigurations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ initializeConfigurations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- x.SUMMONS = 1
+ x.SUMMONS.set(1, Source.NONE)
},
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r: Conditionals = action.characterConditionals
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.FUA_SCALING += fuaScaling * 2
- x.ULT_SCALING += ultScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.FUA_SCALING.buff(fuaScaling * 2, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
- x.BREAK_EFFICIENCY_BOOST += (e >= 1) ? 0.50 : 0
- x.FUA_SCALING += (e >= 6 && r.e6ResShred) ? 0.50 : 0
+ x.BREAK_EFFICIENCY_BOOST.buff((e >= 1) ? 0.50 : 0, Source.NONE)
+ x.FUA_SCALING.buff((e >= 6 && r.e6ResShred) ? 0.50 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 30
- x.ULT_TOUGHNESS_DMG += 60
- x.FUA_TOUGHNESS_DMG += 30 * 2
- x.FUA_TOUGHNESS_DMG += (e >= 6) ? 15 : 0
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff(30 * 2, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff((e >= 6) ? 15 : 0, Source.NONE)
if (r.healAbility == SKILL_TYPE) {
- x.HEAL_TYPE = SKILL_TYPE
- x.HEAL_SCALING += skillHealScaling
- x.HEAL_FLAT += skillHealFlat
+ x.HEAL_TYPE.set(SKILL_TYPE, Source.NONE)
+ x.HEAL_SCALING.buff(skillHealScaling, Source.NONE)
+ x.HEAL_FLAT.buff(skillHealFlat, Source.NONE)
}
if (r.healAbility == ULT_TYPE) {
- x.HEAL_TYPE = ULT_TYPE
- x.HEAL_SCALING += ultHealScaling
- x.HEAL_FLAT += ultHealFlat
+ x.HEAL_TYPE.set(ULT_TYPE, Source.NONE)
+ x.HEAL_SCALING.buff(ultHealScaling, Source.NONE)
+ x.HEAL_FLAT.buff(ultHealFlat, Source.NONE)
}
if (r.healAbility == NONE_TYPE) {
- x.HEAL_TYPE = NONE_TYPE
- x.HEAL_SCALING += talentHealScaling
- x.HEAL_FLAT += talentHealFlat
+ x.HEAL_TYPE.set(NONE_TYPE, Source.NONE)
+ x.HEAL_SCALING.buff(talentHealScaling, Source.NONE)
+ x.HEAL_FLAT.buff(talentHealFlat, Source.NONE)
}
-
- return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
const m: Conditionals = action.characterConditionals
- if (x.ENEMY_WEAKNESS_BROKEN) {
- x.DEF_PEN += (e >= 1 && m.e1DefShred) ? 0.20 : 0
+ if (x.a[Key.ENEMY_WEAKNESS_BROKEN]) {
+ x.DEF_PEN.buff((e >= 1 && m.e1DefShred) ? 0.20 : 0, Source.NONE)
}
- buffAbilityVulnerability(x, BREAK_TYPE, ultBreakVulnerability, (m.befogState))
+ buffAbilityVulnerability(x, BREAK_TYPE, (m.befogState) ? ultBreakVulnerability : 0, Source.NONE)
- x[Stats.BE] += (e >= 2 && m.e2BeBuff) ? 0.40 : 0
- x.RES_PEN += (e >= 6 && m.e6ResShred) ? 0.20 : 0
+ x.BE.buff((e >= 2 && m.e2BeBuff) ? 0.40 : 0, Source.NONE)
+ x.RES_PEN.buff((e >= 6 && m.e6ResShred) ? 0.20 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardFuaAtkFinalizer(x, action, context, hitMultiByTargets[context.enemyCount])
standardAtkHealFinalizer(x)
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
return gpuStandardFuaAtkFinalizer(hitMultiByTargets[context.enemyCount]) + gpuStandardAtkHealFinalizer()
},
- dynamicConditionals: [LingshaConversionConditional],
- }
-}
-
-const LingshaConversionConditional: DynamicConditional = {
- id: 'LingshaConversionConditional',
- type: ConditionalType.ABILITY,
- activation: ConditionalActivation.CONTINUOUS,
- dependsOn: [Stats.BE],
- condition: function (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) {
- return true
- },
- effect: function (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
- if (!r.beConversion) {
- return
- }
-
- const stateValue = action.conditionalState[this.id] || 0
- const buffValueAtk = Math.min(0.50, 0.25 * x[Stats.BE]) * context.baseATK
- const buffValueOhb = Math.min(0.20, 0.10 * x[Stats.BE])
-
- const stateBuffValueAtk = Math.min(0.50, 0.25 * stateValue) * context.baseATK
- const stateBuffValueOhb = Math.min(0.20, 0.10 * stateValue)
-
- action.conditionalState[this.id] = x[Stats.BE]
-
- const finalBuffAtk = buffValueAtk - (stateValue ? stateBuffValueAtk : 0)
- const finalBuffOhb = buffValueOhb - (stateValue ? stateBuffValueOhb : 0)
-
- buffStat(x, Stats.ATK, finalBuffAtk, action, context)
- buffStat(x, Stats.OHB, finalBuffOhb, action, context)
- },
- gpu: function (action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
-
- return conditionalWgslWrapper(this, `
+ dynamicConditionals: [{
+ id: 'LingshaConversionConditional',
+ type: ConditionalType.ABILITY,
+ activation: ConditionalActivation.CONTINUOUS,
+ dependsOn: [Stats.BE],
+ condition: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
+ return true
+ },
+ effect: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
+ const r: Conditionals = action.characterConditionals
+ if (!r.beConversion) {
+ return
+ }
+
+ const stateValue = action.conditionalState[this.id] || 0
+ const buffValueAtk = Math.min(0.50, 0.25 * x.a[Key.BE]) * context.baseATK
+ const buffValueOhb = Math.min(0.20, 0.10 * x.a[Key.BE])
+
+ const stateBuffValueAtk = Math.min(0.50, 0.25 * stateValue) * context.baseATK
+ const stateBuffValueOhb = Math.min(0.20, 0.10 * stateValue)
+
+ action.conditionalState[this.id] = x.a[Key.BE]
+
+ const finalBuffAtk = buffValueAtk - (stateValue ? stateBuffValueAtk : 0)
+ const finalBuffOhb = buffValueOhb - (stateValue ? stateBuffValueOhb : 0)
+
+ x.ATK.buffDynamic(finalBuffAtk, Source.NONE, action, context)
+ x.OHB.buffDynamic(finalBuffOhb, Source.NONE, action, context)
+ },
+ gpu: function (action: OptimizerAction, context: OptimizerContext) {
+ const r: Conditionals = action.characterConditionals
+
+ return conditionalWgslWrapper(this, `
if (${wgslFalse(r.beConversion)}) {
return;
}
@@ -245,13 +234,7 @@ let finalBuffOhb = buffValueOhb - select(0, stateBuffValueOhb, stateValue > 0);
buffDynamicATK(finalBuffAtk, p_x, p_state);
buffDynamicOHB(finalBuffOhb, p_x, p_state);
`)
- },
-}
-
-type ContentDefinition> = {
- [K in keyof T]: ContentItem & { id: K };
-}
-
-type Conditionals> = {
- [K in keyof T]: number;
+ },
+ }],
+ }
}
diff --git a/src/lib/conditionals/character/Luka.ts b/src/lib/conditionals/character/Luka.ts
index 76bfe7109..20a63fcdf 100644
--- a/src/lib/conditionals/character/Luka.ts
+++ b/src/lib/conditionals/character/Luka.ts
@@ -1,16 +1,9 @@
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardAtkFinalizer,
- standardAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -26,92 +19,96 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const ultScaling = ult(e, 3.30, 3.564)
const dotScaling = skill(e, 3.38, 3.718)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ basicEnhanced: true,
+ targetUltDebuffed: true,
+ e1TargetBleeding: true,
+ basicEnhancedExtraHits: 3,
+ e4TalentStacks: 4,
+ }
+
+ const teammateDefaults = {
+ targetUltDebuffed: true,
+ }
+
+ const content: ContentDefinition = {
+ basicEnhanced: {
id: 'basicEnhanced',
+ formItem: 'switch',
text: t('Content.basicEnhanced.text'),
content: t('Content.basicEnhanced.content'),
},
- {
- formItem: 'switch',
+ targetUltDebuffed: {
id: 'targetUltDebuffed',
+ formItem: 'switch',
text: t('Content.targetUltDebuffed.text'),
content: t('Content.targetUltDebuffed.content', { targetUltDebuffDmgTakenValue: TsUtils.precisionRound(100 * targetUltDebuffDmgTakenValue) }),
},
- {
- formItem: 'slider',
+ basicEnhancedExtraHits: {
id: 'basicEnhancedExtraHits',
+ formItem: 'slider',
text: t('Content.basicEnhancedExtraHits.text'),
content: t('Content.basicEnhancedExtraHits.content'),
min: 0,
max: 3,
},
- {
- formItem: 'switch',
+ e1TargetBleeding: {
id: 'e1TargetBleeding',
+ formItem: 'switch',
text: t('Content.e1TargetBleeding.text'),
content: t('Content.e1TargetBleeding.content'),
disabled: e < 1,
},
- {
- formItem: 'slider',
+ e4TalentStacks: {
id: 'e4TalentStacks',
+ formItem: 'slider',
text: t('Content.e4TalentStacks.text'),
content: t('Content.e4TalentStacks.content'),
min: 0,
max: 4,
disabled: e < 4,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'targetUltDebuffed'),
- ]
+ const teammateContent: ContentDefinition = {
+ targetUltDebuffed: content.targetUltDebuffed,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- basicEnhanced: true,
- targetUltDebuffed: true,
- e1TargetBleeding: true,
- basicEnhancedExtraHits: 3,
- e4TalentStacks: 4,
- }),
- teammateDefaults: () => ({
- targetUltDebuffed: true,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.ATK_P] += (e >= 4) ? r.e4TalentStacks * 0.05 : 0
+ x.ATK_P.buff((e >= 4) ? r.e4TalentStacks * 0.05 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += (r.basicEnhanced) ? basicEnhancedScaling : basicScaling
- x.BASIC_SCALING += (r.basicEnhanced && r.basicEnhancedExtraHits) * basicEnhancedHitValue
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
- x.DOT_SCALING += dotScaling
+ x.BASIC_SCALING.buff((r.basicEnhanced) ? basicEnhancedScaling : basicScaling, Source.NONE)
+ x.BASIC_SCALING.buff((r.basicEnhanced && r.basicEnhancedExtraHits) * basicEnhancedHitValue, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.DOT_SCALING.buff(dotScaling, Source.NONE)
// Boost
- x.ELEMENTAL_DMG += (e >= 1 && r.e1TargetBleeding) ? 0.15 : 0
+ x.ELEMENTAL_DMG.buff((e >= 1 && r.e1TargetBleeding) ? 0.15 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += (r.basicEnhanced) ? 60 : 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 90
+ x.BASIC_TOUGHNESS_DMG.buff((r.basicEnhanced) ? 60 : 30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(90, Source.NONE)
- x.DOT_CHANCE = 1.00
+ x.DOT_CHANCE.set(1.00, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x.VULNERABILITY += (m.targetUltDebuffed) ? targetUltDebuffDmgTakenValue : 0
+ x.VULNERABILITY.buff((m.targetUltDebuffed) ? targetUltDebuffDmgTakenValue : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/Luocha.ts b/src/lib/conditionals/character/Luocha.ts
index 69ce044e9..edd0a92a3 100644
--- a/src/lib/conditionals/character/Luocha.ts
+++ b/src/lib/conditionals/character/Luocha.ts
@@ -1,17 +1,17 @@
-import { ComputedStatsObject, NONE_TYPE, SKILL_TYPE } from 'lib/conditionals/conditionalConstants'
+import { NONE_TYPE, SKILL_TYPE } from 'lib/conditionals/conditionalConstants'
import {
AbilityEidolon,
- findContentId,
+ Conditionals,
+ ContentDefinition,
gpuStandardAtkFinalizer,
gpuStandardAtkHealFinalizer,
standardAtkFinalizer,
standardAtkHealFinalizer,
} from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -29,10 +29,21 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const talentHealScaling = talent(e, 0.18, 0.192)
const talentHealFlat = talent(e, 240, 267)
- const content: ContentItem[] = [
- {
- formItem: 'select',
+ const defaults = {
+ healAbility: NONE_TYPE,
+ fieldActive: true,
+ e6ResReduction: true,
+ }
+
+ const teammateDefaults = {
+ fieldActive: true,
+ e6ResReduction: true,
+ }
+
+ const content: ContentDefinition = {
+ healAbility: {
id: 'healAbility',
+ formItem: 'select',
text: tHeal('Text'),
content: tHeal('Content'),
options: [
@@ -49,70 +60,64 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
],
fullWidth: true,
},
- {
- formItem: 'switch',
+ fieldActive: {
id: 'fieldActive',
+ formItem: 'switch',
text: t('Content.fieldActive.text'),
content: t('Content.fieldActive.content'),
// disabled: e < 1, Not disabling this one since technically the field can be active at E0
- }, {
- formItem: 'switch',
+ },
+ e6ResReduction: {
id: 'e6ResReduction',
+ formItem: 'switch',
text: t('Content.e6ResReduction.text'),
content: t('Content.e6ResReduction.content'),
disabled: e < 6,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'fieldActive'),
- findContentId(content, 'e6ResReduction'),
- ]
+ const teammateContent: ContentDefinition = {
+ fieldActive: content.fieldActive,
+ e6ResReduction: content.e6ResReduction,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- healAbility: NONE_TYPE,
- fieldActive: true,
- e6ResReduction: true,
- }),
- teammateDefaults: () => ({
- fieldActive: true,
- e6ResReduction: true,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.ULT_TOUGHNESS_DMG += 60
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
if (r.healAbility == SKILL_TYPE) {
- x.HEAL_TYPE = SKILL_TYPE
- x.HEAL_SCALING += skillHealScaling
- x.HEAL_FLAT += skillHealFlat
+ x.HEAL_TYPE.set(SKILL_TYPE, Source.NONE)
+ x.HEAL_SCALING.buff(skillHealScaling, Source.NONE)
+ x.HEAL_FLAT.buff(skillHealFlat, Source.NONE)
}
if (r.healAbility == NONE_TYPE) {
- x.HEAL_TYPE = NONE_TYPE
- x.HEAL_SCALING += talentHealScaling
- x.HEAL_FLAT += talentHealFlat
+ x.HEAL_TYPE.set(NONE_TYPE, Source.NONE)
+ x.HEAL_SCALING.buff(talentHealScaling, Source.NONE)
+ x.HEAL_FLAT.buff(talentHealFlat, Source.NONE)
}
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.ATK_P] += (e >= 1 && m.fieldActive) ? 0.20 : 0
+ x.ATK_P.buff((e >= 1 && m.fieldActive) ? 0.20 : 0, Source.NONE)
- x.RES_PEN += (e >= 6 && m.e6ResReduction) ? 0.20 : 0
+ x.RES_PEN.buff((e >= 6 && m.e6ResReduction) ? 0.20 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject) => {
+ finalizeCalculations: (x: ComputedStatsArray) => {
standardAtkFinalizer(x)
standardAtkHealFinalizer(x)
},
diff --git a/src/lib/conditionals/character/Lynx.ts b/src/lib/conditionals/character/Lynx.ts
index 44df01668..085580eef 100644
--- a/src/lib/conditionals/character/Lynx.ts
+++ b/src/lib/conditionals/character/Lynx.ts
@@ -1,19 +1,20 @@
-import { ComputedStatsObject, NONE_TYPE, SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { NONE_TYPE, SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
import {
AbilityEidolon,
- findContentId,
+ Conditionals,
+ ContentDefinition,
gpuStandardHpFinalizer,
gpuStandardHpHealFinalizer,
standardHpFinalizer,
standardHpHealFinalizer,
} from 'lib/conditionals/conditionalUtils'
import { ConditionalActivation, ConditionalType, Stats } from 'lib/constants'
-import { buffStat, conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
+import { conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
import { wgslFalse, wgslTrue } from 'lib/gpu/injection/wgslUtils'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -37,10 +38,10 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const talentHealScaling = talent(e, 0.036, 0.0384)
const talentHealFlat = talent(e, 96, 106.8)
- const content: ContentItem[] = [
- {
- formItem: 'select',
+ const content: ContentDefinition = {
+ healAbility: {
id: 'healAbility',
+ formItem: 'select',
text: tHeal('Text'),
content: tHeal('Content'),
options: [
@@ -50,22 +51,22 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
],
fullWidth: true,
},
- {
- formItem: 'switch',
+ skillBuff: {
id: 'skillBuff',
+ formItem: 'switch',
text: t('Content.skillBuff.text'),
content: t('Content.skillBuff.content', {
skillHpPercentBuff: TsUtils.precisionRound(100 * skillHpPercentBuff),
skillHpFlatBuff: skillHpFlatBuff,
}),
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'skillBuff'),
- {
- formItem: 'slider',
+ const teammateContent: ContentDefinition = {
+ skillBuff: content.skillBuff,
+ teammateHPValue: {
id: 'teammateHPValue',
+ formItem: 'slider',
text: t('TeammateContent.teammateHPValue.text'),
content: t('TeammateContent.teammateHPValue.content', {
skillHpPercentBuff: TsUtils.precisionRound(100 * skillHpPercentBuff),
@@ -74,62 +75,66 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
min: 0,
max: 10000,
},
- ]
+ }
+
+ const defaults = {
+ healAbility: ULT_TYPE,
+ skillBuff: true,
+ }
+
+ const teammateDefaults = {
+ skillBuff: true,
+ teammateHPValue: 6000,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- healAbility: ULT_TYPE,
- skillBuff: true,
- }),
- teammateDefaults: () => ({
- skillBuff: true,
- teammateHPValue: 6000,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
-
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
-
- x.BASIC_TOUGHNESS_DMG += 30
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
+
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
if (r.healAbility == SKILL_TYPE) {
- x.HEAL_TYPE = SKILL_TYPE
- x.HEAL_SCALING += skillHealScaling
- x.HEAL_FLAT += skillHealFlat
+ x.HEAL_TYPE.set(SKILL_TYPE, Source.NONE)
+ x.HEAL_SCALING.buff(skillHealScaling, Source.NONE)
+ x.HEAL_FLAT.buff(skillHealFlat, Source.NONE)
}
if (r.healAbility == ULT_TYPE) {
- x.HEAL_TYPE = ULT_TYPE
- x.HEAL_SCALING += ultHealScaling
- x.HEAL_FLAT += ultHealFlat
+ x.HEAL_TYPE.set(ULT_TYPE, Source.NONE)
+ x.HEAL_SCALING.buff(ultHealScaling, Source.NONE)
+ x.HEAL_FLAT.buff(ultHealFlat, Source.NONE)
}
if (r.healAbility == NONE_TYPE) {
- x.HEAL_TYPE = NONE_TYPE
- x.HEAL_SCALING += talentHealScaling
- x.HEAL_FLAT += talentHealFlat
+ x.HEAL_TYPE.set(NONE_TYPE, Source.NONE)
+ x.HEAL_SCALING.buff(talentHealScaling, Source.NONE)
+ x.HEAL_FLAT.buff(talentHealFlat, Source.NONE)
}
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.RES] += (e >= 6 && m.skillBuff) ? 0.30 : 0
+ x.RES.buff((e >= 6 && m.skillBuff) ? 0.30 : 0, Source.NONE)
},
- precomputeTeammateEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const t = action.characterConditionals
+ precomputeTeammateEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const t: Conditionals = action.characterConditionals
- x[Stats.HP] += (t.skillBuff) ? skillHpPercentBuff * t.teammateHPValue : 0
- x[Stats.HP] += (e >= 6 && t.skillBuff) ? 0.06 * t.teammateHPValue : 0
- x[Stats.HP] += (t.skillBuff) ? skillHpFlatBuff : 0
+ x.HP.buff((t.skillBuff) ? skillHpPercentBuff * t.teammateHPValue : 0, Source.NONE)
+ x.HP.buff((e >= 6 && t.skillBuff) ? 0.06 * t.teammateHPValue : 0, Source.NONE)
+ x.HP.buff((t.skillBuff) ? skillHpFlatBuff : 0, Source.NONE)
const atkBuffValue = (e >= 4 && t.skillBuff) ? 0.03 * t.teammateHPValue : 0
- x[Stats.ATK] += atkBuffValue
+ x.ATK.buff(atkBuffValue, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardHpFinalizer(x)
standardHpHealFinalizer(x)
},
@@ -145,8 +150,8 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
condition: function () {
return true
},
- effect: function (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ effect: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
+ const r: Conditionals = action.characterConditionals
if (!r.skillBuff) {
return
}
@@ -157,7 +162,7 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
let stateBuffHP = 0
let stateBuffATK = 0
- const convertibleHpValue = x[Stats.HP] - x.RATIO_BASED_HP_BUFF
+ const convertibleHpValue = x.a[Key.HP] - x.a[Key.RATIO_BASED_HP_BUFF]
buffHP += skillHpPercentBuff * convertibleHpValue + skillHpFlatBuff
stateBuffHP += skillHpPercentBuff * stateValue + skillHpFlatBuff
@@ -171,17 +176,17 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
stateBuffHP += 0.06 * stateValue
}
- action.conditionalState[this.id] = x[Stats.HP]
+ action.conditionalState[this.id] = x.a[Key.HP]
const finalBuffHp = buffHP - (stateValue ? stateBuffHP : 0)
const finalBuffAtk = buffATK - (stateValue ? stateBuffATK : 0)
- x.RATIO_BASED_HP_BUFF += finalBuffHp
+ x.RATIO_BASED_HP_BUFF.buff(finalBuffHp, Source.NONE)
- buffStat(x, Stats.HP, finalBuffHp, action, context)
- buffStat(x, Stats.ATK, finalBuffAtk, action, context)
+ x.HP.buffDynamic(finalBuffHp, Source.NONE, action, context)
+ x.ATK.buffDynamic(finalBuffAtk, Source.NONE, action, context)
},
gpu: function (action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
return conditionalWgslWrapper(this, `
if (${wgslFalse(r.skillBuff)}) {
diff --git a/src/lib/conditionals/character/March7th.ts b/src/lib/conditionals/character/March7th.ts
index 83ad13d69..f64cc916f 100644
--- a/src/lib/conditionals/character/March7th.ts
+++ b/src/lib/conditionals/character/March7th.ts
@@ -1,12 +1,7 @@
-import { ASHBLAZING_ATK_STACK, ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- calculateAshblazingSet,
- gpuStandardDefShieldFinalizer,
- standardDefShieldFinalizer,
-} from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ASHBLAZING_ATK_STACK } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, calculateAshblazingSet, gpuStandardDefShieldFinalizer, standardDefShieldFinalizer } from 'lib/conditionals/conditionalUtils'
import { wgslTrue } from 'lib/gpu/injection/wgslUtils'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
@@ -26,33 +21,31 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
return {
content: () => [],
- teammateContent: () => [],
defaults: () => ({}),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
- x.FUA_SCALING += fuaScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.FUA_SCALING.buff(fuaScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.ULT_TOUGHNESS_DMG += 60
- x.FUA_TOUGHNESS_DMG += 30
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff(30, Source.NONE)
- x.SHIELD_SCALING += skillShieldScaling
- x.SHIELD_FLAT += skillShieldFlat
+ x.SHIELD_SCALING.buff(skillShieldScaling, Source.NONE)
+ x.SHIELD_FLAT.buff(skillShieldFlat, Source.NONE)
return x
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
const ashblazingAtk = calculateAshblazingSet(x, action, context, hitMulti)
- x.BASIC_DMG += x.BASIC_SCALING * x[Stats.ATK]
- x.SKILL_DMG += x.SKILL_SCALING * x[Stats.ATK]
- x.ULT_DMG += x.ULT_SCALING * x[Stats.ATK]
- x.FUA_DMG += x.FUA_SCALING * (x[Stats.ATK] + ashblazingAtk)
- x.FUA_DMG += (e >= 4) ? 0.30 * x[Stats.DEF] : 0
+ x.BASIC_DMG.buff(x.a[Key.BASIC_SCALING] * x.a[Key.ATK], Source.NONE)
+ x.SKILL_DMG.buff(x.a[Key.SKILL_SCALING] * x.a[Key.ATK], Source.NONE)
+ x.ULT_DMG.buff(x.a[Key.ULT_SCALING] * x.a[Key.ATK], Source.NONE)
+ x.FUA_DMG.buff(x.a[Key.FUA_SCALING] * (x.a[Key.ATK] + ashblazingAtk), Source.NONE)
+ x.FUA_DMG.buff((e >= 4) ? 0.30 * x.a[Key.DEF] : 0, Source.NONE)
standardDefShieldFinalizer(x)
},
diff --git a/src/lib/conditionals/character/March7thImaginary.ts b/src/lib/conditionals/character/March7thImaginary.ts
index 8c8efde9c..5e21c56d7 100644
--- a/src/lib/conditionals/character/March7thImaginary.ts
+++ b/src/lib/conditionals/character/March7thImaginary.ts
@@ -1,12 +1,11 @@
-import { ASHBLAZING_ATK_STACK, BASIC_TYPE, ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ASHBLAZING_ATK_STACK, BASIC_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityCd, buffAbilityDmg } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -23,124 +22,124 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
// 0.06
const fuaHitCountMulti = ASHBLAZING_ATK_STACK * (1 * 0.40 + 2 * 0.60)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ enhancedBasic: true,
+ basicAttackHits: 6,
+ talentDmgBuff: true,
+ selfSpdBuff: true,
+ masterAdditionalDmgBuff: true,
+ masterToughnessRedBuff: true,
+ e6CdBuff: true,
+ }
+
+ const teammateDefaults = {
+ masterBuff: true,
+ masterCdBeBuffs: true,
+ }
+
+ const content: ContentDefinition = {
+ enhancedBasic: {
id: 'enhancedBasic',
+ formItem: 'switch',
text: t('Content.enhancedBasic.text'),
content: t('Content.enhancedBasic.content', { BasicEnhancedScaling: TsUtils.precisionRound(100 * basicEnhancedScaling) }),
},
- {
- formItem: 'slider',
+ basicAttackHits: {
id: 'basicAttackHits',
+ formItem: 'slider',
text: t('Content.basicAttackHits.text'),
content: t('Content.basicAttackHits.content', { BasicEnhancedScaling: TsUtils.precisionRound(100 * basicEnhancedScaling) }),
min: 3,
max: 6,
},
- {
- formItem: 'switch',
+ masterAdditionalDmgBuff: {
id: 'masterAdditionalDmgBuff',
+ formItem: 'switch',
text: t('Content.masterAdditionalDmgBuff.text'),
content: t('Content.masterAdditionalDmgBuff.content', { ShifuDmgBuff: TsUtils.precisionRound(100 * basicExtraScalingMasterBuff) }),
},
- {
- formItem: 'switch',
+ masterToughnessRedBuff: {
id: 'masterToughnessRedBuff',
+ formItem: 'switch',
text: t('Content.masterToughnessRedBuff.text'),
content: t('Content.masterToughnessRedBuff.content'),
},
- {
- formItem: 'switch',
+ talentDmgBuff: {
id: 'talentDmgBuff',
+ formItem: 'switch',
text: t('Content.talentDmgBuff.text'),
content: t('Content.talentDmgBuff.content', { TalentDmgBuff: TsUtils.precisionRound(100 * talentDmgBuff) }),
},
- {
- formItem: 'switch',
+ selfSpdBuff: {
id: 'selfSpdBuff',
+ formItem: 'switch',
text: t('Content.selfSpdBuff.text'),
content: t('Content.selfSpdBuff.content'),
disabled: e < 1,
},
- {
- formItem: 'switch',
+ e6CdBuff: {
id: 'e6CdBuff',
+ formItem: 'switch',
text: t('Content.e6CdBuff.text'),
content: t('Content.e6CdBuff.content'),
disabled: e < 6,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- {
- formItem: 'switch',
+ const teammateContent: ContentDefinition = {
+ masterBuff: {
id: 'masterBuff',
+ formItem: 'switch',
text: t('TeammateContent.masterBuff.text'),
content: t('TeammateContent.masterBuff.content', { ShifuSpeedBuff: TsUtils.precisionRound(100 * skillSpdScaling) }),
},
- {
- formItem: 'switch',
+ masterCdBeBuffs: {
id: 'masterCdBeBuffs',
+ formItem: 'switch',
text: t('TeammateContent.masterCdBeBuffs.text'),
content: t('TeammateContent.masterCdBeBuffs.content'),
},
- ]
-
- const defaults = {
- enhancedBasic: true,
- basicAttackHits: 6,
- talentDmgBuff: true,
- selfSpdBuff: true,
- masterAdditionalDmgBuff: true,
- masterToughnessRedBuff: true,
- e6CdBuff: true,
- }
-
- const teammateDefaults = {
- masterBuff: true,
- masterCdBeBuffs: true,
}
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => (defaults),
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
teammateDefaults: () => (teammateDefaults),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- x[Stats.SPD_P] += (e >= 1 && r.selfSpdBuff) ? 0.10 : 0
- buffAbilityDmg(x, BASIC_TYPE, talentDmgBuff, (r.talentDmgBuff))
+ x.SPD_P.buff((e >= 1 && r.selfSpdBuff) ? 0.10 : 0, Source.NONE)
+ buffAbilityDmg(x, BASIC_TYPE, (r.talentDmgBuff) ? talentDmgBuff : 0, Source.NONE)
- buffAbilityCd(x, BASIC_TYPE, 0.50, (e >= 6 && r.e6CdBuff && r.enhancedBasic))
+ buffAbilityCd(x, BASIC_TYPE, (e >= 6 && r.e6CdBuff && r.enhancedBasic) ? 0.50 : 0, Source.NONE)
const additionalMasterBuffScaling = (r.masterAdditionalDmgBuff)
? basicExtraScalingMasterBuff * r.basicAttackHits
: 0
- x.BASIC_SCALING += (r.enhancedBasic) ? basicEnhancedScaling * r.basicAttackHits : basicScaling
- x.BASIC_SCALING += (r.enhancedBasic) ? additionalMasterBuffScaling : basicExtraScalingMasterBuff
- x.ULT_SCALING += ultScaling
- x.FUA_SCALING += (e >= 2) ? 0.60 : 0
+ x.BASIC_SCALING.buff((r.enhancedBasic) ? basicEnhancedScaling * r.basicAttackHits : basicScaling, Source.NONE)
+ x.BASIC_SCALING.buff((r.enhancedBasic) ? additionalMasterBuffScaling : basicExtraScalingMasterBuff, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.FUA_SCALING.buff((e >= 2) ? 0.60 : 0, Source.NONE)
const toughnessDmgBoost = (r.masterToughnessRedBuff) ? 2.0 : 1.0
- x.BASIC_TOUGHNESS_DMG += toughnessDmgBoost * ((r.enhancedBasic) ? 15 * r.basicAttackHits : 30)
- x.ULT_TOUGHNESS_DMG += 90
- x.FUA_TOUGHNESS_DMG += (e >= 2) ? 30 : 0
+ x.BASIC_TOUGHNESS_DMG.buff(toughnessDmgBoost * ((r.enhancedBasic) ? 15 * r.basicAttackHits : 30), Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(90, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff((e >= 2) ? 30 : 0, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
},
- precomputeTeammateEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const t = action.characterConditionals
+ precomputeTeammateEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const t: Conditionals = action.characterConditionals
- x[Stats.SPD_P] += (t.masterBuff) ? skillSpdScaling : 0
+ x.SPD_P.buff((t.masterBuff) ? skillSpdScaling : 0, Source.NONE)
- x[Stats.CD] += (t.masterBuff && t.masterCdBeBuffs) ? 0.60 : 0
- x[Stats.BE] += (t.masterBuff && t.masterCdBeBuffs) ? 0.36 : 0
+ x.CD.buff((t.masterBuff && t.masterCdBeBuffs) ? 0.60 : 0, Source.NONE)
+ x.BE.buff((t.masterBuff && t.masterCdBeBuffs) ? 0.36 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardFuaAtkFinalizer(x, action, context, fuaHitCountMulti)
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
diff --git a/src/lib/conditionals/character/Misha.ts b/src/lib/conditionals/character/Misha.ts
index e5d07e730..22bc771fd 100644
--- a/src/lib/conditionals/character/Misha.ts
+++ b/src/lib/conditionals/character/Misha.ts
@@ -1,16 +1,9 @@
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardAtkFinalizer,
- standardAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -22,76 +15,80 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
let ultStackScaling = ult(e, 0.60, 0.65)
ultStackScaling += (e >= 4 ? 0.06 : 0)
- const content: ContentItem[] = [
- {
- formItem: 'slider',
+ const defaults = {
+ ultHitsOnTarget: 10,
+ enemyFrozen: true,
+ e2DefReduction: true,
+ e6UltDmgBoost: true,
+ }
+
+ const teammateDefaults = {
+ e2DefReduction: true,
+ }
+
+ const content: ContentDefinition = {
+ ultHitsOnTarget: {
id: 'ultHitsOnTarget',
+ formItem: 'slider',
text: t('Content.ultHitsOnTarget.text'),
content: t('Content.ultHitsOnTarget.content', { ultStackScaling: TsUtils.precisionRound(100 * ultStackScaling) }),
min: 1,
max: 10,
},
- {
- formItem: 'switch',
+ enemyFrozen: {
id: 'enemyFrozen',
+ formItem: 'switch',
text: t('Content.enemyFrozen.text'),
content: t('Content.enemyFrozen.content'),
},
- {
- formItem: 'switch',
+ e2DefReduction: {
id: 'e2DefReduction',
+ formItem: 'switch',
text: t('Content.e2DefReduction.text'),
content: t('Content.e2DefReduction.content'),
disabled: e < 2,
},
- {
- formItem: 'switch',
+ e6UltDmgBoost: {
id: 'e6UltDmgBoost',
+ formItem: 'switch',
text: t('Content.e6UltDmgBoost.text'),
content: t('Content.e6UltDmgBoost.content'),
disabled: e < 6,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'e2DefReduction'),
- ]
+ const teammateContent: ContentDefinition = {
+ e2DefReduction: content.e2DefReduction,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- ultHitsOnTarget: 10,
- enemyFrozen: true,
- e2DefReduction: true,
- e6UltDmgBoost: true,
- }),
- teammateDefaults: () => ({
- e2DefReduction: true,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- x[Stats.CD] += (r.enemyFrozen) ? 0.30 : 0
+ x.CD.buff((r.enemyFrozen) ? 0.30 : 0, Source.NONE)
- x.ELEMENTAL_DMG += (e >= 6 && r.e6UltDmgBoost) ? 0.30 : 0
+ x.ELEMENTAL_DMG.buff((e >= 6 && r.e6UltDmgBoost) ? 0.30 : 0, Source.NONE)
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultStackScaling * (r.ultHitsOnTarget)
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultStackScaling * (r.ultHitsOnTarget), Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 30 + 15 * (r.ultHitsOnTarget - 1)
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(30 + 15 * (r.ultHitsOnTarget - 1), Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x.DEF_PEN += (e >= 2 && m.e2DefReduction) ? 0.16 : 0
+ x.DEF_PEN.buff((e >= 2 && m.e2DefReduction) ? 0.16 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/Moze.ts b/src/lib/conditionals/character/Moze.ts
index 2977b1711..77c249037 100644
--- a/src/lib/conditionals/character/Moze.ts
+++ b/src/lib/conditionals/character/Moze.ts
@@ -1,17 +1,11 @@
-import { ASHBLAZING_ATK_STACK, ComputedStatsObject, FUA_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardFuaAtkFinalizer,
- standardFuaAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ASHBLAZING_ATK_STACK, FUA_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityVulnerability } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -27,90 +21,90 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const fuaHitCountMulti = ASHBLAZING_ATK_STACK * (1 * 0.08 + 2 * 0.08 + 3 * 0.08 + 4 * 0.08 + 5 * 0.08 + 6 * 0.6)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ preyMark: true,
+ e2CdBoost: true,
+ e4DmgBuff: true,
+ e6MultiplierIncrease: true,
+ }
+
+ const teammateDefaults = {
+ preyMark: true,
+ e2CdBoost: true,
+ }
+
+ const content: ContentDefinition = {
+ preyMark: {
id: 'preyMark',
+ formItem: 'switch',
text: t('Content.preyMark.text'),
content: t('Content.preyMark.content', {
PreyAdditionalMultiplier: TsUtils.precisionRound(100 * additionalDmgScaling),
FuaScaling: TsUtils.precisionRound(100 * fuaScaling),
}),
},
- {
- formItem: 'switch',
+ e2CdBoost: {
id: 'e2CdBoost',
+ formItem: 'switch',
text: t('Content.e2CdBoost.text'),
content: t('Content.e2CdBoost.content'),
disabled: e < 2,
},
- {
- formItem: 'switch',
+ e4DmgBuff: {
id: 'e4DmgBuff',
+ formItem: 'switch',
text: t('Content.e4DmgBuff.text'),
content: t('Content.e4DmgBuff.content'),
disabled: e < 4,
},
- {
- formItem: 'switch',
+ e6MultiplierIncrease: {
id: 'e6MultiplierIncrease',
+ formItem: 'switch',
text: t('Content.e6MultiplierIncrease.text'),
content: t('Content.e6MultiplierIncrease.content'),
disabled: e < 6,
},
- ]
-
- const teammateContent: ContentItem[] = [
- findContentId(content, 'preyMark'),
- findContentId(content, 'e2CdBoost'),
- ]
-
- const defaults = {
- preyMark: true,
- e2CdBoost: true,
- e4DmgBuff: true,
- e6MultiplierIncrease: true,
}
- const teammateDefaults = {
- preyMark: true,
- e2CdBoost: true,
+ const teammateContent: ContentDefinition = {
+ preyMark: content.preyMark,
+ e2CdBoost: content.e2CdBoost,
}
return {
- content: () => content,
- teammateContent: () => teammateContent,
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
defaults: () => defaults,
teammateDefaults: () => teammateDefaults,
- initializeConfigurations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- x.ULT_DMG_TYPE = ULT_TYPE | FUA_TYPE
+ initializeConfigurations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ x.ULT_DMG_TYPE.set(ULT_TYPE | FUA_TYPE, Source.NONE)
},
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- x.ELEMENTAL_DMG += (e >= 4 && r.e4DmgBuff) ? 0.30 : 0
+ x.ELEMENTAL_DMG.buff((e >= 4 && r.e4DmgBuff) ? 0.30 : 0, Source.NONE)
- x.BASIC_SCALING += basicScaling + ((r.preyMark) ? additionalDmgScaling : 0)
- x.SKILL_SCALING += skillScaling + ((r.preyMark) ? additionalDmgScaling : 0)
- x.FUA_SCALING += fuaScaling + ((r.preyMark) ? additionalDmgScaling : 0)
- x.FUA_SCALING += (e >= 6 && r.e6MultiplierIncrease) ? 0.25 : 0
- x.ULT_SCALING += ultScaling + ((r.preyMark) ? additionalDmgScaling : 0)
+ x.BASIC_SCALING.buff(basicScaling + ((r.preyMark) ? additionalDmgScaling : 0), Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling + ((r.preyMark) ? additionalDmgScaling : 0), Source.NONE)
+ x.FUA_SCALING.buff(fuaScaling + ((r.preyMark) ? additionalDmgScaling : 0), Source.NONE)
+ x.FUA_SCALING.buff((e >= 6 && r.e6MultiplierIncrease) ? 0.25 : 0, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling + ((r.preyMark) ? additionalDmgScaling : 0), Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 90
- x.FUA_TOUGHNESS_DMG += 30
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(90, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff(30, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- buffAbilityVulnerability(x, FUA_TYPE, 0.25, (m.preyMark))
+ buffAbilityVulnerability(x, FUA_TYPE, (m.preyMark) ? 0.25 : 0, Source.NONE)
- x[Stats.CD] += (e >= 2 && m.preyMark && m.e2CdBoost) ? 0.40 : 0
+ x.CD.buff((e >= 2 && m.preyMark && m.e2CdBoost) ? 0.40 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardFuaAtkFinalizer(x, action, context, fuaHitCountMulti)
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
diff --git a/src/lib/conditionals/character/Natasha.ts b/src/lib/conditionals/character/Natasha.ts
index 2dfe05552..994d2d3ee 100644
--- a/src/lib/conditionals/character/Natasha.ts
+++ b/src/lib/conditionals/character/Natasha.ts
@@ -1,11 +1,10 @@
-import { ComputedStatsObject, SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, standardHpHealFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, standardHpHealFinalizer } from 'lib/conditionals/conditionalUtils'
import { wgslTrue } from 'lib/gpu/injection/wgslUtils'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -21,10 +20,10 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const skillHealScaling = skill(e, 0.105, 0.112)
const skillHealFlat = skill(e, 280, 311.5)
- const content: ContentItem[] = [
- {
- formItem: 'select',
+ const content: ContentDefinition = {
+ healAbility: {
id: 'healAbility',
+ formItem: 'select',
text: tHeal('Text'),
content: tHeal('Content'),
options: [
@@ -33,44 +32,42 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
],
fullWidth: true,
},
- ]
+ }
const defaults = {
healAbility: ULT_TYPE,
}
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => (defaults),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- x[Stats.OHB] += 0.10
+ x.OHB.buff(0.10, Source.NONE)
- x.BASIC_SCALING += basicScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
if (r.healAbility == SKILL_TYPE) {
- x.HEAL_TYPE = SKILL_TYPE
- x.HEAL_SCALING += skillHealScaling
- x.HEAL_FLAT += skillHealFlat
+ x.HEAL_TYPE.set(SKILL_TYPE, Source.NONE)
+ x.HEAL_SCALING.buff(skillHealScaling, Source.NONE)
+ x.HEAL_FLAT.buff(skillHealFlat, Source.NONE)
}
if (r.healAbility == ULT_TYPE) {
- x.HEAL_TYPE = ULT_TYPE
- x.HEAL_SCALING += ultHealScaling
- x.HEAL_FLAT += ultHealFlat
+ x.HEAL_TYPE.set(ULT_TYPE, Source.NONE)
+ x.HEAL_SCALING.buff(ultHealScaling, Source.NONE)
+ x.HEAL_FLAT.buff(ultHealFlat, Source.NONE)
}
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- x.BASIC_DMG += x.BASIC_SCALING * x[Stats.ATK]
- x.BASIC_DMG += (e >= 6) ? 0.40 * x[Stats.HP] : 0
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ x.BASIC_DMG.buff(x.a[Key.BASIC_SCALING] * x.a[Key.ATK], Source.NONE)
+ x.BASIC_DMG.buff((e >= 6) ? 0.40 * x.a[Key.HP] : 0, Source.NONE)
standardHpHealFinalizer(x)
},
diff --git a/src/lib/conditionals/character/Pela.ts b/src/lib/conditionals/character/Pela.ts
index a07d20c12..25759533c 100644
--- a/src/lib/conditionals/character/Pela.ts
+++ b/src/lib/conditionals/character/Pela.ts
@@ -1,13 +1,12 @@
-import { BASIC_TYPE, ComputedStatsObject, SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, findContentId } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { BASIC_TYPE, SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition } from 'lib/conditionals/conditionalUtils'
import { wgslTrue } from 'lib/gpu/injection/wgslUtils'
import { buffAbilityDmg } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -20,97 +19,101 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const skillScaling = skill(e, 2.10, 2.31)
const ultScaling = ult(e, 1.00, 1.08)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ teamEhrBuff: true,
+ enemyDebuffed: true,
+ skillRemovedBuff: false,
+ ultDefPenDebuff: true,
+ e4SkillResShred: true,
+ }
+
+ const teammateDefaults = {
+ teamEhrBuff: true,
+ ultDefPenDebuff: true,
+ e4SkillResShred: true,
+ }
+
+ const content: ContentDefinition = {
+ teamEhrBuff: {
id: 'teamEhrBuff',
+ formItem: 'switch',
text: t('Content.teamEhrBuff.text'),
content: t('Content.teamEhrBuff.content'),
},
- {
- formItem: 'switch',
+ enemyDebuffed: {
id: 'enemyDebuffed',
+ formItem: 'switch',
text: t('Content.enemyDebuffed.text'),
content: t('Content.enemyDebuffed.content'),
},
- {
- formItem: 'switch',
+ skillRemovedBuff: {
id: 'skillRemovedBuff',
+ formItem: 'switch',
text: t('Content.skillRemovedBuff.text'),
content: t('Content.skillRemovedBuff.content'),
},
- {
- formItem: 'switch',
+ ultDefPenDebuff: {
id: 'ultDefPenDebuff',
+ formItem: 'switch',
text: t('Content.ultDefPenDebuff.text'),
content: t('Content.ultDefPenDebuff.content', { ultDefPenValue: TsUtils.precisionRound(100 * ultDefPenValue) }),
},
- {
- formItem: 'switch',
+ e4SkillResShred: {
id: 'e4SkillResShred',
+ formItem: 'switch',
text: t('Content.e4SkillResShred.text'),
content: t('Content.e4SkillResShred.content'),
disabled: e < 4,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'teamEhrBuff'),
- findContentId(content, 'ultDefPenDebuff'),
- findContentId(content, 'e4SkillResShred'),
- ]
+ const teammateContent: ContentDefinition = {
+ teamEhrBuff: content.teamEhrBuff,
+ ultDefPenDebuff: content.ultDefPenDebuff,
+ e4SkillResShred: content.e4SkillResShred,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- teamEhrBuff: true,
- enemyDebuffed: true,
- skillRemovedBuff: false,
- ultDefPenDebuff: true,
- e4SkillResShred: true,
- }),
- teammateDefaults: () => ({
- teamEhrBuff: true,
- ultDefPenDebuff: true,
- e4SkillResShred: true,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.SPD_P] += (e >= 2 && r.skillRemovedBuff) ? 0.10 : 0
+ x.SPD_P.buff((e >= 2 && r.skillRemovedBuff) ? 0.10 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
- buffAbilityDmg(x, BASIC_TYPE | SKILL_TYPE | ULT_TYPE, 0.20, (r.skillRemovedBuff))
- x.ELEMENTAL_DMG += (r.enemyDebuffed) ? 0.20 : 0
+ buffAbilityDmg(x, BASIC_TYPE | SKILL_TYPE | ULT_TYPE, (r.skillRemovedBuff) ? 0.20 : 0, Source.NONE)
+ x.ELEMENTAL_DMG.buff((r.enemyDebuffed) ? 0.20 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 60
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.EHR] += (m.teamEhrBuff) ? 0.10 : 0
+ x.EHR.buff((m.teamEhrBuff) ? 0.10 : 0, Source.NONE)
- x.DEF_PEN += (m.ultDefPenDebuff) ? ultDefPenValue : 0
- x.ICE_RES_PEN += (e >= 4 && m.e4SkillResShred) ? 0.12 : 0
+ x.DEF_PEN.buff((m.ultDefPenDebuff) ? ultDefPenValue : 0, Source.NONE)
+ x.ICE_RES_PEN.buff((e >= 4 && m.e4SkillResShred) ? 0.12 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- x.BASIC_DMG += x.BASIC_SCALING * x[Stats.ATK]
- x.SKILL_DMG += x.SKILL_SCALING * x[Stats.ATK]
- x.ULT_DMG += x.ULT_SCALING * x[Stats.ATK]
-
- x.BASIC_DMG += (e >= 6) ? 0.40 * x[Stats.ATK] : 0
- x.SKILL_DMG += (e >= 6) ? 0.40 * x[Stats.ATK] : 0
- x.ULT_DMG += (e >= 6) ? 0.40 * x[Stats.ATK] : 0
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ x.BASIC_DMG.buff(x.a[Key.BASIC_SCALING] * x.a[Key.ATK], Source.NONE)
+ x.SKILL_DMG.buff(x.a[Key.SKILL_SCALING] * x.a[Key.ATK], Source.NONE)
+ x.ULT_DMG.buff(x.a[Key.ULT_SCALING] * x.a[Key.ATK], Source.NONE)
+
+ x.BASIC_DMG.buff((e >= 6) ? 0.40 * x.a[Key.ATK] : 0, Source.NONE)
+ x.SKILL_DMG.buff((e >= 6) ? 0.40 * x.a[Key.ATK] : 0, Source.NONE)
+ x.ULT_DMG.buff((e >= 6) ? 0.40 * x.a[Key.ATK] : 0, Source.NONE)
},
gpuFinalizeCalculations: () => {
return `
diff --git a/src/lib/conditionals/character/Qingque.ts b/src/lib/conditionals/character/Qingque.ts
index 811a7859d..2a8a0a5e0 100644
--- a/src/lib/conditionals/character/Qingque.ts
+++ b/src/lib/conditionals/character/Qingque.ts
@@ -1,13 +1,12 @@
-import { ASHBLAZING_ATK_STACK, ComputedStatsObject, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ASHBLAZING_ATK_STACK, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardFuaAtkFinalizer, standardFuaAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityDmg } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
import { NumberToNumberMap } from 'types/Common'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -31,70 +30,70 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const hitMultiSingle = ASHBLAZING_ATK_STACK * (1 * 1 / 1)
function getHitMulti(action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
return r.basicEnhanced
? hitMultiByTargetsBlast[context.enemyCount]
: hitMultiSingle
}
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ basicEnhanced: true,
+ basicEnhancedSpdBuff: false,
+ skillDmgIncreaseStacks: 4,
+ }
+
+ const content: ContentDefinition = {
+ basicEnhanced: {
id: 'basicEnhanced',
+ formItem: 'switch',
text: t('Content.basicEnhanced.text'),
content: t('Content.basicEnhanced.content', { talentAtkBuff: TsUtils.precisionRound(100 * talentAtkBuff) }),
},
- {
- formItem: 'switch',
+ basicEnhancedSpdBuff: {
id: 'basicEnhancedSpdBuff',
+ formItem: 'switch',
text: t('Content.basicEnhancedSpdBuff.text'),
content: t('Content.basicEnhancedSpdBuff.content'),
},
- {
- formItem: 'slider',
+ skillDmgIncreaseStacks: {
id: 'skillDmgIncreaseStacks',
+ formItem: 'slider',
text: t('Content.skillDmgIncreaseStacks.text'),
content: t('Content.skillDmgIncreaseStacks.content', { skillStackDmg: TsUtils.precisionRound(100 * skillStackDmg) }),
min: 0,
max: 4,
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => ({
- basicEnhanced: true,
- basicEnhancedSpdBuff: false,
- skillDmgIncreaseStacks: 4,
- }),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.ATK_P] += (r.basicEnhanced) ? talentAtkBuff : 0
- x[Stats.SPD_P] += (r.basicEnhancedSpdBuff) ? 0.10 : 0
+ x.ATK_P.buff((r.basicEnhanced) ? talentAtkBuff : 0, Source.NONE)
+ x.SPD_P.buff((r.basicEnhancedSpdBuff) ? 0.10 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += (r.basicEnhanced) ? basicEnhancedScaling : basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
- x.FUA_SCALING += (e >= 4) ? x.BASIC_SCALING : 0
+ x.BASIC_SCALING.buff((r.basicEnhanced) ? basicEnhancedScaling : basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.FUA_SCALING.buff((e >= 4) ? x.a[Key.BASIC_SCALING] : 0, Source.NONE)
// Boost
- x.ELEMENTAL_DMG += r.skillDmgIncreaseStacks * skillStackDmg
- buffAbilityDmg(x, ULT_TYPE, 0.10, (e >= 1))
+ x.ELEMENTAL_DMG.buff(r.skillDmgIncreaseStacks * skillStackDmg, Source.NONE)
+ buffAbilityDmg(x, ULT_TYPE, (e >= 1) ? 0.10 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += (r.basicEnhanced) ? 60 : 30
- x.ULT_TOUGHNESS_DMG += 60
- x.FUA_TOUGHNESS_DMG += (r.basicEnhanced) ? 60 : 30
+ x.BASIC_TOUGHNESS_DMG.buff((r.basicEnhanced) ? 60 : 30, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff((r.basicEnhanced) ? 60 : 30, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardFuaAtkFinalizer(x, action, context, getHitMulti(action, context))
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
diff --git a/src/lib/conditionals/character/Rappa.ts b/src/lib/conditionals/character/Rappa.ts
index e2f1b2e9b..8c8bf1230 100644
--- a/src/lib/conditionals/character/Rappa.ts
+++ b/src/lib/conditionals/character/Rappa.ts
@@ -1,12 +1,10 @@
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { CURRENT_DATA_VERSION, Stats } from 'lib/constants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { RappaConversionConditional } from 'lib/gpu/conditionals/dynamicConditionals'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -25,20 +23,34 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const maxChargeStacks = e >= 6 ? 15 : 10
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const teammateDefaults = {
+ teammateBreakVulnerability: 0.10,
+ e4SpdBuff: true,
+ }
+
+ const defaults = {
+ sealformActive: true,
+ atkToBreakVulnerability: true,
+ chargeStacks: e >= 6 ? 10 : 5,
+ e1DefPen: true,
+ e2Buffs: true,
+ e4SpdBuff: true,
+ }
+
+ const content: ContentDefinition = {
+ sealformActive: {
id: 'sealformActive',
+ formItem: 'switch',
text: t('Content.sealformActive.text'),
content: t('Content.sealformActive.content', { ultBeBuff: TsUtils.precisionRound(100 * ultBeBuff) }),
},
- {
- formItem: 'switch',
+ atkToBreakVulnerability: {
id: 'atkToBreakVulnerability',
+ formItem: 'switch',
text: t('Content.atkToBreakVulnerability.text'),
content: t('Content.atkToBreakVulnerability.content'),
},
- {
+ chargeStacks: {
id: 'chargeStacks',
formItem: 'slider',
text: t('Content.chargeStacks.text'),
@@ -46,104 +58,91 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
min: 0,
max: maxChargeStacks,
},
- {
- formItem: 'switch',
+ e1DefPen: {
id: 'e1DefPen',
+ formItem: 'switch',
text: t('Content.e1DefPen.text'),
content: t('Content.e1DefPen.content'),
disabled: e < 1,
},
- {
- formItem: 'switch',
+ e2Buffs: {
id: 'e2Buffs',
+ formItem: 'switch',
text: t('Content.e2Buffs.text'),
content: t('Content.e2Buffs.content'),
disabled: e < 2,
},
- {
- formItem: 'switch',
+ e4SpdBuff: {
id: 'e4SpdBuff',
+ formItem: 'switch',
text: t('Content.e4SpdBuff.text'),
content: t('Content.e4SpdBuff.content'),
disabled: e < 4,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- {
- formItem: 'slider',
+ const teammateContent: ContentDefinition = {
+ teammateBreakVulnerability: {
id: 'teammateBreakVulnerability',
+ formItem: 'slider',
text: t('TeammateContent.teammateBreakVulnerability.text'),
content: t('TeammateContent.teammateBreakVulnerability.content'),
min: 0,
max: 0.10,
percent: true,
},
- {
- formItem: 'switch',
+ e4SpdBuff: {
id: 'e4SpdBuff',
+ formItem: 'switch',
text: t('TeammateContent.e4SpdBuff.text'),
content: t('TeammateContent.e4SpdBuff.content'),
disabled: e < 4,
},
- ]
-
- const defaults = {
- sealformActive: true,
- atkToBreakVulnerability: true,
- chargeStacks: e >= 6 ? 10 : 5,
- e1DefPen: true,
- e2Buffs: true,
- e4SpdBuff: true,
}
-
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => (defaults),
- teammateDefaults: () => ({
- teammateBreakVulnerability: 0.10,
- e4SpdBuff: true,
- }),
- initializeConfigurations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ initializeConfigurations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
if (r.sealformActive) {
- x.ENEMY_WEAKNESS_BROKEN = 1
+ x.ENEMY_WEAKNESS_BROKEN.set(1, Source.NONE)
}
},
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- x[Stats.BE] += (r.sealformActive) ? ultBeBuff : 0
- x.BREAK_EFFICIENCY_BOOST += (r.sealformActive) ? 0.50 : 0
+ x.BE.buff((r.sealformActive) ? ultBeBuff : 0, Source.NONE)
+ x.BREAK_EFFICIENCY_BOOST.buff((r.sealformActive) ? 0.50 : 0, Source.NONE)
- x.DEF_PEN += (e >= 1 && r.sealformActive && r.e1DefPen) ? 0.15 : 0
+ x.DEF_PEN.buff((e >= 1 && r.sealformActive && r.e1DefPen) ? 0.15 : 0, Source.NONE)
- x[Stats.SPD_P] += (e >= 4 && r.sealformActive && r.e4SpdBuff) ? 0.12 : 0
+ x.SPD_P.buff((e >= 4 && r.sealformActive && r.e4SpdBuff) ? 0.12 : 0, Source.NONE)
- x.BASIC_SUPER_BREAK_MODIFIER += (r.sealformActive) ? 0.60 : 0
+ x.BASIC_SUPER_BREAK_MODIFIER.buff((r.sealformActive) ? 0.60 : 0, Source.NONE)
- x.BASIC_BREAK_DMG_MODIFIER = talentBreakDmgModifier + r.chargeStacks * talentChargeMultiplier
+ x.BASIC_BREAK_DMG_MODIFIER.set(talentBreakDmgModifier + r.chargeStacks * talentChargeMultiplier, Source.NONE)
- x.BASIC_SCALING += (r.sealformActive) ? basicEnhancedScaling : basicScaling
- x.SKILL_SCALING += skillScaling
+ x.BASIC_SCALING.buff((r.sealformActive) ? basicEnhancedScaling : basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += (r.sealformActive) ? 75 + (2 + r.chargeStacks) * 3 : 30
- x.SKILL_TOUGHNESS_DMG += 30
+ x.BASIC_TOUGHNESS_DMG.buff((r.sealformActive) ? 75 + (2 + r.chargeStacks) * 3 : 30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(30, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
},
- precomputeTeammateEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const t = action.characterConditionals
+ precomputeTeammateEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const t: Conditionals = action.characterConditionals
- x.BREAK_VULNERABILITY += t.teammateBreakVulnerability
+ x.BREAK_VULNERABILITY.buff(t.teammateBreakVulnerability, Source.NONE)
- x[Stats.SPD_P] += (e >= 4 && t.e4SpdBuff) ? 0.12 : 0
+ x.SPD_P.buff((e >= 4 && t.e4SpdBuff) ? 0.12 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
dynamicConditionals: [RappaConversionConditional],
}
diff --git a/src/lib/conditionals/character/Robin.ts b/src/lib/conditionals/character/Robin.ts
index 28755fa37..e0755ade5 100644
--- a/src/lib/conditionals/character/Robin.ts
+++ b/src/lib/conditionals/character/Robin.ts
@@ -1,13 +1,12 @@
-import { ComputedStatsObject, FUA_TYPE } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, findContentId } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { FUA_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition } from 'lib/conditionals/conditionalUtils'
import { wgslTrue } from 'lib/gpu/injection/wgslUtils'
import { buffAbilityCd } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -22,10 +21,30 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const basicScaling = basic(e, 1.00, 1.10)
const ultScaling = ult(e, 1.20, 1.296)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ concertoActive: true,
+ skillDmgBuff: true,
+ talentCdBuff: true,
+ e1UltResPen: true,
+ e4TeamResBuff: false,
+ e6UltCDBoost: true,
+ }
+
+ const teammateDefaults = {
+ concertoActive: true,
+ skillDmgBuff: true,
+ talentCdBuff: true,
+ teammateATKValue: 5000,
+ traceFuaCdBoost: true,
+ e1UltResPen: true,
+ e2UltSpdBuff: false,
+ e4TeamResBuff: true,
+ }
+
+ const content: ContentDefinition = {
+ concertoActive: {
id: 'concertoActive',
+ formItem: 'switch',
text: t('Content.concertoActive.text'),
content: t('Content.concertoActive.content', {
ultAtkBuffScalingValue: TsUtils.precisionRound(100 * ultAtkBuffScalingValue),
@@ -33,47 +52,47 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
ultScaling: TsUtils.precisionRound(100 * ultScaling),
}),
},
- {
- formItem: 'switch',
+ skillDmgBuff: {
id: 'skillDmgBuff',
+ formItem: 'switch',
text: t('Content.skillDmgBuff.text'),
content: t('Content.skillDmgBuff.content', { skillDmgBuffValue: TsUtils.precisionRound(100 * skillDmgBuffValue) }),
},
- {
- formItem: 'switch',
+ talentCdBuff: {
id: 'talentCdBuff',
+ formItem: 'switch',
text: t('Content.talentCdBuff.text'),
content: t('Content.talentCdBuff.content', { talentCdBuffValue: TsUtils.precisionRound(100 * talentCdBuffValue) }),
},
- {
- formItem: 'switch',
+ e1UltResPen: {
id: 'e1UltResPen',
+ formItem: 'switch',
text: t('Content.e1UltResPen.text'),
content: t('Content.e1UltResPen.content'),
disabled: e < 1,
},
- {
- formItem: 'switch',
+ e4TeamResBuff: {
id: 'e4TeamResBuff',
+ formItem: 'switch',
text: t('Content.e4TeamResBuff.text'),
content: t('Content.e4TeamResBuff.content'),
disabled: e < 4,
},
- {
- formItem: 'switch',
+ e6UltCDBoost: {
id: 'e6UltCDBoost',
+ formItem: 'switch',
text: t('Content.e6UltCDBoost.text'),
content: t('Content.e6UltCDBoost.content'),
disabled: e < 6,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'concertoActive'),
- findContentId(content, 'skillDmgBuff'),
- {
- formItem: 'slider',
+ const teammateContent: ContentDefinition = {
+ concertoActive: content.concertoActive,
+ skillDmgBuff: content.skillDmgBuff,
+ teammateATKValue: {
id: 'teammateATKValue',
+ formItem: 'slider',
text: t('TeammateContent.teammateATKValue.text'),
content: t('TeammateContent.teammateATKValue.content', {
ultAtkBuffFlatValue: TsUtils.precisionRound(100 * ultAtkBuffFlatValue),
@@ -82,87 +101,71 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
min: 0,
max: 7000,
},
- findContentId(content, 'talentCdBuff'),
- {
- formItem: 'switch',
+ talentCdBuff: content.talentCdBuff,
+ traceFuaCdBoost: {
id: 'traceFuaCdBoost',
+ formItem: 'switch',
text: t('TeammateContent.traceFuaCdBoost.text'),
content: t('TeammateContent.traceFuaCdBoost.content'),
},
- findContentId(content, 'e1UltResPen'),
- {
- formItem: 'switch',
+ e1UltResPen: content.e1UltResPen,
+ e2UltSpdBuff: {
id: 'e2UltSpdBuff',
+ formItem: 'switch',
text: t('TeammateContent.e2UltSpdBuff.text'),
content: t('TeammateContent.e2UltSpdBuff.content'),
disabled: e < 2,
},
- ]
-
- const defaults = {
- concertoActive: true,
- skillDmgBuff: true,
- talentCdBuff: true,
- e1UltResPen: true,
- e4TeamResBuff: false,
- e6UltCDBoost: true,
+ e4TeamResBuff: content.e4TeamResBuff,
}
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => (defaults),
- teammateDefaults: () => ({
- concertoActive: true,
- skillDmgBuff: true,
- talentCdBuff: true,
- teammateATKValue: 5000,
- traceFuaCdBoost: true,
- e1UltResPen: true,
- e2UltSpdBuff: false,
- e4TeamResBuff: true,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
-
- x.BASIC_SCALING += basicScaling
- x.ULT_ADDITIONAL_DMG_SCALING += (r.concertoActive) ? ultScaling : 0
-
- x.BASIC_TOUGHNESS_DMG += 30
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
+
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.ULT_ADDITIONAL_DMG_SCALING.buff((r.concertoActive) ? ultScaling : 0, Source.NONE)
+
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.CD] += (m.talentCdBuff) ? talentCdBuffValue : 0
- x[Stats.RES] += (e >= 4 && m.concertoActive && m.e4TeamResBuff) ? 0.50 : 0
+ x.CD.buff((m.talentCdBuff) ? talentCdBuffValue : 0, Source.NONE)
+ x.RES.buff((e >= 4 && m.concertoActive && m.e4TeamResBuff) ? 0.50 : 0, Source.NONE)
- x.ELEMENTAL_DMG += (m.skillDmgBuff) ? skillDmgBuffValue : 0
- x.RES_PEN += (e >= 1 && m.concertoActive && m.e1UltResPen) ? 0.24 : 0
+ x.ELEMENTAL_DMG.buff((m.skillDmgBuff) ? skillDmgBuffValue : 0, Source.NONE)
+ x.RES_PEN.buff((e >= 1 && m.concertoActive && m.e1UltResPen) ? 0.24 : 0, Source.NONE)
},
- precomputeTeammateEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const t = action.characterConditionals
+ precomputeTeammateEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const t: Conditionals = action.characterConditionals
+
+ x.ATK.buff((t.concertoActive) ? t.teammateATKValue * ultAtkBuffScalingValue + ultAtkBuffFlatValue : 0, Source.NONE)
+ x.RATIO_BASED_ATK_BUFF.buff((t.concertoActive) ? t.teammateATKValue * ultAtkBuffScalingValue : 0, Source.NONE)
- x[Stats.ATK] += (t.concertoActive) ? t.teammateATKValue * ultAtkBuffScalingValue + ultAtkBuffFlatValue : 0
- x.RATIO_BASED_ATK_BUFF += (t.concertoActive) ? t.teammateATKValue * ultAtkBuffScalingValue : 0
+ x.SPD_P.buff((e >= 2 && t.concertoActive && t.e2UltSpdBuff) ? 0.16 : 0, Source.NONE)
- x[Stats.SPD_P] += (e >= 2 && t.concertoActive && t.e2UltSpdBuff) ? 0.16 : 0
- buffAbilityCd(x, FUA_TYPE, 0.25, (t.traceFuaCdBoost && t.concertoActive))
+ buffAbilityCd(x, FUA_TYPE, t.traceFuaCdBoost && t.concertoActive ? 0.25 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- x[Stats.ATK] += (r.concertoActive) ? x[Stats.ATK] * ultAtkBuffScalingValue + ultAtkBuffFlatValue : 0
+ x.ATK.buff((r.concertoActive) ? x.a[Key.ATK] * ultAtkBuffScalingValue + ultAtkBuffFlatValue : 0, Source.NONE)
- x.ULT_ADDITIONAL_DMG_CR_OVERRIDE = 1.00
- x.ULT_ADDITIONAL_DMG_CD_OVERRIDE = (e >= 6 && r.concertoActive && r.e6UltCDBoost) ? 6.00 : 1.50
+ x.ULT_ADDITIONAL_DMG_CR_OVERRIDE.buff(1.00, Source.NONE)
+ x.ULT_ADDITIONAL_DMG_CD_OVERRIDE.buff((e >= 6 && r.concertoActive && r.e6UltCDBoost) ? 6.00 : 1.50, Source.NONE)
- x.BASIC_DMG += x.BASIC_SCALING * x[Stats.ATK]
- x.ULT_ADDITIONAL_DMG += x.ULT_ADDITIONAL_DMG_SCALING * x[Stats.ATK]
+ x.BASIC_DMG.buff(x.a[Key.BASIC_SCALING] * x.a[Key.ATK], Source.NONE)
+ x.ULT_ADDITIONAL_DMG.buff(x.a[Key.ULT_ADDITIONAL_DMG_SCALING] * x.a[Key.ATK], Source.NONE)
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
return `
if (${wgslTrue(r.concertoActive)}) {
buffDynamicATK(x.ATK * ${ultAtkBuffScalingValue} + ${ultAtkBuffFlatValue}, p_x, p_state);
diff --git a/src/lib/conditionals/character/RuanMei.ts b/src/lib/conditionals/character/RuanMei.ts
index b90a0e6b6..bdbc64bc7 100644
--- a/src/lib/conditionals/character/RuanMei.ts
+++ b/src/lib/conditionals/character/RuanMei.ts
@@ -1,18 +1,9 @@
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardAtkFinalizer,
- standardAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { RuanMeiConversionConditional } from 'lib/gpu/conditionals/dynamicConditionals'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -24,116 +15,120 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const skillScaling = skill(e, 0.32, 0.352)
const talentSpdScaling = talent(e, 0.10, 0.104)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ skillOvertoneBuff: true,
+ teamBEBuff: true,
+ ultFieldActive: true,
+ e2AtkBoost: false,
+ e4BeBuff: false,
+ }
+
+ const teammateDefaults = {
+ skillOvertoneBuff: true,
+ teamSpdBuff: true,
+ teamBEBuff: true,
+ ultFieldActive: true,
+ e2AtkBoost: false,
+ teamDmgBuff: 0.36,
+ }
+
+ const content: ContentDefinition = {
+ skillOvertoneBuff: {
id: 'skillOvertoneBuff',
+ formItem: 'switch',
text: t('Content.skillOvertoneBuff.text'),
content: t('Content.skillOvertoneBuff.content', { skillScaling: TsUtils.precisionRound(100 * skillScaling) }),
},
- {
- formItem: 'switch',
+ teamBEBuff: {
id: 'teamBEBuff',
+ formItem: 'switch',
text: t('Content.teamBEBuff.text'),
content: t('Content.teamBEBuff.content'),
},
- {
- formItem: 'switch',
+ ultFieldActive: {
id: 'ultFieldActive',
+ formItem: 'switch',
text: t('Content.ultFieldActive.text'),
content: t('Content.ultFieldActive.content', { fieldResPenValue: TsUtils.precisionRound(100 * fieldResPenValue) }),
},
- {
- formItem: 'switch',
+ e2AtkBoost: {
id: 'e2AtkBoost',
+ formItem: 'switch',
text: t('Content.e2AtkBoost.text'),
content: t('Content.e2AtkBoost.content'),
disabled: (e < 2),
},
- {
- formItem: 'switch',
+ e4BeBuff: {
id: 'e4BeBuff',
+ formItem: 'switch',
text: t('Content.e4BeBuff.text'),
content: t('Content.e4BeBuff.content'),
disabled: (e < 4),
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'skillOvertoneBuff'),
- {
- formItem: 'switch',
+ const teammateContent: ContentDefinition = {
+ skillOvertoneBuff: content.skillOvertoneBuff,
+ teamSpdBuff: {
id: 'teamSpdBuff',
+ formItem: 'switch',
text: t('TeammateContent.teamSpdBuff.text'),
content: t('TeammateContent.teamSpdBuff.content', { talentSpdScaling: TsUtils.precisionRound(100 * talentSpdScaling) }),
},
- findContentId(content, 'teamBEBuff'),
- {
- formItem: 'slider',
+ teamBEBuff: content.teamBEBuff,
+ teamDmgBuff: {
id: 'teamDmgBuff',
+ formItem: 'slider',
text: t('TeammateContent.teamDmgBuff.text'),
content: t('TeammateContent.teamDmgBuff.content'),
min: 0,
max: 0.36,
percent: true,
},
- findContentId(content, 'ultFieldActive'),
- findContentId(content, 'e2AtkBoost'),
- ]
+ ultFieldActive: content.ultFieldActive,
+ e2AtkBoost: content.e2AtkBoost,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- skillOvertoneBuff: true,
- teamBEBuff: true,
- ultFieldActive: true,
- e2AtkBoost: false,
- e4BeBuff: false,
- }),
- teammateDefaults: () => ({
- skillOvertoneBuff: true,
- teamSpdBuff: true,
- teamBEBuff: true,
- ultFieldActive: true,
- e2AtkBoost: false,
- teamDmgBuff: 0.36,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.ATK_P] += (e >= 2 && r.e2AtkBoost) ? 0.40 : 0
- x[Stats.BE] += (e >= 4 && r.e4BeBuff) ? 1.00 : 0
+ x.ATK_P.buff((e >= 2 && r.e2AtkBoost) ? 0.40 : 0, Source.NONE)
+ x.BE.buff((e >= 4 && r.e4BeBuff) ? 1.00 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.BE] += (m.teamBEBuff) ? 0.20 : 0
+ x.BE.buff((m.teamBEBuff) ? 0.20 : 0, Source.NONE)
- x.ELEMENTAL_DMG += (m.skillOvertoneBuff) ? skillScaling : 0
- x.BREAK_EFFICIENCY_BOOST += (m.skillOvertoneBuff) ? 0.50 : 0
+ x.ELEMENTAL_DMG.buff((m.skillOvertoneBuff) ? skillScaling : 0, Source.NONE)
+ x.BREAK_EFFICIENCY_BOOST.buff((m.skillOvertoneBuff) ? 0.50 : 0, Source.NONE)
- x.RES_PEN += (m.ultFieldActive) ? fieldResPenValue : 0
- x.DEF_PEN += (e >= 1 && m.ultFieldActive) ? 0.20 : 0
+ x.RES_PEN.buff((m.ultFieldActive) ? fieldResPenValue : 0, Source.NONE)
+ x.DEF_PEN.buff((e >= 1 && m.ultFieldActive) ? 0.20 : 0, Source.NONE)
},
- precomputeTeammateEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const t = action.characterConditionals
+ precomputeTeammateEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const t: Conditionals = action.characterConditionals
- x[Stats.SPD_P] += (t.teamSpdBuff) ? talentSpdScaling : 0
- x.ELEMENTAL_DMG += t.teamDmgBuff
+ x.SPD_P.buff((t.teamSpdBuff) ? talentSpdScaling : 0, Source.NONE)
+ x.ELEMENTAL_DMG.buff(t.teamDmgBuff, Source.NONE)
- x[Stats.ATK_P] += (e >= 2 && t.e2AtkBoost) ? 0.40 : 0
- x.RATIO_BASED_ATK_P_BUFF += (e >= 2 && t.e2AtkBoost) ? 0.40 : 0
+ x.ATK_P.buff((e >= 2 && t.e2AtkBoost) ? 0.40 : 0, Source.NONE)
+ x.RATIO_BASED_ATK_P_BUFF.buff((e >= 2 && t.e2AtkBoost) ? 0.40 : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
dynamicConditionals: [RuanMeiConversionConditional],
}
diff --git a/src/lib/conditionals/character/Sampo.ts b/src/lib/conditionals/character/Sampo.ts
index e453cd029..ea191561d 100644
--- a/src/lib/conditionals/character/Sampo.ts
+++ b/src/lib/conditionals/character/Sampo.ts
@@ -1,16 +1,11 @@
-import { ComputedStatsObject, DOT_TYPE } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardAtkFinalizer,
- standardAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
+import { DOT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityVulnerability } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -25,75 +20,78 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const dotScaling = talent(e, 0.52, 0.572)
const maxExtraHits = e < 1 ? 4 : 5
+ const defaults = {
+ targetDotTakenDebuff: true,
+ skillExtraHits: maxExtraHits,
+ targetWindShear: true,
+ }
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const teammateDefaults = {
+ targetDotTakenDebuff: true,
+ }
+
+ const content: ContentDefinition = {
+ targetDotTakenDebuff: {
id: 'targetDotTakenDebuff',
+ formItem: 'switch',
text: t('Content.targetDotTakenDebuff.text'),
content: t('Content.targetDotTakenDebuff.content', { dotVulnerabilityValue: TsUtils.precisionRound(100 * dotVulnerabilityValue) }),
},
- {
- formItem: 'slider',
+ skillExtraHits: {
id: 'skillExtraHits',
+ formItem: 'slider',
text: t('Content.skillExtraHits.text'),
content: t('Content.skillExtraHits.content'),
min: 1,
max: maxExtraHits,
},
- {
- formItem: 'switch',
+ targetWindShear: {
id: 'targetWindShear',
+ formItem: 'switch',
text: t('Content.targetWindShear.text'),
content: t('Content.targetWindShear.content'),
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'targetDotTakenDebuff'),
- ]
+ const teammateContent: ContentDefinition = {
+ targetDotTakenDebuff: content.targetDotTakenDebuff,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- targetDotTakenDebuff: true,
- skillExtraHits: maxExtraHits,
- targetWindShear: true,
- }),
- teammateDefaults: () => ({
- targetDotTakenDebuff: true,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.SKILL_SCALING += (r.skillExtraHits) * skillScaling
- x.ULT_SCALING += ultScaling
- x.DOT_SCALING += dotScaling
- x.DOT_SCALING += (e >= 6) ? 0.15 : 0
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.SKILL_SCALING.buff((r.skillExtraHits) * skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.DOT_SCALING.buff(dotScaling, Source.NONE)
+ x.DOT_SCALING.buff((e >= 6) ? 0.15 : 0, Source.NONE)
// Boost
- x.DMG_RED_MULTI *= (r.targetWindShear) ? (1 - 0.15) : 1
+ x.DMG_RED_MULTI.multiply((r.targetWindShear) ? (1 - 0.15) : 1, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 30 + 15 * r.skillExtraHits
- x.ULT_TOUGHNESS_DMG += 60
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(30 + 15 * r.skillExtraHits, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
- x.DOT_CHANCE = 0.65
+ x.DOT_CHANCE.set(0.65, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- buffAbilityVulnerability(x, DOT_TYPE, dotVulnerabilityValue, (m.targetDotTakenDebuff))
+ buffAbilityVulnerability(x, DOT_TYPE, (m.targetDotTakenDebuff) ? dotVulnerabilityValue : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/Seele.ts b/src/lib/conditionals/character/Seele.ts
index 543f054cc..80ec723f6 100644
--- a/src/lib/conditionals/character/Seele.ts
+++ b/src/lib/conditionals/character/Seele.ts
@@ -1,12 +1,10 @@
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { AbilityEidolon, Conditionals, ContentDefinition } from 'lib/conditionals/conditionalUtils'
import { wgslTrue } from 'lib/gpu/injection/wgslUtils'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -20,85 +18,85 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const skillScaling = skill(e, 2.20, 2.42)
const ultScaling = ult(e, 4.25, 4.59)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ buffedState: true,
+ speedBoostStacks: speedBoostStacksMax,
+ e1EnemyHp80CrBoost: false,
+ e6UltTargetDebuff: true,
+ }
+
+ const content: ContentDefinition = {
+ buffedState: {
id: 'buffedState',
+ formItem: 'switch',
text: t('Content.buffedState.text'),
content: t('Content.buffedState.content', { buffedStateDmgBuff: TsUtils.precisionRound(100 * buffedStateDmgBuff) }),
},
- {
- formItem: 'slider',
+ speedBoostStacks: {
id: 'speedBoostStacks',
+ formItem: 'slider',
text: t('Content.speedBoostStacks.text'),
content: t('Content.speedBoostStacks.content', { speedBoostStacksMax: speedBoostStacksMax }),
min: 0,
max: speedBoostStacksMax,
},
- {
- formItem: 'switch',
+ e1EnemyHp80CrBoost: {
id: 'e1EnemyHp80CrBoost',
+ formItem: 'switch',
text: t('Content.e1EnemyHp80CrBoost.text'),
content: t('Content.e1EnemyHp80CrBoost.content'),
disabled: e < 1,
},
- {
- formItem: 'switch',
+ e6UltTargetDebuff: {
id: 'e6UltTargetDebuff',
+ formItem: 'switch',
text: t('Content.e6UltTargetDebuff.text'),
content: t('Content.e6UltTargetDebuff.content'),
disabled: e < 6,
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => ({
- buffedState: true,
- speedBoostStacks: speedBoostStacksMax,
- e1EnemyHp80CrBoost: false,
- e6UltTargetDebuff: true,
- }),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.CR] += (e >= 1 && r.e1EnemyHp80CrBoost) ? 0.15 : 0
- x[Stats.SPD_P] += 0.25 * r.speedBoostStacks
+ x.CR.buff((e >= 1 && r.e1EnemyHp80CrBoost) ? 0.15 : 0, Source.NONE)
+ x.SPD_P.buff(0.25 * r.speedBoostStacks, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
// Boost
- x.ELEMENTAL_DMG += (r.buffedState) ? buffedStateDmgBuff : 0
- x.RES_PEN += (r.buffedState) ? 0.20 : 0
+ x.ELEMENTAL_DMG.buff((r.buffedState) ? buffedStateDmgBuff : 0, Source.NONE)
+ x.RES_PEN.buff((r.buffedState) ? 0.20 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 90
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(90, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
// TODO: Seele's E6 should have a teammate effect but its kinda hard to calc
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- x.BASIC_DMG += x.BASIC_SCALING * x[Stats.ATK]
- x.SKILL_DMG += x.SKILL_SCALING * x[Stats.ATK]
- x.ULT_DMG += x.ULT_SCALING * x[Stats.ATK]
+ x.BASIC_DMG.buff(x.a[Key.BASIC_SCALING] * x.a[Key.ATK], Source.NONE)
+ x.SKILL_DMG.buff(x.a[Key.SKILL_SCALING] * x.a[Key.ATK], Source.NONE)
+ x.ULT_DMG.buff(x.a[Key.ULT_SCALING] * x.a[Key.ATK], Source.NONE)
- x.BASIC_DMG += (e >= 6 && r.e6UltTargetDebuff) ? 0.15 * x.ULT_DMG : 0
- x.SKILL_DMG += (e >= 6 && r.e6UltTargetDebuff) ? 0.15 * x.ULT_DMG : 0
- x.ULT_DMG += (e >= 6 && r.e6UltTargetDebuff) ? 0.15 * x.ULT_DMG : 0
+ x.BASIC_DMG.buff((e >= 6 && r.e6UltTargetDebuff) ? 0.15 * x.a[Key.ULT_DMG] : 0, Source.NONE)
+ x.SKILL_DMG.buff((e >= 6 && r.e6UltTargetDebuff) ? 0.15 * x.a[Key.ULT_DMG] : 0, Source.NONE)
+ x.ULT_DMG.buff((e >= 6 && r.e6UltTargetDebuff) ? 0.15 * x.a[Key.ULT_DMG] : 0, Source.NONE)
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
return `
x.BASIC_DMG += x.BASIC_SCALING * x.ATK;
x.SKILL_DMG += x.SKILL_SCALING * x.ATK;
diff --git a/src/lib/conditionals/character/Serval.ts b/src/lib/conditionals/character/Serval.ts
index e1f0b82f2..a4b903e7d 100644
--- a/src/lib/conditionals/character/Serval.ts
+++ b/src/lib/conditionals/character/Serval.ts
@@ -1,11 +1,9 @@
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -19,57 +17,57 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const ultScaling = ult(e, 1.80, 1.944)
const dotScaling = skill(e, 1.04, 1.144)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ targetShocked: true,
+ enemyDefeatedBuff: true,
+ }
+
+ const content: ContentDefinition = {
+ targetShocked: {
id: 'targetShocked',
+ formItem: 'switch',
text: t('Content.targetShocked.text'),
content: t('Content.targetShocked.content', { talentExtraDmgScaling: TsUtils.precisionRound(100 * talentExtraDmgScaling) }),
},
- {
- formItem: 'switch',
+ enemyDefeatedBuff: {
id: 'enemyDefeatedBuff',
+ formItem: 'switch',
text: t('Content.enemyDefeatedBuff.text'),
content: t('Content.enemyDefeatedBuff.content'),
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => ({
- targetShocked: true,
- enemyDefeatedBuff: true,
- }),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.ATK_P] += (r.enemyDefeatedBuff) ? 0.20 : 0
+ x.ATK_P.buff((r.enemyDefeatedBuff) ? 0.20 : 0, Source.NONE)
// Scaling;
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
- x.DOT_SCALING += dotScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.DOT_SCALING.buff(dotScaling, Source.NONE)
- x.BASIC_SCALING += (r.targetShocked) ? talentExtraDmgScaling : 0
- x.SKILL_SCALING += (r.targetShocked) ? talentExtraDmgScaling : 0
- x.ULT_SCALING += (r.targetShocked) ? talentExtraDmgScaling : 0
+ x.BASIC_SCALING.buff((r.targetShocked) ? talentExtraDmgScaling : 0, Source.NONE)
+ x.SKILL_SCALING.buff((r.targetShocked) ? talentExtraDmgScaling : 0, Source.NONE)
+ x.ULT_SCALING.buff((r.targetShocked) ? talentExtraDmgScaling : 0, Source.NONE)
// Boost
- x.ELEMENTAL_DMG += (e >= 6 && r.targetShocked) ? 0.30 : 0
+ x.ELEMENTAL_DMG.buff((e >= 6 && r.targetShocked) ? 0.30 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 60
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
- x.DOT_CHANCE = 0.65
+ x.DOT_CHANCE.set(0.65, Source.NONE)
return x
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/SilverWolf.ts b/src/lib/conditionals/character/SilverWolf.ts
index b07219066..e3c1c0f78 100644
--- a/src/lib/conditionals/character/SilverWolf.ts
+++ b/src/lib/conditionals/character/SilverWolf.ts
@@ -1,15 +1,9 @@
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardAtkFinalizer,
- standardAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -24,96 +18,100 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const skillScaling = skill(e, 1.96, 2.156)
const ultScaling = ult(e, 3.80, 4.104)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ skillWeaknessResShredDebuff: false,
+ skillResShredDebuff: true,
+ talentDefShredDebuff: true,
+ ultDefShredDebuff: true,
+ targetDebuffs: 5,
+ }
+
+ const teammateDefaults = {
+ skillWeaknessResShredDebuff: false,
+ skillResShredDebuff: true,
+ talentDefShredDebuff: true,
+ ultDefShredDebuff: true,
+ targetDebuffs: 5,
+ }
+
+ const content: ContentDefinition = {
+ skillResShredDebuff: {
id: 'skillResShredDebuff',
+ formItem: 'switch',
text: t('Content.skillResShredDebuff.text'),
content: t('Content.skillResShredDebuff.content', { skillResShredValue: TsUtils.precisionRound(100 * skillResShredValue) }),
},
- {
- formItem: 'switch',
+ skillWeaknessResShredDebuff: {
id: 'skillWeaknessResShredDebuff',
+ formItem: 'switch',
text: t('Content.skillWeaknessResShredDebuff.text'),
content: t('Content.skillWeaknessResShredDebuff.content'),
},
- {
- formItem: 'switch',
+ talentDefShredDebuff: {
id: 'talentDefShredDebuff',
+ formItem: 'switch',
text: t('Content.talentDefShredDebuff.text'),
content: t('Content.talentDefShredDebuff.content', { talentDefShredDebuffValue: TsUtils.precisionRound(100 * talentDefShredDebuffValue) }),
},
- {
- formItem: 'switch',
+ ultDefShredDebuff: {
id: 'ultDefShredDebuff',
+ formItem: 'switch',
text: t('Content.ultDefShredDebuff.text'),
content: t('Content.ultDefShredDebuff.content', { ultDefShredValue: TsUtils.precisionRound(100 * ultDefShredValue) }),
},
- {
- formItem: 'slider',
+ targetDebuffs: {
id: 'targetDebuffs',
+ formItem: 'slider',
text: t('Content.targetDebuffs.text'),
content: t('Content.targetDebuffs.content'),
min: 0,
max: 5,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'skillResShredDebuff'),
- findContentId(content, 'skillWeaknessResShredDebuff'),
- findContentId(content, 'talentDefShredDebuff'),
- findContentId(content, 'ultDefShredDebuff'),
- findContentId(content, 'targetDebuffs'),
- ]
+ const teammateContent: ContentDefinition = {
+ skillResShredDebuff: content.skillResShredDebuff,
+ skillWeaknessResShredDebuff: content.skillWeaknessResShredDebuff,
+ talentDefShredDebuff: content.talentDefShredDebuff,
+ ultDefShredDebuff: content.ultDefShredDebuff,
+ targetDebuffs: content.targetDebuffs,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- skillWeaknessResShredDebuff: false,
- skillResShredDebuff: true,
- talentDefShredDebuff: true,
- ultDefShredDebuff: true,
- targetDebuffs: 5,
- }),
- teammateDefaults: () => ({
- skillWeaknessResShredDebuff: false,
- skillResShredDebuff: true,
- talentDefShredDebuff: true,
- ultDefShredDebuff: true,
- targetDebuffs: 5,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
- x.ULT_SCALING += (e >= 4) ? r.targetDebuffs * 0.20 : 0
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
+ x.ULT_SCALING.buff((e >= 4) ? r.targetDebuffs * 0.20 : 0, Source.NONE)
// Boost
- x.ELEMENTAL_DMG += (e >= 6) ? r.targetDebuffs * 0.20 : 0
+ x.ELEMENTAL_DMG.buff((e >= 6) ? r.targetDebuffs * 0.20 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 90
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(90, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x.RES_PEN += (m.skillWeaknessResShredDebuff) ? 0.20 : 0
- x.RES_PEN += (m.skillResShredDebuff) ? skillResShredValue : 0
- x.RES_PEN += (m.skillResShredDebuff && m.targetDebuffs >= 3) ? 0.03 : 0
- x.DEF_PEN += (m.ultDefShredDebuff) ? ultDefShredValue : 0
- x.DEF_PEN += (m.talentDefShredDebuff) ? talentDefShredDebuffValue : 0
+ x.RES_PEN.buff((m.skillWeaknessResShredDebuff) ? 0.20 : 0, Source.NONE)
+ x.RES_PEN.buff((m.skillResShredDebuff) ? skillResShredValue : 0, Source.NONE)
+ x.RES_PEN.buff((m.skillResShredDebuff && m.targetDebuffs >= 3) ? 0.03 : 0, Source.NONE)
+ x.DEF_PEN.buff((m.ultDefShredDebuff) ? ultDefShredValue : 0, Source.NONE)
+ x.DEF_PEN.buff((m.talentDefShredDebuff) ? talentDefShredDebuffValue : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/Sparkle.ts b/src/lib/conditionals/character/Sparkle.ts
index f28f79d17..bbc674545 100644
--- a/src/lib/conditionals/character/Sparkle.ts
+++ b/src/lib/conditionals/character/Sparkle.ts
@@ -1,18 +1,12 @@
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardAtkFinalizer,
- standardAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { ConditionalActivation, ConditionalType, Stats } from 'lib/constants'
-import { buffStat, conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
+import { conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
import { wgslFalse } from 'lib/gpu/injection/wgslUtils'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -28,52 +22,67 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const skillScaling = skill(e, 0, 0)
const ultScaling = ult(e, 0, 0)
- const atkBoostByQuantumAllies = {
+ const atkBoostByQuantumAllies: Record = {
0: 0,
1: 0.05,
2: 0.15,
3: 0.30,
}
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ skillCdBuff: false,
+ cipherBuff: true,
+ talentStacks: 3,
+ quantumAllies: 3,
+ }
+
+ const teammateDefaults = {
+ ...defaults,
+ ...{
+ skillCdBuff: true,
+ teammateCDValue: 2.5,
+ },
+ }
+
+ const content: ContentDefinition = {
+ skillCdBuff: {
id: 'skillCdBuff',
+ formItem: 'switch',
text: t('Content.skillCdBuff.text'),
content: t('Content.skillCdBuff.content', {
skillCdBuffScaling: TsUtils.precisionRound(100 * skillCdBuffScaling),
skillCdBuffBase: TsUtils.precisionRound(100 * skillCdBuffBase),
}),
},
- {
- formItem: 'switch',
+ cipherBuff: {
id: 'cipherBuff',
+ formItem: 'switch',
text: t('Content.cipherBuff.text'),
content: t('Content.cipherBuff.content', { cipherTalentStackBoost: TsUtils.precisionRound(100 * cipherTalentStackBoost) }),
},
- {
- formItem: 'slider',
+ talentStacks: {
id: 'talentStacks',
+ formItem: 'slider',
text: t('Content.talentStacks.text'),
content: t('Content.talentStacks.content', { talentBaseStackBoost: TsUtils.precisionRound(100 * talentBaseStackBoost) }),
min: 0,
max: 3,
},
- {
- formItem: 'slider',
+ quantumAllies: {
id: 'quantumAllies',
+ formItem: 'slider',
text: t('Content.quantumAllies.text'),
content: t('Content.quantumAllies.content'),
min: 0,
max: 3,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'skillCdBuff'),
- {
- formItem: 'slider',
+ const teammateContent: ContentDefinition = {
+ skillCdBuff: content.skillCdBuff,
+ teammateCDValue: {
id: 'teammateCDValue',
+ formItem: 'slider',
text: t('TeammateContent.teammateCDValue.text'),
content: t('TeammateContent.teammateCDValue.content', {
skillCdBuffScaling: TsUtils.precisionRound(100 * skillCdBuffScaling),
@@ -83,60 +92,53 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
max: 3.50,
percent: true,
},
- findContentId(content, 'cipherBuff'),
- findContentId(content, 'talentStacks'),
- findContentId(content, 'quantumAllies'),
- ]
-
- const defaults = {
- skillCdBuff: false,
- cipherBuff: true,
- talentStacks: 3,
- quantumAllies: 3,
+ cipherBuff: content.cipherBuff,
+ talentStacks: content.talentStacks,
+ quantumAllies: content.quantumAllies,
}
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => (defaults),
- teammateDefaults: () => ({
- ...defaults,
- ...{
- skillCdBuff: true,
- teammateCDValue: 2.5,
- },
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
// Main damage type
- x[Stats.ATK_P] += 0.15 + (context.elementalDamageType == Stats.Quantum_DMG
- ? (atkBoostByQuantumAllies[m.quantumAllies] || 0)
- : 0)
- x[Stats.ATK_P] += (e >= 1 && m.cipherBuff) ? 0.40 : 0
-
- x.ELEMENTAL_DMG += (m.cipherBuff)
- ? m.talentStacks * (talentBaseStackBoost + cipherTalentStackBoost)
- : m.talentStacks * talentBaseStackBoost
- x.DEF_PEN += (e >= 2) ? 0.08 * m.talentStacks : 0
+ x.ATK_P.buff(
+ 0.15 + (context.elementalDamageType == Stats.Quantum_DMG
+ ? (atkBoostByQuantumAllies[m.quantumAllies] || 0)
+ : 0),
+ Source.NONE)
+ x.ATK_P.buff((e >= 1 && m.cipherBuff) ? 0.40 : 0, Source.NONE)
+
+ x.ELEMENTAL_DMG.buff(
+ (m.cipherBuff)
+ ? m.talentStacks * (talentBaseStackBoost + cipherTalentStackBoost)
+ : m.talentStacks * talentBaseStackBoost,
+ Source.NONE)
+ x.DEF_PEN.buff((e >= 2) ? 0.08 * m.talentStacks : 0, Source.NONE)
},
- precomputeTeammateEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const t = action.characterConditionals
-
- x[Stats.CD] += (t.skillCdBuff)
- ? skillCdBuffBase + (skillCdBuffScaling + (e >= 6 ? 0.30 : 0)) * t.teammateCDValue
- : 0
+ precomputeTeammateEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const t: Conditionals = action.characterConditionals
+
+ x.CD.buff(
+ (t.skillCdBuff)
+ ? skillCdBuffBase + (skillCdBuffScaling + (e >= 6 ? 0.30 : 0)) * t.teammateCDValue
+ : 0,
+ Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
dynamicConditionals: [
{
@@ -148,8 +150,8 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
condition: function () {
return true
},
- effect: function (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ effect: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
+ const r: Conditionals = action.characterConditionals
if (!r.skillCdBuff) {
return
}
@@ -157,20 +159,20 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const buffScalingValue = (skillCdBuffScaling + (e >= 6 ? 0.30 : 0))
const stateValue = action.conditionalState[this.id] || 0
- const convertibleCdValue = x[Stats.CD] - x.RATIO_BASED_CD_BUFF
+ const convertibleCdValue = x.a[Key.CD] - x.a[Key.RATIO_BASED_CD_BUFF]
const buffCD = buffScalingValue * convertibleCdValue + skillCdBuffBase
const stateBuffCD = buffScalingValue * stateValue + skillCdBuffBase
- action.conditionalState[this.id] = x[Stats.CD]
+ action.conditionalState[this.id] = x.a[Key.CD]
const finalBuffCd = buffCD - (stateValue ? stateBuffCD : 0)
- x.RATIO_BASED_CD_BUFF += finalBuffCd
+ x.RATIO_BASED_CD_BUFF.buff(finalBuffCd, Source.NONE)
- buffStat(x, Stats.CD, finalBuffCd, action, context)
+ x.CD.buffDynamic(finalBuffCd, Source.NONE, action, context)
},
gpu: function (action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
const buffScalingValue = (skillCdBuffScaling + (e >= 6 ? 0.30 : 0))
return conditionalWgslWrapper(this, `
diff --git a/src/lib/conditionals/character/Sunday.ts b/src/lib/conditionals/character/Sunday.ts
index a539be7b2..3406b80d2 100644
--- a/src/lib/conditionals/character/Sunday.ts
+++ b/src/lib/conditionals/character/Sunday.ts
@@ -1,13 +1,12 @@
import i18next from 'i18next'
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, findContentId, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { ConditionalActivation, ConditionalType, CURRENT_DATA_VERSION, Stats } from 'lib/constants'
-import { buffStat, conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
+import { conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
import { wgslFalse } from 'lib/gpu/injection/wgslUtils'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -21,14 +20,33 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const basicScaling = basic(e, 1.00, 1.10)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ skillDmgBuff: false,
+ talentCrBuffStacks: 0,
+ techniqueDmgBuff: false,
+ e1DefPen: false,
+ e2DmgBuff: false,
+ }
+
+ const teammateDefaults = {
+ skillDmgBuff: true,
+ talentCrBuffStacks: e < 6 ? 1 : 3,
+ beatified: true,
+ teammateCDValue: 2.50,
+ techniqueDmgBuff: false,
+ e1DefPen: true,
+ e2DmgBuff: true,
+ e6CrToCdConversion: true,
+ }
+
+ const content: ContentDefinition = {
+ skillDmgBuff: {
id: 'skillDmgBuff',
+ formItem: 'switch',
text: 'Skill DMG buff',
content: i18next.t('BetaMessage', { ns: 'conditionals', Version: CURRENT_DATA_VERSION }),
},
- {
+ talentCrBuffStacks: {
id: 'talentCrBuffStacks',
formItem: 'slider',
text: 'Talent CR buff stacks',
@@ -36,109 +54,88 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
min: 0,
max: e < 6 ? 1 : 3,
},
- {
- formItem: 'switch',
+ techniqueDmgBuff: {
id: 'techniqueDmgBuff',
+ formItem: 'switch',
text: 'Technique DMG buff',
content: i18next.t('BetaMessage', { ns: 'conditionals', Version: CURRENT_DATA_VERSION }),
},
- {
- formItem: 'switch',
+ e1DefPen: {
id: 'e1DefPen',
+ formItem: 'switch',
text: 'E1 DEF PEN',
content: i18next.t('BetaMessage', { ns: 'conditionals', Version: CURRENT_DATA_VERSION }),
disabled: e < 1,
},
- {
- formItem: 'switch',
+ e2DmgBuff: {
id: 'e2DmgBuff',
+ formItem: 'switch',
text: 'E2 Beatified DMG buff',
content: i18next.t('BetaMessage', { ns: 'conditionals', Version: CURRENT_DATA_VERSION }),
disabled: e < 2,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'skillDmgBuff'),
- findContentId(content, 'talentCrBuffStacks'),
- {
- formItem: 'switch',
+ const teammateContent: ContentDefinition = {
+ skillDmgBuff: content.skillDmgBuff,
+ talentCrBuffStacks: content.talentCrBuffStacks,
+ beatified: {
id: 'beatified',
+ formItem: 'switch',
text: 'Ult CD buff',
content: i18next.t('BetaMessage', { ns: 'conditionals', Version: CURRENT_DATA_VERSION }),
},
- {
- formItem: 'slider',
+ teammateCDValue: {
id: 'teammateCDValue',
+ formItem: 'slider',
text: 'Sunday Combat CD',
content: i18next.t('BetaMessage', { ns: 'conditionals', Version: CURRENT_DATA_VERSION }),
min: 0,
max: 3.00,
percent: true,
},
- findContentId(content, 'techniqueDmgBuff'),
- findContentId(content, 'e1DefPen'),
- findContentId(content, 'e2DmgBuff'),
- {
- formItem: 'switch',
+ techniqueDmgBuff: content.techniqueDmgBuff,
+ e1DefPen: content.e1DefPen,
+ e2DmgBuff: content.e2DmgBuff,
+ e6CrToCdConversion: {
id: 'e6CrToCdConversion',
+ formItem: 'switch',
text: 'E6 CR to CD conversion',
content: i18next.t('BetaMessage', { ns: 'conditionals', Version: CURRENT_DATA_VERSION }),
disabled: e < 6,
},
- ]
-
- const defaults = {
- skillDmgBuff: false,
- talentCrBuffStacks: 0,
- techniqueDmgBuff: false,
- e1DefPen: false,
- e2DmgBuff: false,
- }
-
- const teammateDefaults = {
- skillDmgBuff: true,
- talentCrBuffStacks: e < 6 ? 1 : 3,
- beatified: true,
- teammateCDValue: 2.50,
- techniqueDmgBuff: false,
- e1DefPen: true,
- e2DmgBuff: true,
- e6CrToCdConversion: true,
}
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => (defaults),
- teammateDefaults: () => (teammateDefaults),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- // Stats
-
- x.BASIC_SCALING += basicScaling
-
- x.BASIC_TOUGHNESS_DMG = 30
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+
+ x.BASIC_TOUGHNESS_DMG.set(30, Source.NONE)
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.CR] += m.talentCrBuffStacks * talentCrBuffValue
- x.ELEMENTAL_DMG += (m.skillDmgBuff) ? skillDmgBoostValue : 0
- x.ELEMENTAL_DMG += (m.skillDmgBuff && x.SUMMONS > 0) ? skillDmgBoostValue : 0
- x.ELEMENTAL_DMG += (m.techniqueDmgBuff) ? 0.50 : 0
+ x.CR.buff(m.talentCrBuffStacks * talentCrBuffValue, Source.NONE)
+ x.ELEMENTAL_DMG.buff((m.skillDmgBuff) ? skillDmgBoostValue : 0, Source.NONE)
+ x.ELEMENTAL_DMG.buff((m.skillDmgBuff && x.a[Key.SUMMONS] > 0) ? skillDmgBoostValue : 0, Source.NONE)
+ x.ELEMENTAL_DMG.buff((m.techniqueDmgBuff) ? 0.50 : 0, Source.NONE)
- x.DEF_PEN += (e >= 1 && m.e1DefPen && m.skillDmgBuff) ? 0.20 : 0
- x.DEF_PEN += (e >= 1 && m.e1DefPen && m.skillDmgBuff && x.SUMMONS > 0) ? 0.20 : 0
+ x.DEF_PEN.buff((e >= 1 && m.e1DefPen && m.skillDmgBuff) ? 0.20 : 0, Source.NONE)
+ x.DEF_PEN.buff((e >= 1 && m.e1DefPen && m.skillDmgBuff && x.a[Key.SUMMONS] > 0) ? 0.20 : 0, Source.NONE)
- x.ELEMENTAL_DMG += (e >= 2 && m.e2DmgBuff) ? 0.30 : 0
+ x.ELEMENTAL_DMG.buff((e >= 2 && m.e2DmgBuff) ? 0.30 : 0, Source.NONE)
},
- precomputeTeammateEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const t = action.characterConditionals
+ precomputeTeammateEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const t: Conditionals = action.characterConditionals
- x[Stats.CD] += (t.beatified) ? ultCdBoostValue * t.teammateCDValue : 0
- x[Stats.CD] += (t.beatified) ? ultCdBoostBaseValue : 0
+ x.CD.buff((t.beatified) ? ultCdBoostValue * t.teammateCDValue : 0, Source.NONE)
+ x.CD.buff((t.beatified) ? ultCdBoostBaseValue : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
standardAtkFinalizer(x)
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
@@ -151,23 +148,23 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
activation: ConditionalActivation.CONTINUOUS,
dependsOn: [Stats.CR],
ratioConversion: true,
- condition: function (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) {
- return x[Stats.CR] > 1.00
+ condition: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
+ return x.a[Key.CR] > 1.00
},
- effect: function (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ effect: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
+ const r: Conditionals = action.characterConditionals
if (!(e >= 6 && r.e6CrToCdConversion)) {
return
}
const stateValue = action.conditionalState[this.id] || 0
- const buffValue = Math.floor((x[Stats.CR] - 1.00) / 0.01) * 2.00 * 0.01
+ const buffValue = Math.floor((x.a[Key.CR] - 1.00) / 0.01) * 2.00 * 0.01
action.conditionalState[this.id] = buffValue
- buffStat(x, Stats.CD, buffValue - stateValue, action, context)
+ x.CD.buffDynamic(buffValue - stateValue, Source.NONE, action, context)
},
gpu: function (action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
return conditionalWgslWrapper(this, `
if (${wgslFalse(e >= 6 && r.e6CrToCdConversion)}) {
diff --git a/src/lib/conditionals/character/Sushang.ts b/src/lib/conditionals/character/Sushang.ts
index e535e4f0c..622cf4ee3 100644
--- a/src/lib/conditionals/character/Sushang.ts
+++ b/src/lib/conditionals/character/Sushang.ts
@@ -1,12 +1,11 @@
-import { ComputedStatsObject, SKILL_TYPE } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { SKILL_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityDmg } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -22,64 +21,64 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const skillExtraHitScaling = skill(e, 1.00, 1.10)
const ultScaling = ult(e, 3.20, 3.456)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ ultBuffedState: true,
+ e2DmgReductionBuff: true,
+ skillExtraHits: 3,
+ skillTriggerStacks: 10,
+ talentSpdBuffStacks: talentSpdBuffStacksMax,
+ }
+
+ const content: ContentDefinition = {
+ ultBuffedState: {
id: 'ultBuffedState',
+ formItem: 'switch',
text: t('Content.ultBuffedState.text'),
content: t('Content.ultBuffedState.content', { ultBuffedAtk: TsUtils.precisionRound(100 * ultBuffedAtk) }),
},
- {
- formItem: 'slider',
+ skillExtraHits: {
id: 'skillExtraHits',
+ formItem: 'slider',
text: t('Content.skillExtraHits.text'),
content: t('Content.skillExtraHits.content'),
min: 0,
max: 3,
},
- {
- formItem: 'slider',
+ skillTriggerStacks: {
id: 'skillTriggerStacks',
+ formItem: 'slider',
text: t('Content.skillTriggerStacks.text'),
content: t('Content.skillTriggerStacks.content'),
min: 0,
max: 10,
},
- {
- formItem: 'slider',
+ talentSpdBuffStacks: {
id: 'talentSpdBuffStacks',
+ formItem: 'slider',
text: t('Content.talentSpdBuffStacks.text'),
content: t('Content.talentSpdBuffStacks.content', { talentSpdBuffValue: TsUtils.precisionRound(100 * talentSpdBuffValue) }),
min: 0,
max: talentSpdBuffStacksMax,
},
- {
- formItem: 'switch',
+ e2DmgReductionBuff: {
id: 'e2DmgReductionBuff',
+ formItem: 'switch',
text: t('Content.e2DmgReductionBuff.text'),
content: t('Content.e2DmgReductionBuff.content'),
disabled: e < 2,
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => ({
- ultBuffedState: true,
- e2DmgReductionBuff: true,
- skillExtraHits: 3,
- skillTriggerStacks: 10,
- talentSpdBuffStacks: talentSpdBuffStacksMax,
- }),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.BE] += (e >= 4) ? 0.40 : 0
- x[Stats.ATK_P] += (r.ultBuffedState) ? ultBuffedAtk : 0
- x[Stats.SPD_P] += (r.talentSpdBuffStacks) * talentSpdBuffValue
+ x.BE.buff((e >= 4) ? 0.40 : 0, Source.NONE)
+ x.ATK_P.buff((r.ultBuffedState) ? ultBuffedAtk : 0, Source.NONE)
+ x.SPD_P.buff((r.talentSpdBuffStacks) * talentSpdBuffValue, Source.NONE)
/*
* Scaling
@@ -92,24 +91,24 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
stanceSkillScaling += (r.ultBuffedState && r.skillExtraHits >= 3) ? skillExtraHitScaling * 0.5 : 0
const stanceScalingProportion = stanceSkillScaling / (stanceSkillScaling + originalSkillScaling)
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += originalSkillScaling
- x.SKILL_SCALING += stanceSkillScaling
- x.ULT_SCALING += ultScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(originalSkillScaling, Source.NONE)
+ x.SKILL_SCALING.buff(stanceSkillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
// Boost
- buffAbilityDmg(x, SKILL_TYPE, r.skillTriggerStacks * 0.025 * stanceScalingProportion)
- x.DMG_RED_MULTI *= (e >= 2 && r.e2DmgReductionBuff) ? (1 - 0.20) : 1
+ buffAbilityDmg(x, SKILL_TYPE, r.skillTriggerStacks * 0.025 * stanceScalingProportion, Source.NONE)
+ x.DMG_RED_MULTI.multiply((e >= 2 && r.e2DmgReductionBuff) ? (1 - 0.20) : 1, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += 90
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(90, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/Tingyun.ts b/src/lib/conditionals/character/Tingyun.ts
index 228783cb7..b3dba5505 100644
--- a/src/lib/conditionals/character/Tingyun.ts
+++ b/src/lib/conditionals/character/Tingyun.ts
@@ -1,13 +1,13 @@
-import { BASIC_TYPE, ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, findContentId } from 'lib/conditionals/conditionalUtils'
+import { BASIC_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition } from 'lib/conditionals/conditionalUtils'
import { ConditionalActivation, ConditionalType, Stats } from 'lib/constants'
-import { buffStat, conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
+import { conditionalWgslWrapper } from 'lib/gpu/conditionals/dynamicConditionals'
import { wgslFalse, wgslTrue } from 'lib/gpu/injection/wgslUtils'
import { buffAbilityDmg } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -24,10 +24,24 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const skillScaling = skill(e, 0, 0)
const ultScaling = ult(e, 0, 0)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ benedictionBuff: false,
+ skillSpdBuff: false,
+ ultSpdBuff: false,
+ ultDmgBuff: false,
+ }
+
+ const teammateDefaults = {
+ benedictionBuff: true,
+ ultSpdBuff: false,
+ ultDmgBuff: true,
+ teammateAtkBuffValue: skillAtkBoostScaling,
+ }
+
+ const content: ContentDefinition = {
+ benedictionBuff: {
id: 'benedictionBuff',
+ formItem: 'switch',
text: t('Content.benedictionBuff.text'),
content: t('Content.benedictionBuff.content', {
skillAtkBoostScaling: TsUtils.precisionRound(100 * skillAtkBoostScaling),
@@ -35,32 +49,32 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
skillLightningDmgBoostScaling: TsUtils.precisionRound(100 * skillLightningDmgBoostScaling),
}),
},
- {
- formItem: 'switch',
+ skillSpdBuff: {
id: 'skillSpdBuff',
+ formItem: 'switch',
text: t('Content.skillSpdBuff.text'),
content: t('Content.skillSpdBuff.content'),
},
- {
- formItem: 'switch',
+ ultDmgBuff: {
id: 'ultDmgBuff',
+ formItem: 'switch',
text: t('Content.ultDmgBuff.text'),
content: t('Content.ultDmgBuff.content', { ultDmgBoost: TsUtils.precisionRound(100 * ultDmgBoost) }),
},
- {
- formItem: 'switch',
+ ultSpdBuff: {
id: 'ultSpdBuff',
+ formItem: 'switch',
text: t('Content.ultSpdBuff.text'),
content: t('Content.ultSpdBuff.content'),
disabled: e < 1,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'benedictionBuff'),
- {
- formItem: 'slider',
+ const teammateContent: ContentDefinition = {
+ benedictionBuff: content.benedictionBuff,
+ teammateAtkBuffValue: {
id: 'teammateAtkBuffValue',
+ formItem: 'slider',
text: t('TeammateContent.teammateAtkBuffValue.text'),
content: t('TeammateContent.teammateAtkBuffValue.content', {
skillAtkBoostScaling: TsUtils.precisionRound(100 * skillAtkBoostScaling),
@@ -71,68 +85,64 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
max: skillAtkBoostScaling,
percent: true,
},
- findContentId(content, 'ultDmgBuff'),
- findContentId(content, 'ultSpdBuff'),
- ]
+ ultDmgBuff: content.ultDmgBuff,
+ ultSpdBuff: content.ultSpdBuff,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- benedictionBuff: false,
- skillSpdBuff: false,
- ultSpdBuff: false,
- ultDmgBuff: false,
- }),
- teammateDefaults: () => ({
- benedictionBuff: true,
- ultSpdBuff: false,
- ultDmgBuff: true,
- teammateAtkBuffValue: skillAtkBoostScaling,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.SPD_P] += (r.skillSpdBuff) ? 0.20 : 0
+ x.SPD_P.buff((r.skillSpdBuff) ? 0.20 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
// Boost
- buffAbilityDmg(x, BASIC_TYPE, 0.40)
+ buffAbilityDmg(x, BASIC_TYPE, 0.40, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.SPD_P] += (e >= 1 && m.ultSpdBuff) ? 0.20 : 0
+ x.SPD_P.buff((e >= 1 && m.ultSpdBuff) ? 0.20 : 0, Source.NONE)
- x.ELEMENTAL_DMG += (m.ultDmgBuff) ? ultDmgBoost : 0
+ x.ELEMENTAL_DMG.buff((m.ultDmgBuff) ? ultDmgBoost : 0, Source.NONE)
},
- precomputeTeammateEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const t = action.characterConditionals
+ precomputeTeammateEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const t: Conditionals = action.characterConditionals
- x[Stats.ATK_P] += (t.benedictionBuff) ? t.teammateAtkBuffValue : 0
+ x.ATK_P.buff((t.benedictionBuff) ? t.teammateAtkBuffValue : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// x[Stats.ATK] += (r.benedictionBuff) ? x[Stats.ATK] * skillAtkBoostMax : 0
- x.BASIC_DMG += x.BASIC_SCALING * x[Stats.ATK] + ((r.benedictionBuff)
- ? skillLightningDmgBoostScaling + talentScaling
- : 0) * x[Stats.ATK]
- x.SKILL_DMG += x.SKILL_SCALING * x[Stats.ATK]
- x.ULT_DMG += x.ULT_SCALING * x[Stats.ATK]
+ x.BASIC_DMG.buff(
+ x.a[Key.BASIC_SCALING] * x.a[Key.ATK]
+ + (
+ (r.benedictionBuff)
+ ? skillLightningDmgBoostScaling + talentScaling
+ : 0
+ ) * x.a[Key.ATK],
+ Source.NONE)
+
+ x.SKILL_DMG.buff(x.a[Key.SKILL_SCALING] * x.a[Key.ATK], Source.NONE)
+ x.ULT_DMG.buff(x.a[Key.ULT_SCALING] * x.a[Key.ATK], Source.NONE)
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
return `
x.BASIC_DMG += x.BASIC_SCALING * x.ATK;
if (${wgslTrue(r.benedictionBuff)}) {
@@ -153,27 +163,27 @@ x.ULT_DMG += x.ULT_SCALING * x.ATK;
condition: function () {
return true
},
- effect: function (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ effect: function (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) {
+ const r: Conditionals = action.characterConditionals
if (!r.benedictionBuff) {
return
}
const stateValue = action.conditionalState[this.id] || 0
- const convertibleAtkValue = x[Stats.ATK] - x.RATIO_BASED_ATK_BUFF
+ const convertibleAtkValue = x.a[Key.ATK] - x.a[Key.RATIO_BASED_ATK_BUFF]
const buffATK = skillAtkBoostMax * convertibleAtkValue
const stateBuffATK = skillAtkBoostMax * stateValue
- action.conditionalState[this.id] = x[Stats.ATK]
+ action.conditionalState[this.id] = x.a[Key.ATK]
const finalBuffAtk = buffATK - (stateValue ? stateBuffATK : 0)
- x.RATIO_BASED_ATK_BUFF += finalBuffAtk
+ x.RATIO_BASED_ATK_BUFF.buff(finalBuffAtk, Source.NONE)
- buffStat(x, Stats.ATK, finalBuffAtk, action, context)
+ x.ATK.buffDynamic(finalBuffAtk, Source.NONE, action, context)
},
gpu: function (action: OptimizerAction, context: OptimizerContext) {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
return conditionalWgslWrapper(this, `
if (${wgslFalse(r.benedictionBuff)}) {
diff --git a/src/lib/conditionals/character/Topaz.ts b/src/lib/conditionals/character/Topaz.ts
index 3f728f9aa..1924041eb 100644
--- a/src/lib/conditionals/character/Topaz.ts
+++ b/src/lib/conditionals/character/Topaz.ts
@@ -1,18 +1,11 @@
-import {
- ASHBLAZING_ATK_STACK,
- BASIC_TYPE,
- ComputedStatsObject,
- FUA_TYPE,
- SKILL_TYPE,
-} from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, calculateAshblazingSet, findContentId } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { ASHBLAZING_ATK_STACK, BASIC_TYPE, FUA_TYPE, SKILL_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, calculateAshblazingSet, Conditionals, ContentDefinition } from 'lib/conditionals/conditionalUtils'
import { buffAbilityCd, buffAbilityResPen, buffAbilityVulnerability } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -39,99 +32,103 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const fuaEnhancedHitCountMulti = ASHBLAZING_ATK_STACK
* (1 * 1 / 10 + 2 * 1 / 10 + 3 * 1 / 10 + 4 * 1 / 10 + 5 * 1 / 10 + 6 * 1 / 10 + 7 * 1 / 10 + 8 * 3 / 10)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ enemyProofOfDebtDebuff: true,
+ numbyEnhancedState: true,
+ e1DebtorStacks: 2,
+ }
+
+ const teammateDefaults = {
+ enemyProofOfDebtDebuff: true,
+ e1DebtorStacks: 2,
+ }
+
+ const content: ContentDefinition = {
+ enemyProofOfDebtDebuff: {
id: 'enemyProofOfDebtDebuff',
+ formItem: 'switch',
text: t('Content.enemyProofOfDebtDebuff.text'),
content: t('Content.enemyProofOfDebtDebuff.content', { proofOfDebtFuaVulnerability: TsUtils.precisionRound(100 * proofOfDebtFuaVulnerability) }),
},
- {
- formItem: 'switch',
+ numbyEnhancedState: {
id: 'numbyEnhancedState',
+ formItem: 'switch',
text: t('Content.numbyEnhancedState.text'),
content: t('Content.numbyEnhancedState.content', {
enhancedStateFuaCdBoost: TsUtils.precisionRound(100 * enhancedStateFuaCdBoost),
enhancedStateFuaScalingBoost: TsUtils.precisionRound(100 * enhancedStateFuaScalingBoost),
}),
},
- {
- formItem: 'slider',
+ e1DebtorStacks: {
id: 'e1DebtorStacks',
+ formItem: 'slider',
text: t('Content.e1DebtorStacks.text'),
content: t('Content.e1DebtorStacks.content'),
min: 0,
max: 2,
disabled: e < 1,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'enemyProofOfDebtDebuff'),
- findContentId(content, 'e1DebtorStacks'),
- ]
+ const teammateContent: ContentDefinition = {
+ enemyProofOfDebtDebuff: content.enemyProofOfDebtDebuff,
+ e1DebtorStacks: content.e1DebtorStacks,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- enemyProofOfDebtDebuff: true,
- numbyEnhancedState: true,
- e1DebtorStacks: 2,
- }),
- teammateDefaults: () => ({
- enemyProofOfDebtDebuff: true,
- e1DebtorStacks: 2,
- }),
- initializeConfigurations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- x.BASIC_DMG_TYPE = BASIC_TYPE | FUA_TYPE
- x.SKILL_DMG_TYPE = SKILL_TYPE | FUA_TYPE
- x.SUMMONS = 1
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ initializeConfigurations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ x.BASIC_DMG_TYPE.set(BASIC_TYPE | FUA_TYPE, Source.NONE)
+ x.SKILL_DMG_TYPE.set(SKILL_TYPE | FUA_TYPE, Source.NONE)
+ x.SUMMONS.set(1, Source.NONE)
},
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
- buffAbilityCd(x, SKILL_TYPE | FUA_TYPE, enhancedStateFuaCdBoost, (r.numbyEnhancedState))
- buffAbilityResPen(x, SKILL_TYPE | FUA_TYPE, 0.10, (e >= 6))
+ buffAbilityCd(x, SKILL_TYPE | FUA_TYPE, (r.numbyEnhancedState) ? enhancedStateFuaCdBoost : 0, Source.NONE)
+ buffAbilityResPen(x, SKILL_TYPE | FUA_TYPE, (e >= 6) ? 0.10 : 0, Source.NONE)
// Numby buffs only applies to the skill/fua not basic, we deduct it from basic
- buffAbilityCd(x, BASIC_TYPE, -enhancedStateFuaCdBoost, (r.numbyEnhancedState))
- buffAbilityResPen(x, BASIC_TYPE, -0.10, (e >= 6))
+ buffAbilityCd(x, BASIC_TYPE, (r.numbyEnhancedState) ? -enhancedStateFuaCdBoost : 0, Source.NONE)
+ buffAbilityResPen(x, BASIC_TYPE, (e >= 6) ? -0.10 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.SKILL_SCALING += (r.numbyEnhancedState) ? enhancedStateFuaScalingBoost : 0
- x.FUA_SCALING += fuaScaling
- x.FUA_SCALING += (r.numbyEnhancedState) ? enhancedStateFuaScalingBoost : 0
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.SKILL_SCALING.buff((r.numbyEnhancedState) ? enhancedStateFuaScalingBoost : 0, Source.NONE)
+ x.FUA_SCALING.buff(fuaScaling, Source.NONE)
+ x.FUA_SCALING.buff((r.numbyEnhancedState) ? enhancedStateFuaScalingBoost : 0, Source.NONE)
// Boost
- x.ELEMENTAL_DMG += (context.enemyElementalWeak) ? 0.15 : 0
+ x.ELEMENTAL_DMG.buff((context.enemyElementalWeak) ? 0.15 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.FUA_TOUGHNESS_DMG += 60
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.FUA_TOUGHNESS_DMG.buff(60, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- buffAbilityVulnerability(x, FUA_TYPE, proofOfDebtFuaVulnerability, (m.enemyProofOfDebtDebuff))
- buffAbilityCd(x, FUA_TYPE, 0.25 * m.e1DebtorStacks, (e >= 1 && m.enemyProofOfDebtDebuff))
+ buffAbilityVulnerability(x, FUA_TYPE, (m.enemyProofOfDebtDebuff) ? proofOfDebtFuaVulnerability : 0, Source.NONE)
+ buffAbilityCd(x, FUA_TYPE, (e >= 1 && m.enemyProofOfDebtDebuff) ? 0.25 * m.e1DebtorStacks : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
const hitMulti = (r.numbyEnhancedState) ? fuaEnhancedHitCountMulti : fuaHitCountMulti
const basicAshblazingAtk = calculateAshblazingSet(x, action, context, basicHitCountMulti)
const fuaAshblazingAtk = calculateAshblazingSet(x, action, context, hitMulti)
- x.BASIC_DMG += x.BASIC_SCALING * (x[Stats.ATK] + basicAshblazingAtk)
- x.FUA_DMG += x.FUA_SCALING * (x[Stats.ATK] + fuaAshblazingAtk)
- x.SKILL_DMG = x.FUA_DMG
+ x.BASIC_DMG.buff(x.a[Key.BASIC_SCALING] * (x.a[Key.ATK] + basicAshblazingAtk), Source.NONE)
+ x.FUA_DMG.buff(x.a[Key.FUA_SCALING] * (x.a[Key.ATK] + fuaAshblazingAtk), Source.NONE)
+ x.SKILL_DMG.set(x.a[Key.FUA_DMG], Source.NONE)
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
const hitMulti = (r.numbyEnhancedState) ? fuaEnhancedHitCountMulti : fuaHitCountMulti
return `
diff --git a/src/lib/conditionals/character/TrailblazerDestruction.ts b/src/lib/conditionals/character/TrailblazerDestruction.ts
index 6b888defc..cfb8349b1 100644
--- a/src/lib/conditionals/character/TrailblazerDestruction.ts
+++ b/src/lib/conditionals/character/TrailblazerDestruction.ts
@@ -1,11 +1,10 @@
-import { ComputedStatsObject, SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
-import { AbilityEidolon, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { SKILL_TYPE, ULT_TYPE } from 'lib/conditionals/conditionalConstants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
import { buffAbilityDmg } from 'lib/optimizer/calculateBuffs'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -20,10 +19,15 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const ultEnhancedScaling = ult(e, 2.70, 2.88)
const ultEnhancedScaling2 = ult(e, 1.62, 1.728)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ enhancedUlt: true,
+ talentStacks: 2,
+ }
+
+ const content: ContentDefinition = {
+ enhancedUlt: {
id: 'enhancedUlt',
+ formItem: 'switch',
text: t('Content.enhancedUlt.text'),
content: t('Content.enhancedUlt.content', {
ultScaling: TsUtils.precisionRound(100 * ultScaling),
@@ -31,48 +35,43 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
ultEnhancedScaling2: TsUtils.precisionRound(100 * ultEnhancedScaling2),
}),
},
- {
- formItem: 'slider',
+ talentStacks: {
id: 'talentStacks',
+ formItem: 'slider',
text: t('Content.talentStacks.text'),
content: t('Content.talentStacks.content', { talentAtkScalingValue: TsUtils.precisionRound(100 * talentAtkScalingValue) }),
min: 0,
max: 2,
},
- ]
+ }
return {
- content: () => content,
- teammateContent: () => [],
- defaults: () => ({
- enhancedUlt: true,
- talentStacks: 2,
- }),
- teammateDefaults: () => ({}),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ defaults: () => defaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.ATK_P] += r.talentStacks * talentAtkScalingValue
- x[Stats.DEF_P] += r.talentStacks * 0.10
- x[Stats.CR] += (x.ENEMY_WEAKNESS_BROKEN) ? 0.25 : 0
+ x.ATK_P.buff(r.talentStacks * talentAtkScalingValue, Source.NONE)
+ x.DEF_P.buff(r.talentStacks * 0.10, Source.NONE)
+ x.CR.buff((x.a[Key.ENEMY_WEAKNESS_BROKEN]) ? 0.25 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += (r.enhancedUlt) ? ultEnhancedScaling : ultScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff((r.enhancedUlt) ? ultEnhancedScaling : ultScaling, Source.NONE)
// Boost
- buffAbilityDmg(x, SKILL_TYPE, 0.25)
- buffAbilityDmg(x, ULT_TYPE, 0.25, (r.enhancedUlt))
+ buffAbilityDmg(x, SKILL_TYPE, 0.25, Source.NONE)
+ buffAbilityDmg(x, ULT_TYPE, (r.enhancedUlt) ? 0.25 : 0, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 60
- x.ULT_TOUGHNESS_DMG += (r.enhancedUlt) ? 60 : 90
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(60, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff((r.enhancedUlt) ? 60 : 90, Source.NONE)
return x
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/TrailblazerHarmony.ts b/src/lib/conditionals/character/TrailblazerHarmony.ts
index c4848d4d8..54d641f96 100644
--- a/src/lib/conditionals/character/TrailblazerHarmony.ts
+++ b/src/lib/conditionals/character/TrailblazerHarmony.ts
@@ -1,16 +1,9 @@
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardAtkFinalizer,
- standardAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
+import { ComputedStatsArray, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -22,48 +15,61 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const ultBeScaling = ult(e, 0.30, 0.33)
const skillMaxHits = e >= 6 ? 6 : 4
- const targetsToSuperBreakMulti = {
+ const targetsToSuperBreakMulti: Record = {
1: 1.60,
3: 1.40,
5: 1.20,
}
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ skillHitsOnTarget: skillMaxHits,
+ backupDancer: true,
+ superBreakDmg: true,
+ e2EnergyRegenBuff: false,
+ }
+
+ const teammateDefaults = {
+ backupDancer: true,
+ superBreakDmg: true,
+ teammateBeValue: 2.00,
+ }
+
+ const content: ContentDefinition = {
+ backupDancer: {
id: 'backupDancer',
+ formItem: 'switch',
text: t('Content.backupDancer.text'),
content: t('Content.backupDancer.content', { ultBeScaling: TsUtils.precisionRound(100 * ultBeScaling) }),
},
- {
- formItem: 'switch',
+ superBreakDmg: {
id: 'superBreakDmg',
+ formItem: 'switch',
text: t('Content.superBreakDmg.text'),
content: t('Content.superBreakDmg.content'),
},
- {
- formItem: 'slider',
+ skillHitsOnTarget: {
id: 'skillHitsOnTarget',
+ formItem: 'slider',
text: t('Content.skillHitsOnTarget.text'),
content: t('Content.skillHitsOnTarget.content'),
min: 0,
max: skillMaxHits,
},
- {
- formItem: 'switch',
+ e2EnergyRegenBuff: {
id: 'e2EnergyRegenBuff',
+ formItem: 'switch',
text: t('Content.e2EnergyRegenBuff.text'),
content: t('Content.e2EnergyRegenBuff.content'),
disabled: e < 2,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'backupDancer'),
- findContentId(content, 'superBreakDmg'),
- {
- formItem: 'slider',
+ const teammateContent: ContentDefinition = {
+ backupDancer: content.backupDancer,
+ superBreakDmg: content.superBreakDmg,
+ teammateBeValue: {
id: 'teammateBeValue',
+ formItem: 'slider',
text: t('TeammateContent.teammateBeValue.text'),
content: t('TeammateContent.teammateBeValue.content'),
min: 0,
@@ -71,66 +77,57 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
percent: true,
disabled: e < 4,
},
- ]
-
- const defaults = {
- skillHitsOnTarget: skillMaxHits,
- backupDancer: true,
- superBreakDmg: true,
- e2EnergyRegenBuff: false,
}
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => (defaults),
- teammateDefaults: () => ({
- backupDancer: true,
- superBreakDmg: true,
- teammateBeValue: 2.00,
- }),
- initializeConfigurations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ initializeConfigurations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
if (r.superBreakDmg) {
- x.ENEMY_WEAKNESS_BROKEN = 1
+ x.ENEMY_WEAKNESS_BROKEN.set(1, Source.NONE)
}
},
- initializeTeammateConfigurations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ initializeTeammateConfigurations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
if (r.superBreakDmg) {
- x.ENEMY_WEAKNESS_BROKEN = 1
+ x.ENEMY_WEAKNESS_BROKEN.set(1, Source.NONE)
}
},
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.ERR] += (e >= 2 && r.e2EnergyRegenBuff) ? 0.25 : 0
+ x.ERR.buff((e >= 2 && r.e2EnergyRegenBuff) ? 0.25 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.SKILL_SCALING += r.skillHitsOnTarget * skillScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.SKILL_SCALING.buff(r.skillHitsOnTarget * skillScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 30 * r.skillHitsOnTarget
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(30 * r.skillHitsOnTarget, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
- x[Stats.BE] += (m.backupDancer) ? ultBeScaling : 0
- x.SUPER_BREAK_HMC_MODIFIER += (m.backupDancer && m.superBreakDmg)
- ? targetsToSuperBreakMulti[context.enemyCount]
- : 0
+ x.BE.buff((m.backupDancer) ? ultBeScaling : 0, Source.NONE)
+ x.SUPER_BREAK_HMC_MODIFIER.buff(
+ (m.backupDancer && m.superBreakDmg)
+ ? targetsToSuperBreakMulti[context.enemyCount]
+ : 0,
+ Source.NONE)
},
- precomputeTeammateEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const t = action.characterConditionals
+ precomputeTeammateEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const t: Conditionals = action.characterConditionals
- x[Stats.BE] += (e >= 4) ? 0.15 * t.teammateBeValue : 0
+ x.BE.buff((e >= 4) ? 0.15 * t.teammateBeValue : 0, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject) => standardAtkFinalizer(x),
+ finalizeCalculations: (x: ComputedStatsArray) => standardAtkFinalizer(x),
gpuFinalizeCalculations: () => gpuStandardAtkFinalizer(),
}
}
diff --git a/src/lib/conditionals/character/TrailblazerPreservation.ts b/src/lib/conditionals/character/TrailblazerPreservation.ts
index a53badf6f..30fbbed18 100644
--- a/src/lib/conditionals/character/TrailblazerPreservation.ts
+++ b/src/lib/conditionals/character/TrailblazerPreservation.ts
@@ -1,16 +1,9 @@
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardDefShieldFinalizer,
- standardDefShieldFinalizer,
-} from 'lib/conditionals/conditionalUtils'
-import { Stats } from 'lib/constants'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardDefShieldFinalizer, standardDefShieldFinalizer } from 'lib/conditionals/conditionalUtils'
import { wgslTrue } from 'lib/gpu/injection/wgslUtils'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -30,100 +23,104 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const talentShieldScaling = talent(e, 0.06, 0.064)
const talentShieldFlat = talent(e, 80, 89)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const defaults = {
+ enhancedBasic: true,
+ skillActive: true,
+ shieldActive: true,
+ e6DefStacks: 3,
+ }
+
+ const teammateDefaults = {
+ skillActive: true,
+ }
+
+ const content: ContentDefinition = {
+ enhancedBasic: {
id: 'enhancedBasic',
+ formItem: 'switch',
text: t('Content.enhancedBasic.text'),
content: t('Content.enhancedBasic.content', { basicEnhancedAtkScaling: TsUtils.precisionRound(100 * basicEnhancedAtkScaling) }),
},
- {
- formItem: 'switch',
+ skillActive: {
id: 'skillActive',
+ formItem: 'switch',
text: t('Content.skillActive.text'),
content: t('Content.skillActive.content', { skillDamageReductionValue: TsUtils.precisionRound(100 * skillDamageReductionValue) }),
},
- {
- formItem: 'switch',
+ shieldActive: {
id: 'shieldActive',
+ formItem: 'switch',
text: t('Content.shieldActive.text'),
content: t('Content.shieldActive.content'),
},
- {
- formItem: 'slider',
+ e6DefStacks: {
id: 'e6DefStacks',
+ formItem: 'slider',
text: t('Content.e6DefStacks.text'),
content: t('Content.e6DefStacks.content'),
min: 0,
max: 3,
disabled: e < 6,
},
- ]
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'skillActive'),
- ]
+ const teammateContent: ContentDefinition = {
+ skillActive: content.skillActive,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- enhancedBasic: true,
- skillActive: true,
- shieldActive: true,
- e6DefStacks: 3,
- }),
- teammateDefaults: () => ({
- skillActive: true,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x[Stats.DEF_P] += (e >= 6) ? r.e6DefStacks * 0.10 : 0
- x[Stats.ATK_P] += (r.shieldActive) ? 0.15 : 0
+ x.DEF_P.buff((e >= 6) ? r.e6DefStacks * 0.10 : 0, Source.NONE)
+ x.ATK_P.buff((r.shieldActive) ? 0.15 : 0, Source.NONE)
// Scaling
- x.SKILL_SCALING += skillScaling
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
// Boost
// This EHR buff only applies to self
- x.DMG_RED_MULTI *= (r.skillActive) ? (1 - skillDamageReductionValue) : 1
+ x.DMG_RED_MULTI.multiply((r.skillActive) ? (1 - skillDamageReductionValue) : 1, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += (r.basicEnhanced) ? 60 : 30
- x.ULT_TOUGHNESS_DMG += 60
+ x.BASIC_TOUGHNESS_DMG.buff((r.enhancedBasic) ? 60 : 30, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
- x.SHIELD_SCALING += talentShieldScaling
- x.SHIELD_FLAT += talentShieldFlat
+ x.SHIELD_SCALING.buff(talentShieldScaling, Source.NONE)
+ x.SHIELD_FLAT.buff(talentShieldFlat, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals = action.characterConditionals
// This EHR buff applies to all
- x.DMG_RED_MULTI *= (m.skillActive) ? (1 - 0.15) : 1
+ x.DMG_RED_MULTI.multiply((m.skillActive) ? (1 - 0.15) : 1, Source.NONE)
},
- finalizeCalculations: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ finalizeCalculations: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
if (r.enhancedBasic) {
- x.BASIC_DMG += basicEnhancedAtkScaling * x[Stats.ATK]
- x.BASIC_DMG += basicEnhancedDefScaling * x[Stats.DEF]
+ x.BASIC_DMG.buff(basicEnhancedAtkScaling * x.a[Key.ATK], Source.NONE)
+ x.BASIC_DMG.buff(basicEnhancedDefScaling * x.a[Key.DEF], Source.NONE)
} else {
- x.BASIC_DMG += basicAtkScaling * x[Stats.ATK]
- x.BASIC_DMG += basicDefScaling * x[Stats.DEF]
+ x.BASIC_DMG.buff(basicAtkScaling * x.a[Key.ATK], Source.NONE)
+ x.BASIC_DMG.buff(basicDefScaling * x.a[Key.DEF], Source.NONE)
}
- x.SKILL_DMG += x.SKILL_SCALING * x[Stats.ATK]
+ x.SKILL_DMG.buff(x.a[Key.SKILL_SCALING] * x.a[Key.ATK], Source.NONE)
- x.ULT_DMG += ultAtkScaling * x[Stats.ATK]
- x.ULT_DMG += ultDefScaling * x[Stats.DEF]
+ x.ULT_DMG.buff(ultAtkScaling * x.a[Key.ATK], Source.NONE)
+ x.ULT_DMG.buff(ultDefScaling * x.a[Key.DEF], Source.NONE)
standardDefShieldFinalizer(x)
},
gpuFinalizeCalculations: (action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ const r: Conditionals = action.characterConditionals
return `
if (${wgslTrue(r.enhancedBasic)}) {
diff --git a/src/lib/conditionals/character/Welt.ts b/src/lib/conditionals/character/Welt.ts
index 638350edd..8626fe78d 100644
--- a/src/lib/conditionals/character/Welt.ts
+++ b/src/lib/conditionals/character/Welt.ts
@@ -1,15 +1,9 @@
-import { ComputedStatsObject } from 'lib/conditionals/conditionalConstants'
-import {
- AbilityEidolon,
- findContentId,
- gpuStandardAtkFinalizer,
- standardAtkFinalizer,
-} from 'lib/conditionals/conditionalUtils'
+import { AbilityEidolon, Conditionals, ContentDefinition, gpuStandardAtkFinalizer, standardAtkFinalizer } from 'lib/conditionals/conditionalUtils'
+import { ComputedStatsArray, Key, Source } from 'lib/optimizer/computedStatsArray'
import { TsUtils } from 'lib/TsUtils'
import { Eidolon } from 'types/Character'
import { CharacterConditional } from 'types/CharacterConditional'
-import { ContentItem } from 'types/Conditionals'
import { OptimizerAction, OptimizerContext } from 'types/Optimizer'
export default (e: Eidolon, withContent: boolean): CharacterConditional => {
@@ -23,84 +17,88 @@ export default (e: Eidolon, withContent: boolean): CharacterConditional => {
const ultScaling = ult(e, 1.50, 1.62)
const talentScaling = talent(e, 0.60, 0.66)
- const content: ContentItem[] = [
- {
- formItem: 'switch',
+ const content: ContentDefinition = {
+ enemyDmgTakenDebuff: {
id: 'enemyDmgTakenDebuff',
+ formItem: 'switch',
text: t('Content.enemyDmgTakenDebuff.text'),
content: t('Content.enemyDmgTakenDebuff.content'),
},
- {
- formItem: 'switch',
+ enemySlowed: {
id: 'enemySlowed',
+ formItem: 'switch',
text: t('Content.enemySlowed.text'),
content: t('Content.enemySlowed.content', { talentScaling: TsUtils.precisionRound(100 * talentScaling) }),
},
- {
- formItem: 'slider',
+ skillExtraHits: {
id: 'skillExtraHits',
+ formItem: 'slider',
text: t('Content.skillExtraHits.text'),
content: t('Content.skillExtraHits.content', { skillScaling: TsUtils.precisionRound(100 * skillScaling) }),
min: 0,
max: skillExtraHitsMax,
},
- {
- formItem: 'switch',
+ e1EnhancedState: {
id: 'e1EnhancedState',
+ formItem: 'switch',
text: t('Content.e1EnhancedState.text'),
content: t('Content.e1EnhancedState.content'),
disabled: (e < 1),
},
- ]
+ }
+
+ const teammateContent: ContentDefinition = {
+ enemyDmgTakenDebuff: content.enemyDmgTakenDebuff,
+ }
- const teammateContent: ContentItem[] = [
- findContentId(content, 'enemyDmgTakenDebuff'),
- ]
+ const defaults = {
+ enemySlowed: true,
+ enemyDmgTakenDebuff: true,
+ skillExtraHits: skillExtraHitsMax,
+ e1EnhancedState: true,
+ }
+
+ const teammateDefaults = {
+ enemyDmgTakenDebuff: true,
+ }
return {
- content: () => content,
- teammateContent: () => teammateContent,
- defaults: () => ({
- enemySlowed: true,
- enemyDmgTakenDebuff: true,
- skillExtraHits: skillExtraHitsMax,
- e1EnhancedState: true,
- }),
- teammateDefaults: () => ({
- enemyDmgTakenDebuff: true,
- }),
- precomputeEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const r = action.characterConditionals
+ content: () => Object.values(content),
+ teammateContent: () => Object.values(teammateContent),
+ defaults: () => defaults,
+ teammateDefaults: () => teammateDefaults,
+ precomputeEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const r: Conditionals = action.characterConditionals
// Stats
- x.ELEMENTAL_DMG += (x.ENEMY_WEAKNESS_BROKEN) ? 0.20 : 0
+ x.ELEMENTAL_DMG.buff((x.a[Key.ENEMY_WEAKNESS_BROKEN]) ? 0.20 : 0, Source.NONE)
// Scaling
- x.BASIC_SCALING += basicScaling
- x.SKILL_SCALING += skillScaling
- x.ULT_SCALING += ultScaling
+ x.BASIC_SCALING.buff(basicScaling, Source.NONE)
+ x.SKILL_SCALING.buff(skillScaling, Source.NONE)
+ x.ULT_SCALING.buff(ultScaling, Source.NONE)
- x.BASIC_SCALING += (r.enemySlowed) ? talentScaling : 0
- x.SKILL_SCALING += (r.enemySlowed) ? talentScaling : 0
- x.ULT_SCALING += (r.enemySlowed) ? talentScaling : 0
+ x.BASIC_SCALING.buff((r.enemySlowed) ? talentScaling : 0, Source.NONE)
+ x.SKILL_SCALING.buff((r.enemySlowed) ? talentScaling : 0, Source.NONE)
+ x.ULT_SCALING.buff((r.enemySlowed) ? talentScaling : 0, Source.NONE)
- x.BASIC_SCALING += (e >= 1 && r.e1EnhancedState) ? 0.50 * basicScaling : 0
- x.SKILL_SCALING += (e >= 1 && r.e1EnhancedState) ? 0.80 * skillScaling : 0
+ x.BASIC_SCALING.buff((e >= 1 && r.e1EnhancedState) ? 0.50 * basicScaling : 0, Source.NONE)
+ x.SKILL_SCALING.buff((e >= 1 && r.e1EnhancedState) ? 0.80 * skillScaling : 0, Source.NONE)
- x.SKILL_SCALING += r.skillExtraHits * skillScaling
+ x.SKILL_SCALING.buff(r.skillExtraHits * skillScaling, Source.NONE)
- x.BASIC_TOUGHNESS_DMG += 30
- x.SKILL_TOUGHNESS_DMG += 30 + 30 * r.skillExtraHits
- x.ULT_TOUGHNESS_DMG += 60
+ x.BASIC_TOUGHNESS_DMG.buff(30, Source.NONE)
+ x.SKILL_TOUGHNESS_DMG.buff(30 + 30 * r.skillExtraHits, Source.NONE)
+ x.ULT_TOUGHNESS_DMG.buff(60, Source.NONE)
return x
},
- precomputeMutualEffects: (x: ComputedStatsObject, action: OptimizerAction, context: OptimizerContext) => {
- const m = action.characterConditionals
+ precomputeMutualEffects: (x: ComputedStatsArray, action: OptimizerAction, context: OptimizerContext) => {
+ const m: Conditionals