From 73291133693095050046c1a14de5ef3f0240c660 Mon Sep 17 00:00:00 2001 From: Kayla Glick Date: Mon, 11 Mar 2024 23:49:47 -0400 Subject: [PATCH] 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; + } + } + } + } + } }