diff --git a/api/apib/data_structures/_awarding.apib b/api/apib/data_structures/_awarding.apib index bf2acfe..b66dc10 100644 --- a/api/apib/data_structures/_awarding.apib +++ b/api/apib/data_structures/_awarding.apib @@ -15,8 +15,9 @@ Awarding associating a decoration to a user # AwardingPopulated (Awarding) -Awarding with populated decoration and proposer +Awarding with populated decoration, proposer and army-user ## Properties + proposer (Proposer, required) - app user who requested this awarding + decorationId (Decoration, required) - populated decoration object that is given with the awarding ++ userId (User, required) - populated user who gets this awarding diff --git a/api/apib/data_structures/_log.apib b/api/apib/data_structures/_log.apib index 6feca45..752028e 100644 --- a/api/apib/data_structures/_log.apib +++ b/api/apib/data_structures/_log.apib @@ -77,3 +77,9 @@ + `BLUFOR` + `OPFOR` + `NONE` ++ vehicleClass: `LIGHT` (enum, required) - class of shot vehicle + + Members + + `LIGHT` + + `HEAVY` + + `AIR` + + `UNKNOWN` diff --git a/api/apib/data_structures/_player.apib b/api/apib/data_structures/_player.apib index 0c77839..2657027 100644 --- a/api/apib/data_structures/_player.apib +++ b/api/apib/data_structures/_player.apib @@ -7,7 +7,9 @@ Basic player statistic information object + fraction: `OPFOR` (string, required) - fraction of the player + kill: 5 (number, required) - sum of kills + friendlyFire: 0 (number, required) - sum of friendly fire kills -+ vehicle: 1 (number, required) - sum of vehicle kills ++ vehicleLight: 1 (number, required) - sum of light vehicle kills ++ vehicleHeavy: 1 (number, required) - sum of heavy vehicle kills ++ vehicleAir: 0 (number, required) - sum of air vehicle kills + death: 3 (number, required) - sum of deaths + respawn: 2 (number, required) - sum of respawns + flagTouch: 1 (number, required) - sum of flag captures diff --git a/api/apib/statistics/players.apib b/api/apib/statistics/players.apib index 033c691..f22b444 100644 --- a/api/apib/statistics/players.apib +++ b/api/apib/statistics/players.apib @@ -12,7 +12,9 @@ Every highscore player object contains a field *sum*, representing its order num + kill (array[HighscorePlayer],required) - player highscore for kill + death (array[HighscorePlayer],required) - player highscore for death + friendlyFire (array[HighscorePlayer],required) - player highscore for friendly fire - + vehicle (array[HighscorePlayer],required) - player highscore for vehicle + + vehicleLight (array[HighscorePlayer],required) - player highscore for light vehicle + + vehicleHeavy (array[HighscorePlayer],required) - player highscore for heavy vehicle + + vehicleAir (array[HighscorePlayer],required) - player highscore for air vehicle + revive (array[HighscorePlayer],required) - player highscore for revive + respawn (array[HighscorePlayer],required) - player highscore for respawn + flagTouch (array[HighscorePlayer],required) - player highscore for flag captures diff --git a/api/models/logs/vehicle.js b/api/models/logs/vehicle.js index 8e9920e..704d96e 100644 --- a/api/models/logs/vehicle.js +++ b/api/models/logs/vehicle.js @@ -25,6 +25,11 @@ const LogVehicleKillSchema = new Schema({ enum: ['BLUFOR', 'OPFOR', 'NONE'], required: true, }, + vehicleClass: { + type: String, + enum: ['LIGHT', 'HEAVY', 'AIR', 'UNKNOWN'], + required: true, + }, }, { collection: 'logVehicle', }); diff --git a/api/models/player.js b/api/models/player.js index a91edf1..a407a8a 100644 --- a/api/models/player.js +++ b/api/models/player.js @@ -24,7 +24,19 @@ const PlayerSchema = new Schema({ set: (v) => Math.round(v), required: true, }, - vehicle: { + vehicleLight: { + type: Number, + get: (v) => Math.round(v), + set: (v) => Math.round(v), + required: true, + }, + vehicleHeavy: { + type: Number, + get: (v) => Math.round(v), + set: (v) => Math.round(v), + required: true, + }, + vehicleAir: { type: Number, get: (v) => Math.round(v), set: (v) => Math.round(v), diff --git a/api/routes/awardings.js b/api/routes/awardings.js index 2402632..e256bf9 100644 --- a/api/routes/awardings.js +++ b/api/routes/awardings.js @@ -33,7 +33,7 @@ awarding.route('/') filter.confirmed = 0; } AwardingModel.find(filter, {}, {sort: {date: 'desc'}}) - .populate('decorationId').populate('proposer', resultSet) + .populate('decorationId').populate('proposer', resultSet).populate('userId') .exec((err, items) => { if (err) { err.status = codes.servererror; diff --git a/api/routes/players.js b/api/routes/players.js index 744bf25..cfb148b 100644 --- a/api/routes/players.js +++ b/api/routes/players.js @@ -46,7 +46,9 @@ campaignPlayer.route('/ranking/:campaignId') const resItem = { name: usesUUID ? playerInstances[playerInstances.length - 1].name : player, kill: 0, - vehicle: 0, + vehicleLight: 0, + vehicleHeavy: 0, + vehicleAir: 0, death: 0, friendlyFire: 0, revive: 0, @@ -57,7 +59,9 @@ campaignPlayer.route('/ranking/:campaignId') resItem.kill += playerInstances[i].kill; resItem.death += playerInstances[i].death; resItem.friendlyFire += playerInstances[i].friendlyFire; - resItem.vehicle += playerInstances[i].vehicle; + resItem.vehicleLight += playerInstances[i].vehicleLight; + resItem.vehicleHeavy += playerInstances[i].vehicleHeavy; + resItem.vehicleAir += playerInstances[i].vehicleAir; resItem.revive += playerInstances[i].revive; resItem.respawn += playerInstances[i].respawn; resItem.flagTouch += playerInstances[i].flagTouch; @@ -80,7 +84,9 @@ campaignPlayer.route('/ranking/:campaignId') kill: getSortedField('kill'), death: getSortedField('death'), friendlyFire: getSortedField('friendlyFire'), - vehicle: getSortedField('vehicle'), + vehicleLight: getSortedField('vehicleLight'), + vehicleHeavy: getSortedField('vehicleHeavy'), + vehicleAir: getSortedField('vehicleAir'), revive: getSortedField('revive'), respawn: getSortedField('respawn'), flagTouch: getSortedField('flagTouch'), diff --git a/api/tools/log-parse-tool.js b/api/tools/log-parse-tool.js index 9cc1772..299016c 100644 --- a/api/tools/log-parse-tool.js +++ b/api/tools/log-parse-tool.js @@ -4,6 +4,13 @@ const playerArrayContains = require('./util').playerArrayContains; const WHITESPACE = ' '; +const VehicleClasses = Object.freeze({ + LIGHT: 'Leicht', + HEAVY: 'Schwer', + AIR: 'Flug', + UNKNOWN: 'Unbekannt', +}); + const parseWarLog = (lineArray, war) => { const NAME_TOO_LONG_ERROR = 'Error: ENAMETOOLONG: name too long, open \''; @@ -21,7 +28,7 @@ const parseWarLog = (lineArray, war) => { players: [], }; - const vehicleBlacklist = [ + const VEHICLE_BLACKLIST = [ 'Prowler (Unbewaffnet)', 'Prowler (Bewaffnet)', 'Hunter', 'HEMTT Transporter', 'HEMTT Transporter (abgedeckt)', 'HEMTT SanitÀtsfahrzeug', 'Remote Designator [NATO]', 'UGV Stomper', @@ -67,6 +74,7 @@ const parseWarLog = (lineArray, war) => { shooter: shooter ? shooter.name : null, target: target ? target.name : null, fraction: shooter ? shooter.fraction : 'NONE', + vehicleClass: target.vehicleClass, }); } } else { @@ -95,8 +103,8 @@ const parseWarLog = (lineArray, war) => { const dateString = budg[0].slice(0, -1).split('/').map((s) => parseInt(s)); stats.war.date = new Date(dateString[0], dateString[1] - 1, dateString[2]); } else if (line.includes('Endbudget')) { - stats.war['endBudgetBlufor'] = transformMoneyString(budg[9].substr(1)); - stats.war['endBudgetOpfor'] = transformMoneyString(budg[12].slice(0, -1)); + stats.war['endBudgetBlufor'] = transformMoneyString(budg[9]); + stats.war['endBudgetOpfor'] = transformMoneyString(budg[12].slice(0, -2)); stats.war.endDate = getFullTimeDate(war.date, budg[5]); } else { stats.budget.push(getBudgetEntry(budg, war._id, war.date)); @@ -191,14 +199,31 @@ const parseWarLog = (lineArray, war) => { for (let i = 0; i < stats.players.length; i++) { const playerName = stats.players[i].name; stats.players[i]['respawn'] = stats.respawn.filter((res) => res.player === playerName).length; + stats.players[i]['kill'] = stats.kills.filter((kill) => kill.shooter === playerName && !kill.friendlyFire).length; - stats.players[i]['vehicle'] = stats.vehicles.filter( - (vehicle) => vehicle.shooter === playerName && vehicleBlacklist.indexOf(vehicle.target) < 0).length; + + // TODO: use vehicle class description from enum + stats.players[i]['vehicleLight'] = stats.vehicles.filter( + (vehicle) => vehicle.shooter === playerName && vehicle.vehicleClass === 'LIGHT' && + VEHICLE_BLACKLIST.indexOf(vehicle.target) < 0).length; + + stats.players[i]['vehicleHeavy'] = stats.vehicles.filter( + (vehicle) => vehicle.shooter === playerName && vehicle.vehicleClass === 'HEAVY' && + VEHICLE_BLACKLIST.indexOf(vehicle.target) < 0).length; + + stats.players[i]['vehicleAir'] = stats.vehicles.filter( + (vehicle) => vehicle.shooter === playerName && vehicle.vehicleClass === 'AIR' && + VEHICLE_BLACKLIST.indexOf(vehicle.target) < 0).length; + stats.players[i]['friendlyFire'] = stats.kills.filter( (kill) => kill.shooter === playerName && kill.friendlyFire).length; + stats.players[i]['death'] = stats.kills.filter((kill) => kill.target === playerName).length; + stats.players[i]['revive'] = stats.revive.filter((rev) => rev.medic === playerName && !rev.stabilized).length; + stats.players[i]['flagTouch'] = stats.flag.filter((flag) => flag.player === playerName).length; + stats.players[i]['sort'] = stats.players[i]['kill'] + stats.players[i]['revive'] + stats.players[i]['flagTouch'] - stats.players[i]['friendlyFire'] - stats.players[i]['death'] - stats.players[i]['respawn']; } @@ -250,19 +275,34 @@ const getPlayerAndFractionFromString = (nameAndFractionString) => { } }; -const getVehicleAndFractionFromString = (nameAndFractionString) => { - const nameArray = nameAndFractionString.trim().split(WHITESPACE); - const fractionPart = nameArray[nameArray.length - 1]; +const getVehicleAndFractionFromString = (nameClassFractionString) => { + const nameArray = nameClassFractionString.trim().split(WHITESPACE); - // escape it is some parachute fraction identifier - if (fractionPart === 'OPF_F' || fractionPart === 'BLU_F') { + const vehicleFraction = nameArray[nameArray.length - 1]; + nameArray.pop(); + const vehicleClassString = nameArray[nameArray.length - 1].replace('(', '').replace(')', ''); + nameArray.pop(); + const vehicleName = nameArray.join(WHITESPACE); + + // skip logging here - this is some basic equipment identifier (i.e. parachute) + if (vehicleFraction === 'OPF_F' || vehicleFraction === 'BLU_F' || vehicleClassString === VehicleClasses.UNKNOWN) { return; } - const fraction = fractionPart === '(OPT_NATO)' || fractionPart === '(OPT_NATO_T)' ? 'BLUFOR' : 'OPFOR'; - const name = nameAndFractionString.substring(0, nameAndFractionString.indexOf(fractionPart) - 1); + const fraction = vehicleFraction === '(OPT_NATO)' || vehicleFraction === '(OPT_NATO_T)' ? 'BLUFOR' : 'OPFOR'; - return {name: name, fraction: fraction}; + let vehicleClass; + for (const key in VehicleClasses) { + if (VehicleClasses.hasOwnProperty(key) && VehicleClasses[key] === vehicleClassString) { + vehicleClass = key; + } + } + + return { + name: vehicleName, + fraction: fraction, + vehicleClass: vehicleClass, + }; }; const transformMoneyString = (budgetString) => { diff --git a/package.json b/package.json index 59608ba..5658cf1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "opt-cc", - "version": "1.7.2", + "version": "1.7.3", "author": "Florian Hartwich ", "private": true, "scripts": { diff --git a/static/src/app/app.component.html b/static/src/app/app.component.html index 045a2a6..a6bdc0e 100644 --- a/static/src/app/app.component.html +++ b/static/src/app/app.component.html @@ -86,7 +86,7 @@ - + diff --git a/static/src/app/army/army-member.component.css b/static/src/app/army/army-member.component.css index 2c149bf..40bc2a1 100644 --- a/static/src/app/army/army-member.component.css +++ b/static/src/app/army/army-member.component.css @@ -33,7 +33,7 @@ } tbody { - background: #ffffffe0; + background: rgba(255, 255, 255, 0.88); } .cell-outline { diff --git a/static/src/app/army/army.component.css b/static/src/app/army/army.component.css index ba5a69f..896472d 100644 --- a/static/src/app/army/army.component.css +++ b/static/src/app/army/army.component.css @@ -27,7 +27,7 @@ .middle-row { min-height: 120px; border: rgb(34, 34, 34); - background-color: #ffffffe0; + background-color: rgba(255, 255, 255, 0.88); border-left-style: solid; border-right-style: solid; } diff --git a/static/src/app/models/model-interfaces.ts b/static/src/app/models/model-interfaces.ts index 20cb379..bc7a72b 100644 --- a/static/src/app/models/model-interfaces.ts +++ b/static/src/app/models/model-interfaces.ts @@ -24,7 +24,9 @@ export interface Player { name?: string; warId?: War; kill?: number; - vehicle?: number; + vehicleLight?: number; + vehicleHeavy?: number; + vehicleAir?: number; death?: number; friendlyFire?: number; revive?: number; diff --git a/static/src/app/statistic/campaign-player-detail/campaign-player-detail.component.html b/static/src/app/statistic/campaign-player-detail/campaign-player-detail.component.html index 0d07b97..2e682c5 100644 --- a/static/src/app/statistic/campaign-player-detail/campaign-player-detail.component.html +++ b/static/src/app/statistic/campaign-player-detail/campaign-player-detail.component.html @@ -81,26 +81,6 @@ -
- - -
-
+ +
+ + +
+ +
+ + +
+ +
+ + +
diff --git a/static/src/app/statistic/campaign-player-detail/campaign-player-detail.component.ts b/static/src/app/statistic/campaign-player-detail/campaign-player-detail.component.ts index 9e206bf..acecd7d 100644 --- a/static/src/app/statistic/campaign-player-detail/campaign-player-detail.component.ts +++ b/static/src/app/statistic/campaign-player-detail/campaign-player-detail.component.ts @@ -23,7 +23,9 @@ export class CampaignPlayerDetailComponent implements OnInit { sumData: any[] = []; killData: any[] = []; friendlyFireData: any[] = []; - vehicleKillData: any[] = []; + vehicleLightData: any[] = []; + vehicleHeavyData: any[] = []; + vehicleAirData: any[] = []; deathData: any[] = []; respawnData: any[] = []; reviveData: any[] = []; @@ -31,7 +33,9 @@ export class CampaignPlayerDetailComponent implements OnInit { yAxisKill = 'Kills'; yAxisFriendlyFire = 'FriendlyFire'; - yAxisVehicleKill = 'Fahrzeug-Kills'; + yAxisVehicleLight = 'Fahrzeug (Light)'; + yAxisVehicleHeavy = 'Fahrzeug (Heavy)'; + yAxisVehicleAir = 'Fahrzeug (Air)'; yAxisDeath = 'Tode'; yAxisRespawn = 'Respawn'; yAxisRevive = 'Revive'; @@ -49,7 +53,9 @@ export class CampaignPlayerDetailComponent implements OnInit { showRefLines = true; showRefLabels = true; killRefLines = []; - vehicleKillRefLines = []; + vehicleLightRefLines = []; + vehicleHeavyRefLines = []; + vehicleAirRefLines = []; deathRefLines = []; captureRefLines = []; friendlyFireRefLines = []; @@ -68,7 +74,9 @@ export class CampaignPlayerDetailComponent implements OnInit { totalKills; totalFriendlyFire; - totalVehicleKills; + totalVehicleLight; + totalVehicleHeavy; + totalVehicleAir; totalDeath; totalRespawn; totalRevive; @@ -89,12 +97,14 @@ export class CampaignPlayerDetailComponent implements OnInit { .subscribe(campaignPlayer => { this.campaignPlayer = campaignPlayer; this.killData = this.assignData(this.yAxisKill, 'kill'); - this.vehicleKillData = this.assignData(this.yAxisVehicleKill, 'vehicle'); this.friendlyFireData = this.assignData(this.yAxisFriendlyFire, 'friendlyFire'); this.deathData = this.assignData(this.yAxisDeath, 'death'); this.respawnData = this.assignData(this.yAxisRespawn, 'respawn'); this.reviveData = this.assignData(this.yAxisRevive, 'revive'); this.captureData = this.assignData(this.yAxisCapture, 'flagTouch'); + this.vehicleLightData = this.assignData(this.yAxisVehicleLight, 'vehicleLight'); + this.vehicleHeavyData = this.assignData(this.yAxisVehicleHeavy, 'vehicleHeavy'); + this.vehicleAirData = this.assignData(this.yAxisVehicleAir, 'vehicleAir'); const totalDeathDiv = this.totalDeath === 0 ? 1 : this.totalDeath; this.kdRatio = parseFloat((this.totalKills / totalDeathDiv).toFixed(2)); @@ -113,10 +123,6 @@ export class CampaignPlayerDetailComponent implements OnInit { name: this.yAxisFriendlyFire, value: this.totalFriendlyFire }, - { - name: this.yAxisVehicleKill, - value: this.totalVehicleKills - }, { name: this.yAxisDeath, value: this.totalDeath @@ -132,11 +138,24 @@ export class CampaignPlayerDetailComponent implements OnInit { { name: this.yAxisCapture, value: this.totalCapture - } + }, + { + name: this.yAxisVehicleLight, + value: this.totalVehicleLight + }, + { + name: this.yAxisVehicleHeavy, + value: this.totalVehicleHeavy + }, + { + name: this.yAxisVehicleAir, + value: this.totalVehicleAir + }, ]; - Object.assign(this, [this.sumData, this.killData, this.friendlyFireData, this.vehicleKillData, - this.deathData, this.respawnData, this.reviveData, this.captureData]); + Object.assign(this, [this.sumData, this.killData, this.friendlyFireData, this.vehicleLightData, + this.vehicleHeavyData, this.vehicleAirData, this.deathData, this.respawnData, this.reviveData, + this.captureData]); }); } @@ -165,10 +184,6 @@ export class CampaignPlayerDetailComponent implements OnInit { this.friendlyFireRefLines.push({value: total / playerLength, name: this.avgLabel}); this.totalFriendlyFire = total; break; - case 'vehicle': - this.vehicleKillRefLines.push({value: total / playerLength, name: this.avgLabel}); - this.totalVehicleKills = total; - break; case 'death': this.deathRefLines.push({value: total / playerLength, name: this.avgLabel}); this.totalDeath = total; @@ -185,6 +200,18 @@ export class CampaignPlayerDetailComponent implements OnInit { this.captureRefLines.push({value: total / playerLength, name: this.avgLabel}); this.totalCapture = total; break; + case 'vehicleLight': + this.vehicleLightRefLines.push({value: total / playerLength, name: this.avgLabel}); + this.totalVehicleLight = total; + break; + case 'vehicleHeavy': + this.vehicleHeavyRefLines.push({value: total / playerLength, name: this.avgLabel}); + this.totalVehicleHeavy = total; + break; + case 'vehicleAir': + this.vehicleAirRefLines.push({value: total / playerLength, name: this.avgLabel}); + this.totalVehicleAir = total; + break; } return [killObj]; } diff --git a/static/src/app/statistic/highscore/highscore.component.html b/static/src/app/statistic/highscore/highscore.component.html index e7ce8cf..a62c187 100644 --- a/static/src/app/statistic/highscore/highscore.component.html +++ b/static/src/app/statistic/highscore/highscore.component.html @@ -57,27 +57,6 @@ - - - - - - {{value}} - - - - - - + + + + + + {{value}} + + + + + + + + + + + + {{value}} + + + + + + + + + + + + {{value}} + + + + + + diff --git a/static/src/app/statistic/highscore/highscore.component.ts b/static/src/app/statistic/highscore/highscore.component.ts index 94bd477..353139c 100644 --- a/static/src/app/statistic/highscore/highscore.component.ts +++ b/static/src/app/statistic/highscore/highscore.component.ts @@ -87,7 +87,9 @@ export class StatisticHighScoreComponent implements OnInit { this.players = { kill: this.filterPlayerAttribute('kill'), friendlyFire: this.filterPlayerAttribute('friendlyFire'), - vehicle: this.filterPlayerAttribute('vehicle'), + vehicleLight: this.filterPlayerAttribute('vehicleLight'), + vehicleHeavy: this.filterPlayerAttribute('vehicleLight'), + vehicleAir: this.filterPlayerAttribute('vehicleLight'), death: this.filterPlayerAttribute('death'), respawn: this.filterPlayerAttribute('respawn'), revive: this.filterPlayerAttribute('revive'), diff --git a/static/src/app/statistic/war-detail/scoreboard/scoreboard.component.css b/static/src/app/statistic/war-detail/scoreboard/scoreboard.component.css index c2fcaf4..741bd71 100644 --- a/static/src/app/statistic/war-detail/scoreboard/scoreboard.component.css +++ b/static/src/app/statistic/war-detail/scoreboard/scoreboard.component.css @@ -5,7 +5,7 @@ /* ########### DATATABLE ########### */ ngx-datatable { - width: 1020px; + width: 1040px; margin: auto; height: 68vh; } diff --git a/static/src/app/statistic/war-detail/scoreboard/scoreboard.component.html b/static/src/app/statistic/war-detail/scoreboard/scoreboard.component.html index f7ddd5a..061f789 100644 --- a/static/src/app/statistic/war-detail/scoreboard/scoreboard.component.html +++ b/static/src/app/statistic/war-detail/scoreboard/scoreboard.component.html @@ -23,13 +23,15 @@ {{value === 'BLUFOR' ? fraction.BLUFOR : fraction.OPFOR}} - - - - - - - + + + + + + + + + diff --git a/static/src/app/statistic/war-detail/scoreboard/scoreboard.component.ts b/static/src/app/statistic/war-detail/scoreboard/scoreboard.component.ts index fd4455c..a1dbfa6 100644 --- a/static/src/app/statistic/war-detail/scoreboard/scoreboard.component.ts +++ b/static/src/app/statistic/war-detail/scoreboard/scoreboard.component.ts @@ -19,7 +19,8 @@ export class ScoreboardComponent implements OnChanges { @Output() playerTabSwitch = new EventEmitter(); - tableHead = ['Spieler', 'Fraktion', 'Kills', 'FriendlyFire', 'Fahrzeug', 'Revive', 'Eroberung', 'Tod', 'Respawn']; + tableHead = ['Spieler', 'Fraktion', 'Kills', 'FF', 'Fzg(L)', 'Fzg(H)', 'Fzg(A)', + 'Revive', 'Flagge', 'Tod', 'Respawn']; isSteamUUID = PlayerUtils.isSteamUUID; @@ -82,7 +83,9 @@ export class ScoreboardComponent implements OnChanges { csvOut += player.fraction + ','; csvOut += player.kill + ','; csvOut += player.friendlyFire + ','; - csvOut += player.vehicle + ','; + csvOut += player.vehicleLight + ','; + csvOut += player.vehicleHeavy + ','; + csvOut += player.vehicleAir + ','; csvOut += player.revive + ','; csvOut += player.flagTouch + ','; csvOut += player.death + ',';