From e20e1e49ddd8c40a29c78125b90e16c972eb9d1c Mon Sep 17 00:00:00 2001
From: Kayla Glick <12898988+kayla-glick@users.noreply.github.com>
Date: Mon, 11 Mar 2024 23:56:37 -0400
Subject: [PATCH] A few UI bug fixes (#4233)
* update to correct wotlk classic text
* clean up a few things
---
ui/core/proto_utils/logs_parser.tsx | 584 ++++++++++++++++++----------
ui/scss/homepage/_homepage.scss | 357 ++++++++---------
2 files changed, 567 insertions(+), 374 deletions(-)
diff --git a/ui/core/proto_utils/logs_parser.tsx b/ui/core/proto_utils/logs_parser.tsx
index 351c7305e4..523c91cada 100644
--- a/ui/core/proto_utils/logs_parser.tsx
+++ b/ui/core/proto_utils/logs_parser.tsx
@@ -1,7 +1,7 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { element } from 'tsx-vanilla';
-import { RaidSimResult , ResourceType } from '../proto/api.js';
+import { RaidSimResult, ResourceType } from '../proto/api.js';
import { bucket, getEnumValues, stringComparator, sum } from '../utils.js';
import { ActionId } from './action_id.js';
import { resourceNames, stringToResourceType } from './names.js';
@@ -26,7 +26,12 @@ export class Entity {
}
equals(other: Entity) {
- return this.isTarget == other.isTarget && this.isPet == other.isPet && this.index == other.index && this.name == other.name;
+ return (
+ this.isTarget == other.isTarget &&
+ this.isPet == other.isPet &&
+ this.index == other.index &&
+ this.name == other.name
+ );
}
toString(): string {
@@ -43,7 +48,9 @@ export class Entity {
if (this.isTarget) {
return `[Target ${this.index + 1}]`;
} else if (this.isPet) {
- return `[${this.ownerName} ${this.index + 1}] - ${this.name}`;
+ return `[${this.ownerName} ${this.index + 1}] - ${
+ this.name
+ }`;
} else {
return `[${this.name} ${this.index + 1}]`;
}
@@ -54,7 +61,8 @@ export class Entity {
// 'Target 1' if a target,
// 'PlayerName (#1)' if a player, or
// 'PlayerName (#1) - PetName' if a pet.
- static parseRegex = /\[(Target (\d+))|(([a-zA-Z0-9]+) \(#(\d+)\) - ([a-zA-Z0-9\s]+))|(([a-zA-Z0-9\s]+) \(#(\d+)\))\]/g;
+ static parseRegex =
+ /\[(Target (\d+))|(([a-zA-Z0-9]+) \(#(\d+)\) - ([a-zA-Z0-9\s]+))|(([a-zA-Z0-9\s]+) \(#(\d+)\))\]/g;
static parseAll(str: string): Array {
return Array.from(str.matchAll(Entity.parseRegex)).map(match => {
if (match[1]) {
@@ -71,13 +79,13 @@ export class Entity {
}
interface SimLogParams {
- raw: string,
- logIndex: number,
- timestamp: number,
- source: Entity | null,
- target: Entity | null,
- actionId: ActionId | null,
- threat: number,
+ raw: string;
+ logIndex: number;
+ timestamp: number;
+ source: Entity | null;
+ target: Entity | null;
+ actionId: ActionId | null;
+ threat: number;
}
export class SimLog {
@@ -155,16 +163,25 @@ export class SimLog {
const seconds = Math.floor(positiveTimestamp - minutes * 60);
const milliseconds = ((positiveTimestamp - Math.floor(positiveTimestamp)) * 1000).toFixed();
- let formatted = `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}:${String(milliseconds).padStart(3, '0')}`;
+ let formatted = `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(
+ 2,
+ '0',
+ )}:${String(milliseconds).padStart(3, '0')}`;
if (this.timestamp < 0) {
- formatted = `-${formatted}`
+ formatted = `-${formatted}`;
}
- return formatted
+ return formatted;
}
protected newActionIdLink(): string {
const iconElem = ;
- const actionAnchor = {iconElem} {this.actionId!.name};
+ const actionAnchor = (
+
+
+ {iconElem} {this.actionId!.name}
+
+
+ );
this.actionId?.setBackground(iconElem as HTMLAnchorElement);
this.actionId?.setWowheadHref(actionAnchor as HTMLAnchorElement);
return actionAnchor.outerHTML;
@@ -173,46 +190,50 @@ export class SimLog {
static async parseAll(result: RaidSimResult): Promise> {
const lines = result.logs.split('\n');
- return Promise.all(lines.map((line, lineIndex) => {
- const params: SimLogParams = {
- raw: line,
- logIndex: lineIndex,
- timestamp: 0,
- source: null,
- target: null,
- actionId: null,
- threat: 0,
- };
-
- const threatMatch = line.match(/ \(Threat: (-?[0-9]+\.[0-9]+)\)/);
- if (threatMatch) {
- params.threat = parseFloat(threatMatch[1]);
- line = line.substring(0, threatMatch.index);
- }
-
- const match = line.match(/\[(-?[0-9]+\.[0-9]+)\]\w*(.*)/);
- if (!match || !match[1]) {
- return new SimLog(params);
- }
+ return Promise.all(
+ lines.map((line, lineIndex) => {
+ const params: SimLogParams = {
+ raw: line,
+ logIndex: lineIndex,
+ timestamp: 0,
+ source: null,
+ target: null,
+ actionId: null,
+ threat: 0,
+ };
- params.timestamp = parseFloat(match[1]);
- const remainder = match[2];
+ const threatMatch = line.match(/ \(Threat: (-?[0-9]+\.[0-9]+)\)/);
+ if (threatMatch) {
+ params.threat = parseFloat(threatMatch[1]);
+ line = line.substring(0, threatMatch.index);
+ }
- const entities = Entity.parseAll(remainder);
- params.source = entities[0] || null;
- params.target = entities[1] || null;
+ const match = line.match(/\[(-?[0-9]+\.[0-9]+)\]\w*(.*)/);
+ if (!match || !match[1]) {
+ return new SimLog(params);
+ }
- // Order from most to least common to reduce number of checks.
- return DamageDealtLog.parse(params)
- || ResourceChangedLog.parse(params)
- || AuraEventLog.parse(params)
- || AuraStacksChangeLog.parse(params)
- || MajorCooldownUsedLog.parse(params)
- || CastBeganLog.parse(params)
- || CastCompletedLog.parse(params)
- || StatChangeLog.parse(params)
- || Promise.resolve(new SimLog(params));
- }));
+ params.timestamp = parseFloat(match[1]);
+ const remainder = match[2];
+
+ const entities = Entity.parseAll(remainder);
+ params.source = entities[0] || null;
+ params.target = entities[1] || null;
+
+ // Order from most to least common to reduce number of checks.
+ return (
+ DamageDealtLog.parse(params) ||
+ ResourceChangedLog.parse(params) ||
+ AuraEventLog.parse(params) ||
+ AuraStacksChangeLog.parse(params) ||
+ MajorCooldownUsedLog.parse(params) ||
+ CastBeganLog.parse(params) ||
+ CastCompletedLog.parse(params) ||
+ StatChangeLog.parse(params) ||
+ Promise.resolve(new SimLog(params))
+ );
+ }),
+ );
}
isDamageDealt(): this is DamageDealtLog {
@@ -248,7 +269,9 @@ export class SimLog {
}
// Group events that happen at the same time.
- static groupDuplicateTimestamps(logs: Array): Array> {
+ static groupDuplicateTimestamps(
+ logs: Array,
+ ): Array> {
const grouped: Array> = [];
let curGroup: Array = [];
@@ -284,7 +307,22 @@ export class DamageDealtLog extends SimLog {
readonly partialResist2_4: boolean;
readonly partialResist3_4: boolean;
- constructor(params: SimLogParams, amount: number, type: string, miss: boolean, crit: boolean, crush: boolean, glance: boolean, dodge: boolean, parry: boolean, block: boolean, tick: boolean, partialResist1_4: boolean, partialResist2_4: boolean, partialResist3_4: boolean) {
+ constructor(
+ params: SimLogParams,
+ amount: number,
+ type: string,
+ miss: boolean,
+ crit: boolean,
+ crush: boolean,
+ glance: boolean,
+ dodge: boolean,
+ parry: boolean,
+ block: boolean,
+ tick: boolean,
+ partialResist1_4: boolean,
+ partialResist2_4: boolean,
+ partialResist3_4: boolean,
+ ) {
super(params);
this.amount = amount;
this.type = type;
@@ -323,15 +361,25 @@ export class DamageDealtLog extends SimLog {
result += 'Shield ';
}
- result += this.miss ? 'Miss'
- : this.dodge ? 'Dodge'
- : this.parry ? 'Parry'
- : this.glance ? 'Glance'
- : this.block ? (this.crit ? 'Critical Block' : 'Block')
- : this.crit ? 'Crit'
- : this.crush ? 'Crush'
- : this.tick ? 'Tick'
- : 'Hit';
+ result += this.miss
+ ? 'Miss'
+ : this.dodge
+ ? 'Dodge'
+ : this.parry
+ ? 'Parry'
+ : this.glance
+ ? 'Glance'
+ : this.block
+ ? this.crit
+ ? 'Critical Block'
+ : 'Block'
+ : this.crit
+ ? 'Crit'
+ : this.crush
+ ? 'Crush'
+ : this.tick
+ ? 'Tick'
+ : 'Hit';
result += ' ' + this.target?.toHTMLString();
if (!this.miss && !this.dodge && !this.parry) {
@@ -343,41 +391,48 @@ export class DamageDealtLog extends SimLog {
} else if (this.partialResist3_4) {
result += ' (75% Resist)';
}
- result += '.'
+ result += '.';
}
return result;
}
toString(includeTimestamp = true): string {
const threatPostfix = this.source?.isTarget ? '' : ` (${this.threat.toFixed(2)} Threat)`;
- return `${this.toStringPrefix(includeTimestamp)} ${this.newActionIdLink()} ${this.resultString()}${threatPostfix}`;
+ return `${this.toStringPrefix(
+ includeTimestamp,
+ )} ${this.newActionIdLink()} ${this.resultString()}${threatPostfix}`;
}
static parse(params: SimLogParams): Promise | null {
- const match = params.raw.match(/] (.*?) (tick )?((Miss)|(Hit)|(CriticalBlock)|(Crit)|(Crush)|(Glance)|(Dodge)|(Parry)|(Block))( \((\d+)% Resist\))?( for (\d+\.\d+) ((damage)|(healing)|(shielding)))?/);
+ const match = params.raw.match(
+ /] (.*?) (tick )?((Miss)|(Hit)|(CriticalBlock)|(Crit)|(Crush)|(Glance)|(Dodge)|(Parry)|(Block))( \((\d+)% Resist\))?( for (\d+\.\d+) ((damage)|(healing)|(shielding)))?/,
+ );
if (match) {
- return ActionId.fromLogString(match[1]).fill(params.source?.index).then(cause => {
- params.actionId = cause;
-
- const amount = match[16] ? parseFloat(match[16]) : 0;
- const type = match[17] || '';
-
- return new DamageDealtLog(
- params,
- amount,
- type,
- match[3] == 'Miss',
- match[3] == 'Crit' || match[3] == 'CriticalBlock',
- match[3] == 'Crush',
- match[3] == 'Glance',
- match[3] == 'Dodge',
- match[3] == 'Parry',
- match[3] == 'Block' || match[3] == 'CriticalBlock',
- Boolean(match[2]) && match[2].includes('tick'),
- match[14] == '25',
- match[14] == '50',
- match[14] == '75');
- });
+ return ActionId.fromLogString(match[1])
+ .fill(params.source?.index)
+ .then(cause => {
+ params.actionId = cause;
+
+ const amount = match[16] ? parseFloat(match[16]) : 0;
+ const type = match[17] || '';
+
+ return new DamageDealtLog(
+ params,
+ amount,
+ type,
+ match[3] == 'Miss',
+ match[3] == 'Crit' || match[3] == 'CriticalBlock',
+ match[3] == 'Crush',
+ match[3] == 'Glance',
+ match[3] == 'Dodge',
+ match[3] == 'Parry',
+ match[3] == 'Block' || match[3] == 'CriticalBlock',
+ Boolean(match[2]) && match[2].includes('tick'),
+ match[14] == '25',
+ match[14] == '50',
+ match[14] == '75',
+ );
+ });
} else {
return null;
}
@@ -427,15 +482,19 @@ export class DpsLog extends SimLog {
console.warn('NaN dps!');
}
- return new DpsLog({
- raw: '',
- logIndex: ddLogGroup[0].logIndex,
- timestamp: ddLogGroup[0].timestamp,
- source: ddLogGroup[0].source,
- target: null,
- actionId: null,
- threat: 0,
- }, dps, ddLogGroup);
+ return new DpsLog(
+ {
+ raw: '',
+ logIndex: ddLogGroup[0].logIndex,
+ timestamp: ddLogGroup[0].timestamp,
+ source: ddLogGroup[0].source,
+ target: null,
+ actionId: null,
+ threat: 0,
+ },
+ dps,
+ ddLogGroup,
+ );
});
}
}
@@ -445,7 +504,12 @@ export class ThreatLogGroup extends SimLog {
readonly threatAfter: number;
readonly logs: Array;
- constructor(params: SimLogParams, threatBefore: number, threatAfter: number, logs: Array) {
+ constructor(
+ params: SimLogParams,
+ threatBefore: number,
+ threatAfter: number,
+ logs: Array,
+ ) {
super(params);
this.threatBefore = threatBefore;
this.threatAfter = threatAfter;
@@ -469,7 +533,8 @@ export class ThreatLogGroup extends SimLog {
},
curThreat,
curThreat + newThreat,
- logGroup);
+ logGroup,
+ );
curThreat += newThreat;
return threatLog;
@@ -490,17 +555,26 @@ export class AuraEventLog extends SimLog {
}
toString(includeTimestamp = true): string {
- return `${this.toStringPrefix(includeTimestamp)} Aura ${this.isGained ? 'gained' : this.isFaded ? 'faded' : 'refreshed'}: ${this.newActionIdLink()}.`;
+ return `${this.toStringPrefix(includeTimestamp)} Aura ${
+ this.isGained ? 'gained' : this.isFaded ? 'faded' : 'refreshed'
+ }: ${this.newActionIdLink()}.`;
}
static parse(params: SimLogParams): Promise | null {
const match = params.raw.match(/Aura ((gained)|(faded)|(refreshed)): (.*)/);
if (match && match[5]) {
- return ActionId.fromLogString(match[5]).fill(params.source?.index).then(aura => {
- params.actionId = aura;
- const event = match[1];
- return new AuraEventLog(params, event == 'gained', event == 'faded', event == 'refreshed');
- });
+ return ActionId.fromLogString(match[5])
+ .fill(params.source?.index)
+ .then(aura => {
+ params.actionId = aura;
+ const event = match[1];
+ return new AuraEventLog(
+ params,
+ event == 'gained',
+ event == 'faded',
+ event == 'refreshed',
+ );
+ });
} else {
return null;
}
@@ -518,16 +592,20 @@ export class AuraStacksChangeLog extends SimLog {
}
toString(includeTimestamp = true): string {
- return `${this.toStringPrefix(includeTimestamp)} ${this.newActionIdLink()} stacks: ${this.oldStacks} → ${this.newStacks}.`;
+ return `${this.toStringPrefix(includeTimestamp)} ${this.newActionIdLink()} stacks: ${
+ this.oldStacks
+ } → ${this.newStacks}.`;
}
static parse(params: SimLogParams): Promise | null {
const match = params.raw.match(/(.*) stacks: ([0-9]+) --> ([0-9]+)/);
if (match && match[1]) {
- return ActionId.fromLogString(match[1]).fill(params.source?.index).then(aura => {
- params.actionId = aura;
- return new AuraStacksChangeLog(params, parseInt(match[2]), parseInt(match[3]));
- });
+ return ActionId.fromLogString(match[1])
+ .fill(params.source?.index)
+ .then(aura => {
+ params.actionId = aura;
+ return new AuraStacksChangeLog(params, parseInt(match[2]), parseInt(match[3]));
+ });
} else {
return null;
}
@@ -546,8 +624,15 @@ export class AuraUptimeLog extends SimLog {
this.stacksChange = stacksChange;
}
- static fromLogs(logs: Array, entity: Entity, encounterDuration: number): Array {
- const unmatchedGainedLogs: Array<{ gained: AuraEventLog, stacks: Array }> = [];
+ static fromLogs(
+ logs: Array,
+ entity: Entity,
+ encounterDuration: number,
+ ): Array {
+ const unmatchedGainedLogs: Array<{
+ gained: AuraEventLog;
+ stacks: Array;
+ }> = [];
const uptimeLogs: Array = [];
logs.forEach((log: SimLog) => {
@@ -556,7 +641,9 @@ export class AuraUptimeLog extends SimLog {
}
if (log.isAuraStacksChange()) {
- const matchingGainedIdx = unmatchedGainedLogs.findIndex(gainedLog => gainedLog.gained.actionId!.equals(log.actionId!));
+ const matchingGainedIdx = unmatchedGainedLogs.findIndex(gainedLog =>
+ gainedLog.gained.actionId!.equals(log.actionId!),
+ );
if (matchingGainedIdx == -1) {
console.warn('Unmatched aura stacks change log: ' + log.actionId!.name);
return;
@@ -574,22 +661,33 @@ export class AuraUptimeLog extends SimLog {
return;
}
- const matchingGainedIdx = unmatchedGainedLogs.findIndex(gainedLog => gainedLog.gained.actionId!.equals(log.actionId!));
+ const matchingGainedIdx = unmatchedGainedLogs.findIndex(gainedLog =>
+ gainedLog.gained.actionId!.equals(log.actionId!),
+ );
if (matchingGainedIdx == -1) {
console.warn('Unmatched aura faded log: ' + log.actionId!.name);
return;
}
- const { gained: gainedLog, stacks: stacksChangeLogs } = unmatchedGainedLogs.splice(matchingGainedIdx, 1)[0];
-
- uptimeLogs.push(new AuraUptimeLog({
- raw: log.raw,
- logIndex: gainedLog.logIndex,
- timestamp: gainedLog.timestamp,
- source: log.source,
- target: log.target,
- actionId: gainedLog.actionId,
- threat: gainedLog.threat,
- }, log.timestamp, stacksChangeLogs));
+ const { gained: gainedLog, stacks: stacksChangeLogs } = unmatchedGainedLogs.splice(
+ matchingGainedIdx,
+ 1,
+ )[0];
+
+ uptimeLogs.push(
+ new AuraUptimeLog(
+ {
+ raw: log.raw,
+ logIndex: gainedLog.logIndex,
+ timestamp: gainedLog.timestamp,
+ source: log.source,
+ target: log.target,
+ actionId: gainedLog.actionId,
+ threat: gainedLog.threat,
+ },
+ log.timestamp,
+ stacksChangeLogs,
+ ),
+ );
if (log.isRefreshed) {
unmatchedGainedLogs.push({ gained: log, stacks: [] });
@@ -599,15 +697,21 @@ export class AuraUptimeLog extends SimLog {
// Auras active at the end won't have a faded log, so need to add them separately.
unmatchedGainedLogs.forEach(unmatchedLog => {
const { gained: gainedLog, stacks: stacksChangeLogs } = unmatchedLog;
- uptimeLogs.push(new AuraUptimeLog({
- raw: gainedLog.raw,
- logIndex: gainedLog.logIndex,
- timestamp: gainedLog.timestamp,
- source: gainedLog.source,
- target: gainedLog.target,
- actionId: gainedLog.actionId,
- threat: gainedLog.threat,
- }, encounterDuration, stacksChangeLogs));
+ uptimeLogs.push(
+ new AuraUptimeLog(
+ {
+ raw: gainedLog.raw,
+ logIndex: gainedLog.logIndex,
+ timestamp: gainedLog.timestamp,
+ source: gainedLog.source,
+ target: gainedLog.target,
+ actionId: gainedLog.actionId,
+ threat: gainedLog.threat,
+ },
+ encounterDuration,
+ stacksChangeLogs,
+ ),
+ );
});
uptimeLogs.sort((a, b) => a.gainedAt - b.gainedAt);
@@ -620,7 +724,10 @@ export class AuraUptimeLog extends SimLog {
let auraLogsIndex = 0;
logs.forEach(log => {
- while (auraLogsIndex < auraLogs.length && auraLogs[auraLogsIndex].gainedAt <= log.timestamp) {
+ while (
+ auraLogsIndex < auraLogs.length &&
+ auraLogs[auraLogsIndex].gainedAt <= log.timestamp
+ ) {
curAuras.push(auraLogs[auraLogsIndex]);
auraLogsIndex++;
}
@@ -639,7 +746,13 @@ export class ResourceChangedLog extends SimLog {
readonly valueAfter: number;
readonly isSpend: boolean;
- constructor(params: SimLogParams, resourceType: ResourceType, valueBefore: number, valueAfter: number, isSpend: boolean) {
+ constructor(
+ params: SimLogParams,
+ resourceType: ResourceType,
+ valueBefore: number,
+ valueAfter: number,
+ isSpend: boolean,
+ ) {
super(params);
this.resourceType = resourceType;
this.valueBefore = valueBefore;
@@ -651,11 +764,23 @@ export class ResourceChangedLog extends SimLog {
const signedDiff = (this.valueAfter - this.valueBefore) * (this.isSpend ? -1 : 1);
const isHealth = this.resourceType == ResourceType.ResourceTypeHealth;
- const verb = isHealth ? (this.isSpend ? 'Lost' : 'Recovered') : (this.isSpend ? 'Spent' : 'Gained');
- const resourceName = resourceNames.get(this.resourceType)!
- const resourceKlass = `resource-${resourceName.replace(/\s/g, "-").toLowerCase()}`;
-
- return `${this.toStringPrefix(includeTimestamp)} ${verb} ${signedDiff.toFixed(1)} ${resourceName} from ${this.newActionIdLink()}. (${this.valueBefore.toFixed(1)} → ${this.valueAfter.toFixed(1)})`;
+ const verb = isHealth
+ ? this.isSpend
+ ? 'Lost'
+ : 'Recovered'
+ : this.isSpend
+ ? 'Spent'
+ : 'Gained';
+ const resourceName = resourceNames.get(this.resourceType)!;
+ const resourceKlass = `resource-${resourceName.replace(/\s/g, '-').toLowerCase()}`;
+
+ return `${this.toStringPrefix(
+ includeTimestamp,
+ )} ${verb} ${signedDiff.toFixed(
+ 1,
+ )} ${resourceName} from ${this.newActionIdLink()}. (${this.valueBefore.toFixed(
+ 1,
+ )} → ${this.valueAfter.toFixed(1)})`;
}
resultString(): string {
@@ -668,13 +793,23 @@ export class ResourceChangedLog extends SimLog {
}
static parse(params: SimLogParams): Promise | null {
- const match = params.raw.match(/((Gained)|(Spent)) \d+\.?\d* ((health)|(mana)|(energy)|(focus)|(rage)|(combo points)) from (.*) \((\d+\.?\d*) --> (\d+\.?\d*)\)/);
+ const match = params.raw.match(
+ /((Gained)|(Spent)) \d+\.?\d* ((health)|(mana)|(energy)|(focus)|(rage)|(combo points)|(runic power)|(blood rune)|(frost rune)|(unholy rune)|(death rune)) from (.*) \((\d+\.?\d*) --> (\d+\.?\d*)\)/,
+ );
if (match) {
const resourceType = stringToResourceType(match[4]);
- return ActionId.fromLogString(match[11]).fill(params.source?.index).then(cause => {
- params.actionId = cause;
- return new ResourceChangedLog(params, resourceType, parseFloat(match[12]), parseFloat(match[13]), match[1] == 'Spent');
- });
+ return ActionId.fromLogString(match[16])
+ .fill(params.source?.index)
+ .then(cause => {
+ params.actionId = cause;
+ return new ResourceChangedLog(
+ params,
+ resourceType,
+ parseFloat(match[17]),
+ parseFloat(match[18]),
+ match[1] == 'Spent',
+ );
+ });
} else {
return null;
}
@@ -687,7 +822,13 @@ export class ResourceChangedLogGroup extends SimLog {
readonly valueAfter: number;
readonly logs: Array;
- constructor(params: SimLogParams, resourceType: ResourceType, valueBefore: number, valueAfter: number, logs: Array) {
+ constructor(
+ params: SimLogParams,
+ resourceType: ResourceType,
+ valueBefore: number,
+ valueAfter: number,
+ logs: Array,
+ ) {
super(params);
this.resourceType = resourceType;
this.valueBefore = valueBefore;
@@ -696,32 +837,44 @@ export class ResourceChangedLogGroup extends SimLog {
}
toString(includeTimestamp = true): string {
- return `${this.toStringPrefix(includeTimestamp)} ${resourceNames.get(this.resourceType)}: ${this.valueBefore.toFixed(1)} → ${this.valueAfter.toFixed(1)}`;
+ return `${this.toStringPrefix(includeTimestamp)} ${resourceNames.get(
+ this.resourceType,
+ )}: ${this.valueBefore.toFixed(1)} → ${this.valueAfter.toFixed(1)}`;
}
static fromLogs(logs: Array): Record> {
- const allResourceChangedLogs = logs.filter((log): log is ResourceChangedLog => log.isResourceChanged());
+ const allResourceChangedLogs = logs.filter((log): log is ResourceChangedLog =>
+ log.isResourceChanged(),
+ );
const results: Partial>> = {};
- const resourceTypes = (getEnumValues(ResourceType) as Array).filter(val => val != ResourceType.ResourceTypeNone);
+ const resourceTypes = (getEnumValues(ResourceType) as Array).filter(
+ val => val != ResourceType.ResourceTypeNone,
+ );
resourceTypes.forEach(resourceType => {
- const resourceChangedLogs = allResourceChangedLogs.filter(log => log.resourceType == resourceType);
+ const resourceChangedLogs = allResourceChangedLogs.filter(
+ log => log.resourceType == resourceType,
+ );
const groupedLogs = SimLog.groupDuplicateTimestamps(resourceChangedLogs);
- results[resourceType] = groupedLogs.map(logGroup => new ResourceChangedLogGroup(
- {
- raw: '',
- logIndex: logGroup[0].logIndex,
- timestamp: logGroup[0].timestamp,
- source: logGroup[0].source,
- target: logGroup[0].target,
- actionId: null,
- threat: 0,
- },
- resourceType,
- logGroup[0].valueBefore,
- logGroup[logGroup.length - 1].valueAfter,
- logGroup));
+ results[resourceType] = groupedLogs.map(
+ logGroup =>
+ new ResourceChangedLogGroup(
+ {
+ raw: '',
+ logIndex: logGroup[0].logIndex,
+ timestamp: logGroup[0].timestamp,
+ source: logGroup[0].source,
+ target: logGroup[0].target,
+ actionId: null,
+ threat: 0,
+ },
+ resourceType,
+ logGroup[0].valueBefore,
+ logGroup[logGroup.length - 1].valueAfter,
+ logGroup,
+ ),
+ );
});
return results as Record>;
@@ -734,16 +887,20 @@ export class MajorCooldownUsedLog extends SimLog {
}
toString(includeTimestamp = true): string {
- return `${this.toStringPrefix(includeTimestamp)} Major cooldown used: ${this.newActionIdLink()}.`;
+ return `${this.toStringPrefix(
+ includeTimestamp,
+ )} Major cooldown used: ${this.newActionIdLink()}.`;
}
static parse(params: SimLogParams): Promise | null {
const match = params.raw.match(/Major cooldown used: (.*)/);
if (match) {
- return ActionId.fromLogString(match[1]).fill(params.source?.index).then(cooldownId => {
- params.actionId = cooldownId;
- return new MajorCooldownUsedLog(params);
- });
+ return ActionId.fromLogString(match[1])
+ .fill(params.source?.index)
+ .then(cooldownId => {
+ params.actionId = cooldownId;
+ return new MajorCooldownUsedLog(params);
+ });
} else {
return null;
}
@@ -763,11 +920,17 @@ export class CastBeganLog extends SimLog {
}
toString(includeTimestamp = true): string {
- return `${this.toStringPrefix(includeTimestamp)} Casting ${this.newActionIdLink()} (Cast time: ${this.castTime.toFixed(2)}s, Cost: ${this.manaCost.toFixed(1)} Mana).`;
+ return `${this.toStringPrefix(
+ includeTimestamp,
+ )} Casting ${this.newActionIdLink()} (Cast time: ${this.castTime.toFixed(
+ 2,
+ )}s, Cost: ${this.manaCost.toFixed(1)} Mana).`;
}
static parse(params: SimLogParams): Promise | null {
- const match = params.raw.match(/Casting (.*) \(Cost = (\d+\.?\d*), Cast Time = (\d+\.?\d*)(m?s), Effective Time = (\d+\.?\d*)(m?s)\)/);
+ const match = params.raw.match(
+ /Casting (.*) \(Cost = (\d+\.?\d*), Cast Time = (\d+\.?\d*)(m?s), Effective Time = (\d+\.?\d*)(m?s)\)/,
+ );
if (match) {
let castTime = parseFloat(match[3]);
if (match[4] == 'ms') {
@@ -777,10 +940,12 @@ export class CastBeganLog extends SimLog {
if (match[6] == 'ms') {
effectiveTime /= 1000;
}
- return ActionId.fromLogString(match[1]).fill(params.source?.index).then(castId => {
- params.actionId = castId;
- return new CastBeganLog(params, parseFloat(match[2]), castTime, effectiveTime);
- });
+ return ActionId.fromLogString(match[1])
+ .fill(params.source?.index)
+ .then(castId => {
+ params.actionId = castId;
+ return new CastBeganLog(params, parseFloat(match[2]), castTime, effectiveTime);
+ });
} else {
return null;
}
@@ -799,10 +964,12 @@ export class CastCompletedLog extends SimLog {
static parse(params: SimLogParams): Promise | null {
const match = params.raw.match(/Completed cast (.*)/);
if (match) {
- return ActionId.fromLogString(match[1]).fill(params.source?.index).then(castId => {
- params.actionId = castId;
- return new CastCompletedLog(params);
- });
+ return ActionId.fromLogString(match[1])
+ .fill(params.source?.index)
+ .then(castId => {
+ params.actionId = castId;
+ return new CastCompletedLog(params);
+ });
} else {
return null;
}
@@ -820,7 +987,11 @@ export class CastLog extends SimLog {
// All instances of damage dealt from the completion of this cast until the completion of the next cast.
readonly damageDealtLogs: Array;
- constructor(castBeganLog: CastBeganLog, castCompletedLog: CastCompletedLog | null, damageDealtLogs: Array) {
+ constructor(
+ castBeganLog: CastBeganLog,
+ castCompletedLog: CastCompletedLog | null,
+ damageDealtLogs: Array,
+ ) {
super({
raw: castBeganLog.raw,
logIndex: castBeganLog.logIndex,
@@ -837,13 +1008,16 @@ export class CastLog extends SimLog {
this.damageDealtLogs = damageDealtLogs;
if (this.castCompletedLog && this.castBeganLog) {
- this.castTime = this.castCompletedLog.timestamp - this.castBeganLog.timestamp
- this.effectiveTime = this.castCompletedLog.timestamp - this.castBeganLog.timestamp
+ this.castTime = this.castCompletedLog.timestamp - this.castBeganLog.timestamp;
+ this.effectiveTime = this.castCompletedLog.timestamp - this.castBeganLog.timestamp;
}
- if (this.castCompletedLog && this.damageDealtLogs.length == 1 &&
+ if (
+ this.castCompletedLog &&
+ this.damageDealtLogs.length == 1 &&
this.castCompletedLog.timestamp < this.damageDealtLogs[0].timestamp &&
- !this.damageDealtLogs[0].tick) {
+ !this.damageDealtLogs[0].tick
+ ) {
this.travelTime = this.damageDealtLogs[0].timestamp - this.castCompletedLog.timestamp;
} else {
this.travelTime = 0;
@@ -851,7 +1025,9 @@ export class CastLog extends SimLog {
}
toString(includeTimestamp = true): string {
- return `${this.toStringPrefix(includeTimestamp)} Casting ${this.actionId!.name} (Cast time = ${this.castTime.toFixed(2)}s).`;
+ return `${this.toStringPrefix(includeTimestamp)} Casting ${
+ this.actionId!.name
+ } (Cast time = ${this.castTime.toFixed(2)}s).`;
}
totalDamage(): number {
@@ -860,7 +1036,9 @@ export class CastLog extends SimLog {
static fromLogs(logs: Array): Array {
const castBeganLogs = logs.filter((log): log is CastBeganLog => log.isCastBegan());
- const castCompletedLogs = logs.filter((log): log is CastCompletedLog => log.isCastCompleted());
+ const castCompletedLogs = logs.filter((log): log is CastCompletedLog =>
+ log.isCastCompleted(),
+ );
const damageDealtLogs = logs.filter((log): log is DamageDealtLog => log.isDamageDealt());
const toBucketKey = (actionId: ActionId) => {
@@ -873,7 +1051,9 @@ export class CastLog extends SimLog {
}
};
const castBeganLogsByAbility = bucket(castBeganLogs, log => toBucketKey(log.actionId!));
- const castCompletedLogsByAbility = bucket(castCompletedLogs, log => toBucketKey(log.actionId!));
+ const castCompletedLogsByAbility = bucket(castCompletedLogs, log =>
+ toBucketKey(log.actionId!),
+ );
const damageDealtLogsByAbility = bucket(damageDealtLogs, log => toBucketKey(log.actionId!));
const castLogs: Array = [];
@@ -899,9 +1079,13 @@ export class CastLog extends SimLog {
// Find all damage dealt logs between the cur and next cast completed logs.
const ddLogs = [];
- while (abilityDamageDealt && ddIdx < abilityDamageDealt.length && (!nextCcLog || abilityDamageDealt[ddIdx].timestamp < nextCcLog.timestamp)) {
+ while (
+ abilityDamageDealt &&
+ ddIdx < abilityDamageDealt.length &&
+ (!nextCcLog || abilityDamageDealt[ddIdx].timestamp < nextCcLog.timestamp)
+ ) {
ddLogs.push(abilityDamageDealt[ddIdx]);
- ddIdx++
+ ddIdx++;
}
castLogs.push(new CastLog(cbLog, ccLog, ddLogs));
}
@@ -924,20 +1108,26 @@ export class StatChangeLog extends SimLog {
toString(includeTimestamp = true): string {
if (this.isGain) {
- return `${this.toStringPrefix(includeTimestamp)} Gained ${this.stats} from ${this.newActionIdLink()}.`;
+ return `${this.toStringPrefix(includeTimestamp)} Gained ${
+ this.stats
+ } from ${this.newActionIdLink()}.`;
} else {
- return `${this.toStringPrefix(includeTimestamp)} Lost ${this.stats} from fading ${this.newActionIdLink()}.`;
+ return `${this.toStringPrefix(includeTimestamp)} Lost ${
+ this.stats
+ } from fading ${this.newActionIdLink()}.`;
}
}
static parse(params: SimLogParams): Promise | null {
const match = params.raw.match(/((Gained)|(Lost)) ({.*}) from (fading )?(.*)/);
if (match) {
- return ActionId.fromLogString(match[6]).fill(params.source?.index).then(effectId => {
- params.actionId = effectId;
- const sign = match[1] == 'Lost' ? -1 : 1;
- return new StatChangeLog(params, sign == 1, match[4]);
- });
+ return ActionId.fromLogString(match[6])
+ .fill(params.source?.index)
+ .then(effectId => {
+ params.actionId = effectId;
+ const sign = match[1] == 'Lost' ? -1 : 1;
+ return new StatChangeLog(params, sign == 1, match[4]);
+ });
} else {
return null;
}
diff --git a/ui/scss/homepage/_homepage.scss b/ui/scss/homepage/_homepage.scss
index 48d50167d1..02b9ba3f9d 100644
--- a/ui/scss/homepage/_homepage.scss
+++ b/ui/scss/homepage/_homepage.scss
@@ -1,189 +1,192 @@
-@use "sass:map";
+@use 'sass:map';
.homepage-image {
- position: fixed;
- width: 100%;
- height: 100%;
- background-image: url("/wotlk/assets/img/wotlk.jpg");
- background-repeat: no-repeat;
- background-size: cover;
- z-index: -1;
+ position: fixed;
+ width: 100%;
+ height: 100%;
+ background-image: url('/wotlk/assets/img/wotlk.jpg');
+ background-repeat: no-repeat;
+ background-size: cover;
+ z-index: -1;
}
#homepage {
- height: 100%;
- display: flex;
- flex-direction: column;
-
-
- .homepage-header-container {
- padding-top: calc(var(--container-padding) / 2);
- }
-
- .homepage-content-container {
- padding-top: var(--container-padding);
- padding-bottom: var(--container-padding);
- }
-
- .homepage-header {
- .homepage-header-container {
- display: flex;
-
- .navbar-brand-container {
- display: flex;
-
- .wowsims-logo {
- width: 6rem;
- margin-right: map-get($spacers, 3);
- }
-
- .wowsims-title {
- @extend .display-1;
- @extend .fw-bold;
- color: $brand;
- margin: 0;
- line-height: 1;
- }
- }
-
- .homepage-header-collapse {
- padding-top: map-get($spacers, 3);
- padding-bottom: map-get($spacers, 3);
- align-items: flex-end;
- justify-content: flex-end;
- }
- }
-
- .expansion-title {
- color: $wrath-blue;
- margin: 0;
- }
- }
-
- .homepage-content-container {
- flex-direction: column;
-
- .info-container {
- margin-bottom: var(--container-padding);
- display: flex;
- flex-direction: column;
-
- .wowsims-info {
- @extend .fs-4;
- width: 75%;
- margin: 0;
- }
- }
-
- .sim-links-container {
- display: flex;
- flex-direction: column;
-
- .sim-links {
- display: flex;
- flex-wrap: wrap;
-
- &:not(:last-child) {
- margin-bottom: map-get($spacers, 5);
- }
-
- &> .sim-link, &> .sim-link-dropdown {
- max-width: 25%;
- flex: 1 25%;
- margin-right: 25%;
- }
-
- .raid-sim-link {
- flex-grow: 0 !important;
- }
-
- .sim-link {
- .sim-link-content {
- .sim-link-label:first-of-type {
- font-size: 1rem;
- }
-
- .sim-link-label:last-of-type {
- font-size: 1.75rem;
- }
- }
- }
- }
- }
- }
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+
+ .homepage-header-container {
+ padding-top: calc(var(--container-padding) / 2);
+ }
+
+ .homepage-content-container {
+ padding-top: var(--container-padding);
+ padding-bottom: var(--container-padding);
+ }
+
+ .homepage-header {
+ .homepage-header-container {
+ display: flex;
+
+ .navbar-brand-container {
+ display: flex;
+
+ .wowsims-logo {
+ width: 6rem;
+ margin-right: map-get($spacers, 3);
+ }
+
+ .wowsims-title {
+ @extend .display-1;
+ @extend .fw-bold;
+ color: $brand;
+ margin: 0;
+ line-height: 1;
+ }
+ }
+
+ .homepage-header-collapse {
+ padding-top: map-get($spacers, 3);
+ padding-bottom: map-get($spacers, 3);
+ align-items: flex-end;
+ justify-content: flex-end;
+ }
+ }
+
+ .expansion-title {
+ color: $wrath-blue;
+ margin: 0;
+ }
+ }
+
+ .homepage-content-container {
+ flex-direction: column;
+
+ .info-container {
+ margin-bottom: var(--container-padding);
+ display: flex;
+ flex-direction: column;
+
+ .wowsims-info {
+ @extend .fs-4;
+ width: 75%;
+ margin: 0;
+ }
+ }
+
+ .sim-links-container {
+ display: flex;
+ flex-direction: column;
+
+ .sim-links {
+ display: flex;
+ flex-wrap: wrap;
+
+ &:not(:last-child) {
+ margin-bottom: map-get($spacers, 5);
+ }
+
+ & > .sim-link,
+ & > .sim-link-dropdown {
+ max-width: 25%;
+ flex: 1 25%;
+ margin-right: 25%;
+ }
+
+ .raid-sim-link {
+ flex-grow: 0 !important;
+ }
+
+ .sim-link {
+ .sim-link-content {
+ .sim-link-label:first-of-type {
+ font-size: 1rem;
+ }
+
+ .sim-link-label:last-of-type {
+ font-size: 1.75rem;
+ }
+ }
+ }
+ }
+ }
+ }
}
@include media-breakpoint-down(lg) {
- #homepage {
- .homepage-content-container {
- .info-container {
- .wowsims-info {
- width: 100%;
- }
- }
-
- .sim-links-container {
- margin-left: map-get($spacers, 3) * -1;
- margin-right: map-get($spacers, 3) * -1;
-
- .sim-links {
- margin-bottom: 0 !important;
-
- .sim-link, .sim-link-dropdown {
- flex: 1 50%;
- margin-right: 0 !important;
- }
-
- .sim-link-dropdown {
- .dropdown-menu {
- position: relative !important;
- inset: 0 !important;
- transform: none !important;
- }
- }
- }
- }
- }
- }
+ #homepage {
+ .homepage-content-container {
+ .info-container {
+ .wowsims-info {
+ width: 100%;
+ }
+ }
+
+ .sim-links-container {
+ margin-left: map-get($spacers, 3) * -1;
+ margin-right: map-get($spacers, 3) * -1;
+
+ .sim-links {
+ margin-bottom: 0 !important;
+
+ .sim-link,
+ .sim-link-dropdown {
+ max-width: 50%;
+ flex: 1 50%;
+ margin-right: 0 !important;
+ }
+
+ .sim-link-dropdown {
+ .dropdown-menu {
+ position: relative !important;
+ inset: 0 !important;
+ transform: none !important;
+ }
+ }
+ }
+ }
+ }
+ }
}
@include media-breakpoint-down(md) {
- #homepage {
- .homepage-header-container,
- .homepage-content-container,
- .homepage-footer-container {
- padding-top: map-get($spacers, 3);
- padding-bottom: map-get($spacers, 3);
- }
-
- .homepage-header {
- .homepage-header-container {
- display: flex;
-
- .navbar-brand-container {
- width: 100%;
- justify-content: space-between;
- align-items: flex-end;
-
- .wowsims-logo {
- width: 48px;
- }
- }
- }
- }
-
- .homepage-content-container {
- .info-container {
- margin-bottom: map-get($spacers, 3);
- }
-
- .sim-links-container {
- .sim-links {
- .sim-link, .sim-link-dropdown {
- flex: 1 100% !important;
- }
- }
- }
- }
- }
+ #homepage {
+ .homepage-header-container,
+ .homepage-content-container,
+ .homepage-footer-container {
+ padding-top: map-get($spacers, 3);
+ padding-bottom: map-get($spacers, 3);
+ }
+
+ .homepage-header {
+ .homepage-header-container {
+ display: flex;
+
+ .navbar-brand-container {
+ width: 100%;
+ justify-content: space-between;
+ align-items: flex-end;
+
+ .wowsims-logo {
+ width: 48px;
+ }
+ }
+ }
+ }
+
+ .homepage-content-container {
+ .info-container {
+ margin-bottom: map-get($spacers, 3);
+ }
+
+ .sim-links-container {
+ .sim-links {
+ .sim-link,
+ .sim-link-dropdown {
+ flex: 1 100% !important;
+ }
+ }
+ }
+ }
+ }
}