Profile

Liam Muller

Liam Muller is a fifth-year Computer Science student with a passion for web development.


PUBG Viewer

PUBG Viewer is a progressive web app written in Angular 5 using the Ionic Framework with a Node.JS API.

Users can sign in and track their stats for the popular video game Player Unknown's Battlegrounds. Features include a breakdown in match details, overall player stats, and the ability to favorite other players.

Technologies Used:

Front-end:

  • Firebase
  • Ionic
  • Angular
  • NGRX

Back-end:

  • Node.js
  • Heroku

PUBG provides an official API, but in order to better control the data flowing into the front-end, I created my own wrapper to process the telemetry data before sending it along. This is why there is an associated back-end with this project.


Screenshots:

Player Details

Player

Match List

Matches

Match Details - Overview

Match_Overview

Match Details - Player Summary

Match_Player

Examples:

API function snippet to aggregate attacks from telemetry data so that the front-end can create the damage map graphic
const aggregatedAttacks = {};
const damageMap = {};
const sortOrder = [
  'HeadShot',
  'TorsoShot',
  'ArmShot',
  'PelvisShot',
  'LegShot',
  'NonSpecific',
];
sortOrder.forEach(bodyPart => {
  const sameBodyPart = playerAttacks.filter(attack => {
    return attack.damageReason === bodyPart;
  });
  if (sameBodyPart && sameBodyPart.length > 0) {
    let damage = 0;
    damageMap[bodyPart] = {
      amount: sameBodyPart.reduce((accumulator, attack) => {
        return accumulator + attack.damage;
      }, damage),
      bodyPart,
    };
  } else {
    damageMap[bodyPart] = {
      amount: 0,
      bodyPart,
    };
  }
});
playerAttacks.forEach(attack => {
  if (!aggregatedAttacks[attack.damageCauserName]) {
    const sameWeapon = playerAttacks.filter(weapon => {
      return attack.damageCauserName === weapon.damageCauserName;
    });
    sameWeapon.forEach((weap, index) => {
      const x = telemetry.find(element => {
        return element.attackId === weap.attackId;
      });
      sameWeapon[index].attacker = x.attacker;
    });
    aggregatedAttacks[attack.damageCauserName] = sameWeapon;
  }
});

teamAttacks[player] = Object.entries(aggregatedAttacks).reduce(
  (arr, [key, value]) => [...arr, value],
  []
);

let ordering = {};
for (var i = 0; i < sortOrder.length; i++) ordering[sortOrder[i]] = i;

teamDamageMap[player] = Object.entries(damageMap)
  .reduce((arr, [key, value]) => [...arr, value], [])
  .sort(function(a, b) {
    return (
      ordering[a.bodyPart] - ordering[b.bodyPart] ||
      a.name.localeCompare(b.bodyPart)
    );
  });
});

Built with React, React-Router, Styled-Components, Contentful, and 💖.

View Updates Here