Skip to content

Commit

Permalink
first pass
Browse files Browse the repository at this point in the history
  • Loading branch information
xiashtra committed Oct 19, 2024
1 parent f392bc2 commit 0c7bd11
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 10 deletions.
36 changes: 30 additions & 6 deletions packages/core/src/sims/cycle_sim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,10 @@ export class CycleProcessor {
...record.dot,
damagePerTick: dmgInfo.dot.damagePerTick,
} : null,
channel: record.channel ? {
...record.channel,
damagePerTick: dmgInfo.channel.damagePerTick,
} : null,
}
}
})
Expand Down Expand Up @@ -792,15 +796,20 @@ export class CycleProcessor {
const directDamage = multiplyFixed(record.directDamage, partialRate);
const dot = record.dot;
const dotDmg = dot ? multiplyIndependent(dot.damagePerTick, dot.actualTickCount) : fixedValue(0);
const totalDamage = addValues(directDamage, dotDmg);
const totalPotency = record.ability.potency + ('dot' in record.ability ? record.ability.dot.tickPotency * record.dot.actualTickCount : 0);
const channel = record.channel;
const channelDmg = channel ? multiplyIndependent(channel.damagePerTick, channel.actualTickCount) : fixedValue(0);
const totalDamage = addValues(directDamage, dotDmg, channelDmg);
const totalPotency = record.ability.potency
+ ('dot' in record.ability ? record.ability.dot.tickPotency * record.dot.actualTickCount : 0)
+ ('channel' in record.ability ? record.ability.channel.tickPotency * record.channel.actualTickCount : 0);
return {
usedAt: record.usedAt,
original: record,
partialRate: partialRate == 1 ? null : partialRate,
directDamage: directDamage.expected,
directDamageFull: directDamage,
dotInfo: dot,
channelInfo: channel,
totalDamage: totalDamage.expected,
totalDamageFull: totalDamage,
totalPotency: totalPotency,
Expand Down Expand Up @@ -947,6 +956,7 @@ export class CycleProcessor {
const animLock = animationLock(ability);
const effectiveAnimLock = effectiveCastTime ? Math.max(effectiveCastTime + CASTER_TAX, animLock) : animLock;
const animLockFinishedAt = this.currentTime + effectiveAnimLock;
const channelFinishedAt = 'channel' in ability ? this.currentTime + ability.channel.duration : undefined;
this.advanceTo(snapshotsAt, true);
const {
buffs,
Expand Down Expand Up @@ -979,6 +989,7 @@ export class CycleProcessor {
buffs: finalBuffs,
usedAt: gcdStartsAt,
dot: dmgInfo.dot,
channel: dmgInfo.channel,
appDelay: appDelayFromSnapshot,
appDelayFromStart: appDelayFromStart,
totalTimeTaken: Math.max(effectiveAnimLock, abilityGcd),
Expand All @@ -998,15 +1009,22 @@ export class CycleProcessor {
if (ability.activatesBuffs) {
ability.activatesBuffs.forEach(buff => this.activateBuffWithDelay(buff, buffDelay));
}
// Anim lock OR cast time, both effectively block use of skills.
// If cast time > GCD recast, then we use that instead. Also factor in caster tax.
this.advanceTo(animLockFinishedAt);
// if this is a channeled ability, then we need to advance to the end of the channel time without auto-attacks
if ('channel' in ability) {
this.advanceTo(channelFinishedAt, true);
} else {
// Anim lock OR cast time, both effectively block use of skills.
// If cast time > GCD recast, then we use that instead. Also factor in caster tax.
this.advanceTo(animLockFinishedAt);
}
// If we're casting a long-cast, then the GCD is blocked for more than a GCD.
if (isGcd) {
this.nextGcdTime = Math.max(gcdFinishedAt, animLockFinishedAt);
}
// Account for potential GCD clipping
else {
else if ('channel' in ability) {
this.nextGcdTime = Math.max(this.nextGcdTime, channelFinishedAt);
} else {
this.nextGcdTime = Math.max(this.nextGcdTime, animLockFinishedAt);
}
// Workaround for auto-attacks after first ability
Expand Down Expand Up @@ -1261,6 +1279,12 @@ export class CycleProcessor {
this.dotMap.set(dotId, usedAbility);
}
}

if (usedAbility.channel) {
// TODO: handle cutting off a channel early due to end of sim time
// TODO: allow cutting off a channel early by other action use?
usedAbility.channel.actualTickCount = usedAbility.channel.fullDurationTicks;
}
}

/**
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/sims/processors/count_sim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ export abstract class BaseUsageCountSim<ResultType extends CountSimResult, Inter
console.debug(`Skill ${skill.name}, count ${count}, duration ${bucket.maxDuration}, total ${valueWithDev.expected}`);
result.push(valueWithDev);
}
if (dmg.channel) {
const valueWithDev = multiplyIndependent(dmg.channel.damagePerTick, dmg.channel.fullDurationTicks * count);
console.debug(`Skill ${skill.name}, count ${count}, duration ${bucket.maxDuration}, total ${valueWithDev.expected}`);
result.push(valueWithDev);
}
return result;
}));
});
Expand Down
26 changes: 24 additions & 2 deletions packages/core/src/sims/sim_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,14 @@ export type DotInfo = Readonly<{
id: number
}>;

/**
* Represents a channeled action.
*/
export type ChannelInfo = Readonly<{
duration: number,
tickPotency: number,
}>;

/**
* Represents combo-related data.
*
Expand Down Expand Up @@ -242,6 +250,7 @@ export type DamagingAbility = Readonly<{
autoCrit?: boolean,
autoDh?: boolean,
dot?: DotInfo,
channel?: ChannelInfo
}>;

/**
Expand Down Expand Up @@ -387,6 +396,12 @@ export type DotDamageUnf = {
actualTickCount?: number
};

export type ChannelDamageUnf = {
fullDurationTicks: number,
damagePerTick: ComputedDamage,
actualTickCount?: number
};

export type ComputedDamage = ValueWithDev;

/**
Expand Down Expand Up @@ -421,6 +436,10 @@ export type PreDmgUsedAbility = {
* If a DoT, the DoT damage
*/
dot?: DotDamageUnf,
/**
* If a channeled action, the channeled damage
*/
channel?: ChannelDamageUnf,
/**
* The total cast time from usedAt
*/
Expand Down Expand Up @@ -455,7 +474,8 @@ export type PreDmgUsedAbility = {

export type PostDmgUsedAbility = PreDmgUsedAbility & {
directDamage: ComputedDamage,
dot?: DotDamageUnf
dot?: DotDamageUnf,
channel?: ChannelDamageUnf
}
/**
* Represents a pseudo-ability used to round out a cycle to exactly 120s.
Expand All @@ -478,6 +498,7 @@ export type FinalizedAbility = {
directDamage: number,
directDamageFull: ComputedDamage,
dotInfo: DotDamageUnf,
channelInfo: ChannelDamageUnf,
combinedEffects: CombinedBuffEffect,
ability: Ability,
buffs: Buff[]
Expand Down Expand Up @@ -645,7 +666,8 @@ export type Buff = PersonalBuff | PartyBuff;

export type DamageResult = {
readonly directDamage: ComputedDamage | null,
readonly dot: DotDamageUnf | null
readonly dot: DotDamageUnf | null,
readonly channel: ChannelDamageUnf | null
}

/**
Expand Down
9 changes: 8 additions & 1 deletion packages/core/src/sims/sim_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ export function abilityToDamageNew(stats: ComputedSetStats, ability: Ability, co
if (!('potency' in ability)) {
return {
directDamage: null,
dot: null
dot: null,
channel: null
}
}
// noinspection AssignmentToFunctionParameterJS
Expand All @@ -62,6 +63,12 @@ export function abilityToDamageNew(stats: ComputedSetStats, ability: Ability, co
fullDurationTicks: ability.dot.duration === 'indefinite' ? 'indefinite' : (ability.dot.duration / 3),
damagePerTick: dotPotencyToDamage(stats, ability.dot.tickPotency, ability, combinedBuffEffects),
} : null,
channel: 'channel' in ability ? {
// channeled actions tick once on use, and once per second afterwards for the full duration
fullDurationTicks: ability.channel.duration + 1,
// channeled actions use the same damage formula as DoTs currently
damagePerTick: dotPotencyToDamage(stats, ability.channel.tickPotency, ability, combinedBuffEffects),
} : null,
}
}

Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/test/sims/cycle_processor_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,13 @@ function multiplyDamage(damageResult: DamageResult, multiplier: number, multiply
stdDev: 0
}
},
channel: (damageResult.channel === null || !multiplyDot) ? damageResult.channel : {
...damageResult.channel,
damagePerTick: {
expected: damageResult.directDamage.expected * multiplier,
stdDev: 0
}
},
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ export class AbilitiesUsedTable extends CustomTable<DisplayRecordFinalized> {
return document.createTextNode('--');
}
let text = used.totalDamage.toFixed(2);
if (used.partialRate !== null || (used.dotInfo && used.dotInfo.fullDurationTicks !== "indefinite" && used.dotInfo.actualTickCount < used.dotInfo.fullDurationTicks)) {
if (used.partialRate !== null
|| (used.dotInfo && used.dotInfo.fullDurationTicks !== "indefinite" && used.dotInfo.actualTickCount < used.dotInfo.fullDurationTicks)
|| (used.channelInfo && used.channelInfo.actualTickCount < used.channelInfo.fullDurationTicks)) {
text += '*';
}
return document.createTextNode(text);
Expand All @@ -150,6 +152,9 @@ export class AbilitiesUsedTable extends CustomTable<DisplayRecordFinalized> {
title.push(`This ability is a DoT. It dealt ${value.dotInfo.actualTickCount}/${value.dotInfo.fullDurationTicks} ticks of ${value.dotInfo.damagePerTick.expected.toFixed(3)} each.\n`);
}
}
if (value.channelInfo) {
title.push(`This ability is a channel. It dealt ${value.channelInfo.actualTickCount}/${value.channelInfo.fullDurationTicks} ticks of ${value.channelInfo.damagePerTick.expected.toFixed(3)} each.\n`);
}
if (title.length > 0) {
colElement.title = title.join('\n');
}
Expand Down

0 comments on commit 0c7bd11

Please sign in to comment.