Calculate own use of RP

This commit is contained in:
Kalle
2022-09-18 12:50:00 +03:00
parent 3b8582d455
commit d10d4bf0c5
7 changed files with 76 additions and 29 deletions

View File

@@ -4,6 +4,11 @@ import { applySpecialEffects } from "./specialEffects";
const ApplySpecialEffects = suite("applySpecialEffects()");
const valueToAps = (value: number) => ({
ap: value,
apBeforeTacticooler: value,
});
ApplySpecialEffects("Adds an effect to empty build", () => {
const aps = applySpecialEffects({
effects: ["CB"],
@@ -12,7 +17,7 @@ ApplySpecialEffects("Adds an effect to empty build", () => {
});
assert.equal(aps.size, 6);
assert.equal(aps.get("ISM"), 10);
assert.equal(aps.get("ISM")?.ap, 10);
});
ApplySpecialEffects(
@@ -20,33 +25,33 @@ ApplySpecialEffects(
() => {
const aps = applySpecialEffects({
effects: ["CB"],
abilityPoints: new Map([["SPU", 10]]),
abilityPoints: new Map([["SPU", valueToAps(10)]]),
ldeIntensity: 0,
});
assert.equal(aps.size, 7);
assert.equal(aps.get("SPU"), 10);
assert.equal(aps.get("SPU")?.ap, 10);
}
);
ApplySpecialEffects("Does not boost ability beyond 57", () => {
const aps = applySpecialEffects({
effects: ["CB"],
abilityPoints: new Map([["ISM", 57]]),
abilityPoints: new Map([["ISM", valueToAps(57)]]),
ldeIntensity: 0,
});
assert.equal(aps.get("ISM"), 57);
assert.equal(aps.get("ISM")?.ap, 57);
});
ApplySpecialEffects("Tacticooler doesn't boost swim speed beyond 29", () => {
const aps = applySpecialEffects({
effects: ["TACTICOOLER"],
abilityPoints: new Map([["SSU", 28]]),
abilityPoints: new Map([["SSU", valueToAps(28)]]),
ldeIntensity: 0,
});
assert.equal(aps.get("SSU"), 29);
assert.equal(aps.get("SSU")?.ap, 29);
});
ApplySpecialEffects(
@@ -54,42 +59,53 @@ ApplySpecialEffects(
() => {
const aps = applySpecialEffects({
effects: ["TACTICOOLER"],
abilityPoints: new Map([["SSU", 30]]),
abilityPoints: new Map([["SSU", valueToAps(30)]]),
ldeIntensity: 0,
});
assert.equal(aps.get("SSU"), 30);
assert.equal(aps.get("SSU")?.ap, 30);
}
);
ApplySpecialEffects("Tacticooler remembers AP before it was applied", () => {
const aps = applySpecialEffects({
effects: ["TACTICOOLER"],
abilityPoints: new Map([["QR", valueToAps(10)]]),
ldeIntensity: 0,
});
assert.equal(aps.get("QR")?.ap, 57);
assert.equal(aps.get("QR")?.apBeforeTacticooler, 10);
});
ApplySpecialEffects("Applies many effects", () => {
const aps = applySpecialEffects({
effects: ["DR", "CB"],
abilityPoints: new Map([["SSU", 1]]),
abilityPoints: new Map([["SSU", valueToAps(1)]]),
ldeIntensity: 0,
});
assert.equal(aps.get("SSU"), 21);
assert.equal(aps.get("SSU")?.ap, 21);
});
ApplySpecialEffects("Applies LDE", () => {
const aps = applySpecialEffects({
effects: ["LDE"],
abilityPoints: new Map([["ISM", 1]]),
abilityPoints: new Map([["ISM", valueToAps(1)]]),
ldeIntensity: 1,
});
assert.equal(aps.get("ISM"), 2);
assert.equal(aps.get("ISM")?.ap, 2);
});
ApplySpecialEffects("Applies LDE (intensity != aps given)", () => {
const aps = applySpecialEffects({
effects: ["LDE"],
abilityPoints: new Map([["ISM", 1]]),
abilityPoints: new Map([["ISM", valueToAps(1)]]),
ldeIntensity: 15,
});
assert.equal(aps.get("ISM"), 18);
assert.equal(aps.get("ISM")?.ap, 18);
});
ApplySpecialEffects.run();

View File

@@ -147,12 +147,17 @@ export function applySpecialEffects({
for (const value of valuesArr) {
const boostsBeyond = "boostsBeyond" in value ? value.boostsBeyond : true;
const currentAP = result.get(value.type) ?? 0;
const newAP = boostsBeyond
const currentAP = result.get(value.type)?.ap ?? 0;
const newAPUnlimited = boostsBeyond
? currentAP + value.ap
: Math.max(currentAP, value.ap);
const newAP = Math.min(newAPUnlimited, MAX_AP);
result.set(value.type, Math.min(newAP, MAX_AP));
result.set(value.type, {
ap: newAP,
apBeforeTacticooler:
effectObj.type === "TACTICOOLER" ? currentAP : newAP,
});
}
}

View File

@@ -121,11 +121,18 @@ function specialPoint({
};
}
const OWN_RESPAWN_PUNISHER_EXTRA_SPECIAL_LOST = 0.225;
function specialSavedAfterDeath({
abilityPoints,
mainWeaponParams,
mainOnlyAbilities,
}: StatFunctionInput): AnalyzedBuild["stats"]["specialPoint"] {
const SPECIAL_SAVED_AFTER_DEATH_ABILITY = "SS";
const hasRespawnPunisher = mainOnlyAbilities.includes("RP");
const extraSpecialLost = hasRespawnPunisher
? OWN_RESPAWN_PUNISHER_EXTRA_SPECIAL_LOST
: 0;
const specialSavedAfterDeathForDisplay = (effect: number) =>
Number(((1.0 - effect) * 100).toFixed(2));
@@ -133,6 +140,7 @@ function specialSavedAfterDeath({
abilityPoints: apFromMap({
abilityPoints: abilityPoints,
ability: SPECIAL_SAVED_AFTER_DEATH_ABILITY,
key: hasRespawnPunisher ? "apBeforeTacticooler" : "ap",
}),
key: "SpecialGaugeRt_Restart",
weapon: mainWeaponParams,
@@ -140,7 +148,7 @@ function specialSavedAfterDeath({
return {
baseValue: specialSavedAfterDeathForDisplay(baseEffect),
value: specialSavedAfterDeathForDisplay(effect),
value: specialSavedAfterDeathForDisplay(effect - extraSpecialLost),
modifiedBy: SPECIAL_SAVED_AFTER_DEATH_ABILITY,
};
}
@@ -436,15 +444,18 @@ function swimSpeed(
}
const RESPAWN_CHASE_FRAME = 150;
const OWN_RESPAWN_PUNISHER_EXTRA_RESPAWN_FRAMES = 68;
function quickRespawnTime(
args: StatFunctionInput
): AnalyzedBuild["stats"]["quickRespawnTime"] {
const QUICK_RESPAWN_TIME_ABILITY = "QR";
const hasRespawnPunisher = args.mainOnlyAbilities.includes("RP");
const chase = abilityPointsToEffects({
abilityPoints: apFromMap({
abilityPoints: args.abilityPoints,
ability: QUICK_RESPAWN_TIME_ABILITY,
key: hasRespawnPunisher ? "apBeforeTacticooler" : "ap",
}),
key: "Dying_ChaseFrm",
weapon: args.mainWeaponParams,
@@ -453,16 +464,23 @@ function quickRespawnTime(
abilityPoints: apFromMap({
abilityPoints: args.abilityPoints,
ability: QUICK_RESPAWN_TIME_ABILITY,
key: hasRespawnPunisher ? "apBeforeTacticooler" : "ap",
}),
key: "Dying_AroundFrm",
weapon: args.mainWeaponParams,
});
const extraFrames = hasRespawnPunisher
? OWN_RESPAWN_PUNISHER_EXTRA_RESPAWN_FRAMES
: 0;
return {
baseValue: framesToSeconds(
RESPAWN_CHASE_FRAME + chase.baseEffect + around.baseEffect
),
value: framesToSeconds(RESPAWN_CHASE_FRAME + chase.effect + around.effect),
value: framesToSeconds(
RESPAWN_CHASE_FRAME + chase.effect + around.effect + extraFrames
),
modifiedBy: QUICK_RESPAWN_TIME_ABILITY,
};
}

View File

@@ -107,7 +107,10 @@ export interface Stat {
modifiedBy: Ability;
}
export type AbilityPoints = Map<Ability, number>;
export type AbilityPoints = Map<
Ability,
{ ap: number; apBeforeTacticooler: number }
>;
export interface StatFunctionInput {
mainWeaponParams: MainWeaponParams;

View File

@@ -25,8 +25,8 @@ BuildToAbilityPoints("Calculates ability points", () => {
EMPTY_ROW,
]);
assert.equal(aps.get("SS"), 13);
assert.equal(aps.get("RSU"), 6);
assert.equal(aps.get("SS")?.ap, 13);
assert.equal(aps.get("RSU")?.ap, 6);
});
BuildToAbilityPoints("Handles ability doubler", () => {
@@ -36,7 +36,7 @@ BuildToAbilityPoints("Handles ability doubler", () => {
EMPTY_ROW,
]);
assert.equal(aps.get("SS"), 6);
assert.equal(aps.get("SS")?.ap, 6);
});
BuildToAbilityPoints("Does not calculate AP for main only abilities", () => {

View File

@@ -31,8 +31,9 @@ export function buildToAbilityPoints(build: BuildAbilitiesTupleWithUnknown) {
const aps = i === 0 ? 10 : 3;
const apsDoubled = aps * (abilityDoublerActive ? 2 : 1);
const newAp = (result.get(ability)?.ap ?? 0) + apsDoubled;
result.set(ability, (result.get(ability) ?? 0) + apsDoubled);
result.set(ability, { ap: newAp, apBeforeTacticooler: newAp });
}
}
@@ -50,11 +51,13 @@ function isStackableAbility(ability: AbilityWithUnknown): ability is Ability {
export function apFromMap({
abilityPoints,
ability,
key = "ap",
}: {
abilityPoints: AbilityPoints;
ability: Ability;
key?: "ap" | "apBeforeTacticooler";
}) {
return abilityPoints.get(ability) ?? 0;
return abilityPoints.get(ability)?.[key] ?? 0;
}
function abilityValues({

View File

@@ -487,15 +487,17 @@ function AbilityPointsDetails({
<summary className="analyzer__ap-summary">{t("abilityPoints")}</summary>
<div className="stack sm horizontal flex-wrap mt-4">
{abilities
.filter((a) => (abilityPoints.get(a.name) ?? 0) > 0)
.filter((a) => (abilityPoints.get(a.name)?.ap ?? 0) > 0)
.sort((a, b) => {
return abilityPoints.get(b.name)! - abilityPoints.get(a.name)!;
return (
abilityPoints.get(b.name)!.ap - abilityPoints.get(a.name)!.ap
);
})
.map((a) => (
<div key={a.name} className="stack items-center">
<Ability ability={a.name} size="TINY" />
<div className="analyzer__ap-text">
{abilityPoints.get(a.name)}
{abilityPoints.get(a.name)?.ap}
</div>
</div>
))}