import {Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges, ViewChild} from '@angular/core'; import {ChartUtils} from '../../../utils/chart-utils'; import {War} from '../../../models/model-interfaces'; import {TranslateService} from '@ngx-translate/core'; import {FractionHelpers} from '../../../utils/global.helpers'; @Component({ selector: 'war-detail-fraction', templateUrl: './fraction-stats.component.html', styleUrls: ['./fraction-stats.component.scss'] }) export class FractionStatsComponent implements OnInit, OnChanges { @ViewChild('overview') private overviewContainer: ElementRef; @Input() war: War; @Input() logData: any; @Input() isSmallLayout: boolean; startDateObj: Date; initialized: any; public activeChartSelect: string; lineChartData: any[] = []; barChartData: any[] = []; tmpPointData; tmpBudgetData; tmpKillData; tmpFrienlyFireData; tmpVehicleData; tmpTransportData; tmpReviveData; tmpStabilizeData; tmpPlayerCountData; colorScheme; fractionNameBlufor; fractionNameOpfor; labels; labelsKeys; chartYAxisLabel: string; // line chart showLineChart = true; gradient = false; yAxis = true; xAxis = true; legend = false; legendTitle = false; showXAxisLabel = false; showYAxisLabel = true; autoscale = true; timeline = false; roundDomains = true; // bar chart barPadding = 0; barAnimations = false; constructor(private translate: TranslateService) { } ngOnInit() { this.setChartYAxisLabel(this.labels.points); } ngOnChanges(changes: SimpleChanges) { if (changes.war || changes.logData) { this.fractionNameBlufor = FractionHelpers.getFractionName(this.war, 'BLUFOR'); this.fractionNameOpfor = FractionHelpers.getFractionName(this.war, 'OPFOR'); this.colorScheme = { domain: [ FractionHelpers.getFractionColor('BLUFOR', this.war), FractionHelpers.getFractionColor('OPFOR', this.war), FractionHelpers.getFractionColor('BLUFOR', this.war, 'LIGHT'), FractionHelpers.getFractionColor('OPFOR', this.war, 'LIGHT'), FractionHelpers.getFractionColor('BLUFOR', this.war, 'DARK'), FractionHelpers.getFractionColor('OPFOR', this.war, 'DARK'), FractionHelpers.getFractionColor('BLUFOR', this.war, 'GREY'), FractionHelpers.getFractionColor('OPFOR', this.war, 'GREY'), ] }; this.initialized = { budget: false, kill: false, revive: false, transport: false, flag: false, playerCount: false }; this.initializeToggleButtons(); Object.assign(this, [this.lineChartData, this.barChartData]); this.activeChartSelect = this.labels.points; this.startDateObj = new Date(this.war.date); this.startDateObj.setHours(0); this.startDateObj.setMinutes(1); this.loadFractionData(); } } /** * show only labels for for data that is available, * to not end up with empty charts in old battles */ initializeToggleButtons() { const newLabels = {}; if (this.logData.points && this.logData.points.length > 0) { newLabels['points'] = 'stats.fraction.select.points'; } if (this.logData.budget && this.logData.budget.length > 0) { newLabels['budget'] = 'stats.fraction.select.budget'; } if (this.logData.kill && this.logData.kill.length > 0) { newLabels['kill'] = 'stats.fraction.select.kills'; newLabels['friendlyFire'] = 'stats.fraction.select.friendly.fire'; } if (this.logData.vehicle && this.logData.vehicle.length > 0) { newLabels['vehicle'] = 'stats.fraction.select.vehicle.kills'; } if (this.logData.transport && this.logData.transport.length > 0) { newLabels['transport'] = 'stats.fraction.select.air.transport'; } if (this.logData.revive && this.logData.revive.length > 0) { newLabels['revive'] = 'stats.fraction.select.revive'; newLabels['stabilize'] = 'stats.fraction.select.stabilize'; } if (this.logData.flag && this.logData.flag.length > 0) { newLabels['flag'] = 'stats.fraction.select.flag'; } if (this.logData.playerCount && this.logData.playerCount.length > 0) { newLabels['playerCount'] = 'stats.fraction.select.player.count'; } this.labels = newLabels; this.labelsKeys = Object.keys(this.labels); } selectChart(newSelection) { this.activeChartSelect = newSelection; this.setChartYAxisLabel(this.activeChartSelect); if (this.activeChartSelect !== this.labels.flag) { this.showLineChart = true; switch (this.activeChartSelect) { case this.labels.points: this.lineChartData = this.tmpPointData; break; case this.labels.budget: this.initBudgetData(); this.lineChartData = this.tmpBudgetData; break; case this.labels.kill: this.initKillData(); this.lineChartData = this.tmpKillData; break; case this.labels.friendlyFire: this.initKillData(); this.lineChartData = this.tmpFrienlyFireData; break; case this.labels.vehicle: this.initVehicleData(); this.lineChartData = this.tmpVehicleData; break; case this.labels.revive: this.initRevive(); this.lineChartData = this.tmpReviveData; break; case this.labels.stabilize: this.initRevive(); this.lineChartData = this.tmpStabilizeData; break; case this.labels.transport: this.initTransportData(); this.lineChartData = this.tmpTransportData; break; case this.labels.playerCount: this.initPlayerCountData(); this.lineChartData = this.tmpPlayerCountData; break; } } else { this.initFlagHoldData(); this.showLineChart = false; } } loadFractionData() { this.initializeTempCollections(); this.initPointData(); this.showLineChart = true; this.lineChartData = this.tmpPointData; } initPointData() { this.logData.points.forEach(pointEntry => { this.tmpPointData[0].series.push( ChartUtils.getSeriesEntry(new Date(pointEntry.time), pointEntry.ptBlufor)); this.tmpPointData[1].series.push( ChartUtils.getSeriesEntry(new Date(pointEntry.time), pointEntry.ptOpfor)); }); this.addFinalTimeData(this.tmpPointData); } initBudgetData() { if (this.initialized.budget) { return; } this.logData.budget.forEach(budgetEntry => { const budgetEntryDate = new Date(budgetEntry.time); const fractionChange = budgetEntry.fraction === 'BLUFOR' ? 0 : 1; const fractionOld = budgetEntry.fraction !== 'BLUFOR' ? 0 : 1; if (ChartUtils.isOneMinuteAhead(budgetEntryDate, this.tmpBudgetData)) { this.tmpBudgetData[fractionChange].series.push(ChartUtils.getSeriesEntry(new Date(budgetEntry.time), budgetEntry.newBudget)); this.tmpBudgetData[fractionOld].series.push(ChartUtils.getSeriesEntry(new Date(budgetEntry.time), this.tmpBudgetData[fractionOld].series[this.tmpBudgetData[fractionOld].series.length - 1].value)); } }); this.addFinalTimeData(this.tmpBudgetData); this.initialized.budget = true; } initKillData() { if (this.initialized.kill) { return; } let killCountBlufor = 0, killCountOpfor = 0, ffKillCountBlufor = 0, ffKillCountOpfor = 0; for (const {killEntry, index} of this.logData.kill.map((kill, pos) => ({killEntry: kill, index: pos}))) { const killEntryDate = new Date(killEntry.time); if (killEntry.friendlyFire === false) { if (killEntry.fraction === 'BLUFOR') { killCountBlufor++; } if (killEntry.fraction === 'OPFOR') { killCountOpfor++; } if (ChartUtils.isOneMinuteAhead(killEntryDate, this.tmpKillData)) { this.tmpKillData[0].series.push(ChartUtils.getSeriesEntry(killEntryDate, killCountBlufor)); this.tmpKillData[1].series.push(ChartUtils.getSeriesEntry(killEntryDate, killCountOpfor)); } } else { if (killEntry.fraction === 'BLUFOR') { ffKillCountBlufor++; } if (killEntry.fraction === 'OPFOR') { ffKillCountOpfor++; } if (ChartUtils.isOneMinuteAhead(killEntryDate, this.tmpFrienlyFireData)) { this.tmpFrienlyFireData[0].series.push(ChartUtils.getSeriesEntry(killEntryDate, ffKillCountBlufor)); this.tmpFrienlyFireData[1].series.push(ChartUtils.getSeriesEntry(killEntryDate, ffKillCountOpfor)); } } if (index === this.logData.kill.length - 1) { this.tmpKillData[0].series.push(ChartUtils.getSeriesEntry(killEntryDate, killCountBlufor)); this.tmpKillData[1].series.push(ChartUtils.getSeriesEntry(killEntryDate, killCountOpfor)); this.tmpFrienlyFireData[0].series.push(ChartUtils.getSeriesEntry(killEntryDate, ffKillCountBlufor)); this.tmpFrienlyFireData[1].series.push(ChartUtils.getSeriesEntry(killEntryDate, ffKillCountOpfor)); } } this.addFinalTimeData(this.tmpKillData); this.addFinalTimeData(this.tmpFrienlyFireData); this.initialized.kill = true; } initRevive() { if (this.initialized.revive) { return; } let reviveCountBlufor = 0, reviveCountOpfor = 0, stabilizeCountBlufor = 0, stabilizeCountOpfor = 0; for (const {reviveEntry, index} of this.logData.revive.map((revive, pos) => ({reviveEntry: revive, index: pos}))) { const reviveEntryDate = new Date(reviveEntry.time); if (reviveEntry.stabilized === false) { if (reviveEntry.fraction === 'BLUFOR') { reviveCountBlufor++; } else { reviveCountOpfor++; } if (ChartUtils.isOneMinuteAhead(reviveEntryDate, this.tmpReviveData)) { this.tmpReviveData[0].series.push(ChartUtils.getSeriesEntry(reviveEntryDate, reviveCountBlufor)); this.tmpReviveData[1].series.push(ChartUtils.getSeriesEntry(reviveEntryDate, reviveCountOpfor)); } } else { if (reviveEntry.fraction === 'BLUFOR') { stabilizeCountBlufor++; } else { stabilizeCountOpfor++; } if (ChartUtils.isOneMinuteAhead(reviveEntryDate, this.tmpStabilizeData)) { this.tmpStabilizeData[0].series.push(ChartUtils.getSeriesEntry(reviveEntryDate, stabilizeCountBlufor)); this.tmpStabilizeData[1].series.push(ChartUtils.getSeriesEntry(reviveEntryDate, stabilizeCountOpfor)); } } if (index === this.logData.revive.length - 1) { this.tmpReviveData[0].series.push(ChartUtils.getSeriesEntry(reviveEntryDate, reviveCountBlufor)); this.tmpReviveData[1].series.push(ChartUtils.getSeriesEntry(reviveEntryDate, reviveCountOpfor)); this.tmpStabilizeData[0].series.push(ChartUtils.getSeriesEntry(reviveEntryDate, stabilizeCountBlufor)); this.tmpStabilizeData[1].series.push(ChartUtils.getSeriesEntry(reviveEntryDate, stabilizeCountOpfor)); } } this.addFinalTimeData(this.tmpReviveData); this.addFinalTimeData(this.tmpStabilizeData); this.initialized.revive = true; } initVehicleData() { if (this.initialized.vehicle) { return; } let vehicleKillCountBlufor = 0, vehicleKillCountOpfor = 0, vehicleLightCountBlufor = 0, vehicleHeavyCountBlufor = 0, vehicleAirCountBlufor = 0, vehicleLightCountOpfor = 0, vehicleHeavyCountOpfor = 0, vehicleAirCountOpfor = 0; for (const {transportEntry: vehicleEntry, index} of this.logData.vehicle.map((transport, pos) => ({ transportEntry: transport, index: pos }))) { const vehicleEntryDate = new Date(vehicleEntry.time); if (vehicleEntry.fraction === 'BLUFOR') { vehicleKillCountBlufor++; if (vehicleEntry.vehicleClass) { switch (vehicleEntry.vehicleClass) { case 'LIGHT': vehicleLightCountBlufor++; break; case 'HEAVY': vehicleHeavyCountBlufor++; break; case 'AIR': vehicleAirCountBlufor++; break; } } } else { vehicleKillCountOpfor++; if (vehicleEntry.vehicleClass) { switch (vehicleEntry.vehicleClass) { case 'LIGHT': vehicleLightCountOpfor++; break; case 'HEAVY': vehicleHeavyCountOpfor++; break; case 'AIR': vehicleAirCountOpfor++; break; } } } if (ChartUtils.isOneMinuteAhead(vehicleEntryDate, this.tmpVehicleData) || index === this.logData.vehicle.length - 1) { this.tmpVehicleData[0].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleKillCountBlufor)); this.tmpVehicleData[1].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleKillCountOpfor)); if (vehicleEntry.vehicleClass) { this.tmpVehicleData[2].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleLightCountBlufor)); this.tmpVehicleData[3].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleLightCountOpfor)); this.tmpVehicleData[4].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleHeavyCountBlufor)); this.tmpVehicleData[5].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleHeavyCountOpfor)); this.tmpVehicleData[6].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleAirCountBlufor)); this.tmpVehicleData[7].series.push(ChartUtils.getSeriesEntry(vehicleEntryDate, vehicleAirCountOpfor)); } } } this.addFinalTimeData(this.tmpVehicleData); this.initialized.vehicle = true; } initTransportData() { if (this.initialized.transport) { return; } let transportCountBlufor = 0, transportCountOpfor = 0; for (const {transportEntry, index} of this.logData.transport.map((transport, pos) => ({ transportEntry: transport, index: pos }))) { const transportEntryDate = new Date(transportEntry.time); if (transportEntry.fraction === 'BLUFOR') { transportCountBlufor++; } else { transportCountOpfor++; } if (ChartUtils.isOneMinuteAhead(transportEntryDate, this.tmpTransportData) || index === this.logData.transport.length - 1) { this.tmpTransportData[0].series.push(ChartUtils.getSeriesEntry(transportEntryDate, transportCountBlufor)); this.tmpTransportData[1].series.push(ChartUtils.getSeriesEntry(transportEntryDate, transportCountOpfor)); } } this.addFinalTimeData(this.tmpTransportData); this.initialized.transport = true; } initPlayerCountData() { if (this.initialized.playerCount) { return; } this.logData.playerCount.forEach(playerCountEntry => { this.tmpPlayerCountData[0].series.push( ChartUtils.getSeriesEntry(new Date(playerCountEntry.time), playerCountEntry.countBlufor)); this.tmpPlayerCountData[1].series.push( ChartUtils.getSeriesEntry(new Date(playerCountEntry.time), playerCountEntry.countOpfor)); }); this.initialized.playerCount = true; this.addFinalTimeData(this.tmpPlayerCountData); } initFlagHoldData() { if (this.initialized.flag) { return; } let flagStatusBlufor = true; let flagStatusOpfor = true; const flagStatusMap = { blufor: {0: flagStatusBlufor}, opfor: {0: flagStatusOpfor}, }; this.logData.flag.forEach((flagEntry) => { if (flagEntry.flagFraction === 'BLUFOR') { flagStatusBlufor = !flagEntry.capture; } else { flagStatusOpfor = !flagEntry.capture; } const entryMinute = Math.round((new Date(flagEntry.time).getTime() - new Date(this.war.date).getTime()) / 60000); flagStatusMap.blufor[entryMinute] = flagStatusBlufor; flagStatusMap.opfor[entryMinute] = flagStatusOpfor; }); const data = []; let lastExistingIdx = 0; const finishMinute = Math.round((new Date(this.war.endDate).getTime() - new Date(this.war.date).getTime()) / 60000); for (let t = 0; t <= finishMinute; t++) { if (flagStatusMap.blufor.hasOwnProperty(t) && flagStatusMap.opfor.hasOwnProperty(t)) { lastExistingIdx = t; } data.push({ name: t, series: [ { name: this.fractionNameBlufor, value: flagStatusMap.blufor[lastExistingIdx] ? 1 : 0 } ] }); data.push({ name: t, series: [ { name: this.fractionNameOpfor, value: flagStatusMap.opfor[lastExistingIdx] ? -1 : 0 } ] }); } this.barChartData = data; this.initialized.flag = true; } initializeTempCollections() { this.tmpPointData = ChartUtils.getMultiDataArray(this.fractionNameBlufor, this.fractionNameOpfor); this.tmpBudgetData = ChartUtils.getMultiDataArray(this.fractionNameBlufor, this.fractionNameOpfor); this.tmpKillData = ChartUtils.getMultiDataArray(this.fractionNameBlufor, this.fractionNameOpfor); this.tmpFrienlyFireData = ChartUtils.getMultiDataArray(this.fractionNameBlufor, this.fractionNameOpfor); this.tmpVehicleData = ChartUtils.getMultiDataArray(this.fractionNameBlufor, this.fractionNameOpfor, this.fractionNameBlufor.concat(' Leicht'), this.fractionNameOpfor.concat(' Leicht'), this.fractionNameBlufor.concat(' Schwer'), this.fractionNameOpfor.concat(' Schwer'), this.fractionNameBlufor.concat(' Luft'), this.fractionNameOpfor.concat(' Luft')); this.tmpTransportData = ChartUtils.getMultiDataArray(this.fractionNameBlufor, this.fractionNameOpfor); this.tmpReviveData = ChartUtils.getMultiDataArray(this.fractionNameBlufor, this.fractionNameOpfor); this.tmpStabilizeData = ChartUtils.getMultiDataArray(this.fractionNameBlufor, this.fractionNameOpfor); this.tmpPlayerCountData = ChartUtils.getMultiDataArray(this.fractionNameBlufor, this.fractionNameOpfor); [this.tmpKillData, this.tmpFrienlyFireData, this.tmpVehicleData, this.tmpReviveData, this.tmpStabilizeData, this.tmpTransportData].forEach(tmp => { [0, 1].forEach(index => { tmp[index].series.push(ChartUtils.getSeriesEntry(this.startDateObj, 0)); }); }); this.tmpBudgetData[0].series.push(ChartUtils.getSeriesEntry(this.startDateObj, this.war.budgetBlufor)); this.tmpBudgetData[1].series.push(ChartUtils.getSeriesEntry(this.startDateObj, this.war.budgetOpfor)); } addFinalTimeData(tmpCollection) { const endDate = new Date(this.war.endDate); if (tmpCollection === this.tmpBudgetData) { this.tmpBudgetData[0].series.push(ChartUtils.getSeriesEntry(endDate, this.war.endBudgetBlufor)); this.tmpBudgetData[1].series.push(ChartUtils.getSeriesEntry(endDate, this.war.endBudgetOpfor)); } else { for (let j = 0; j < tmpCollection.length; j++) { // mayBe check is needed for logs that are simply not existent in older wars, i.e. vehicleKills const maybeLast = tmpCollection[j].series[tmpCollection[j].series.length - 1]; if (maybeLast && maybeLast.name < endDate) { tmpCollection[j].series.push( ChartUtils.getSeriesEntry(endDate, tmpCollection[j].series[tmpCollection[j].series.length - 1].value) ); } } } } setChartYAxisLabel(i18n: string) { this.translate.get(i18n).subscribe((translated) => { this.chartYAxisLabel = translated; }); } axisFormatX(val) { if (val % 10 === 0) { return val; } else { return ''; } } axisFormatY(val) { if (val === 1) { return this.fractionNameBlufor; } if (val === -1) { return this.fractionNameOpfor; } return ''; } }