diff --git a/src/app/common-components/n64-box/n64-box.component.scss b/src/app/common-components/n64-box/n64-box.component.scss
index b7062a7..386d5c4 100644
--- a/src/app/common-components/n64-box/n64-box.component.scss
+++ b/src/app/common-components/n64-box/n64-box.component.scss
@@ -16,7 +16,6 @@ section {
$color-bg-hl 80%,
$color-bg);
- max-width: 800px;
margin: auto;
}
diff --git a/src/app/games/game-summaries/external-game-summary/external-game-summary.component.html b/src/app/games/game-summaries/external-game-summary/external-game-summary.component.html
new file mode 100644
index 0000000..95e5aad
--- /dev/null
+++ b/src/app/games/game-summaries/external-game-summary/external-game-summary.component.html
@@ -0,0 +1,26 @@
+
+
+
+
+ {{ data.title }}
+
+
+
+ Code decompiled: {{ total }}
+
+
+
+
+
+
+
+ GitHub
+
+
+
+ Progress history
+
+
+
+
+
diff --git a/src/app/games/game-summaries/external-game-summary/external-game-summary.component.scss b/src/app/games/game-summaries/external-game-summary/external-game-summary.component.scss
new file mode 100644
index 0000000..ea32660
--- /dev/null
+++ b/src/app/games/game-summaries/external-game-summary/external-game-summary.component.scss
@@ -0,0 +1,25 @@
+mat-card > a {
+ text-decoration: none;
+ }
+
+img[mat-card-avatar] {
+ border-radius: 0;
+ object-fit: contain;
+}
+
+mat-card-actions {
+ text-align: center;
+}
+
+mat-card-title > fa-icon {
+ font-size: 16px;
+ vertical-align: center;
+}
+
+mat-card-actions fa-icon {
+ vertical-align: center;
+}
+
+mat-card-actions a {
+ color: white;
+}
diff --git a/src/app/games/game-summaries/external-game-summary/external-game-summary.component.spec.ts b/src/app/games/game-summaries/external-game-summary/external-game-summary.component.spec.ts
new file mode 100644
index 0000000..ebb3435
--- /dev/null
+++ b/src/app/games/game-summaries/external-game-summary/external-game-summary.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { GameComponent } from '../../game/game.component';
+
+describe('GameComponent', () => {
+ let component: GameComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ GameComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(GameComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/games/game-summaries/external-game-summary/external-game-summary.component.ts b/src/app/games/game-summaries/external-game-summary/external-game-summary.component.ts
new file mode 100644
index 0000000..af5c34f
--- /dev/null
+++ b/src/app/games/game-summaries/external-game-summary/external-game-summary.component.ts
@@ -0,0 +1,48 @@
+import { Component, Input, OnChanges } from '@angular/core';
+import { IExternalGame } from '../../games.service.interface';
+import { faGithub } from '@fortawesome/free-brands-svg-icons';
+import { faChartLine, faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons';
+import { GamesService } from '../../games.service';
+
+/**
+ * Displays an externally-linked game's name, decomp progress, and links.
+ */
+@Component({
+ selector: 'external-game-summary',
+ templateUrl: './external-game-summary.component.html',
+ styleUrls: ['./external-game-summary.component.scss']
+})
+export class ExternalGameSummaryComponent implements OnChanges {
+
+ constructor(private gamesService: GamesService) { }
+
+ /**
+ * The game data to use.
+ */
+ @Input() data: IExternalGame;
+ /**
+ * Shield data.
+ */
+ shield: string;
+ /**
+ * The percentage to display for completion.
+ */
+ total?: string;
+ /**
+ * Icon definitions.
+ */
+ github = faGithub;
+ external = faExternalLinkAlt;
+ chart = faChartLine;
+
+ ngOnChanges(): void {
+
+ if (this.data.shieldURL) {
+ this.gamesService.getGameShield(`${this.data.shieldURL}`).subscribe(
+ res => this.total = res.message,
+ err => console.error(err)
+ );
+ }
+ }
+
+}
diff --git a/src/app/games/game-summaries/game-summaries.component.html b/src/app/games/game-summaries/game-summaries.component.html
index 8a6d91d..546f1f5 100644
--- a/src/app/games/game-summaries/game-summaries.component.html
+++ b/src/app/games/game-summaries/game-summaries.component.html
@@ -1,3 +1,4 @@
+
diff --git a/src/app/games/game-summaries/game-summaries.component.scss b/src/app/games/game-summaries/game-summaries.component.scss
index 793e7a6..06eb588 100644
--- a/src/app/games/game-summaries/game-summaries.component.scss
+++ b/src/app/games/game-summaries/game-summaries.component.scss
@@ -2,7 +2,12 @@ div {
margin-right: -1em;
}
-game-summary {
+h2 {
+ margin: auto;
+ width: max-content;
+}
+
+game-summary, external-game-summary {
flex-grow: 0;
margin-bottom: 12px;
margin-right: 1em;
diff --git a/src/app/games/game-summaries/game-summaries.component.ts b/src/app/games/game-summaries/game-summaries.component.ts
index ab0a19c..18bad9f 100644
--- a/src/app/games/game-summaries/game-summaries.component.ts
+++ b/src/app/games/game-summaries/game-summaries.component.ts
@@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { GamesService } from '../games.service';
-import { IGame } from '../games.service.interface';
+import { IGame, IExternalGame } from '../games.service.interface';
/**
* Container for all game summaries.
@@ -18,12 +18,20 @@ export class GameSummariesComponent implements OnInit {
* List of games to use for "GameSummary" components.
*/
games: IGame[] = [];
+ /**
+ * List of external games to use for "ExternalGameSummary" components.
+ */
+ externalGames: IExternalGame[] = [];
ngOnInit(): void {
this.gamesService.getGames().subscribe(
res => this.games = res,
err => console.error(err)
)
+ this.gamesService.getExternalGames().subscribe(
+ res => this.externalGames = res,
+ err => console.error(err)
+ )
}
}
diff --git a/src/app/games/game-summaries/game-summary/game-summary.component.html b/src/app/games/game-summaries/game-summary/game-summary.component.html
index afe15c9..31e6931 100644
--- a/src/app/games/game-summaries/game-summary/game-summary.component.html
+++ b/src/app/games/game-summaries/game-summary/game-summary.component.html
@@ -1,18 +1,19 @@
-
+
{{ data.title }}
- Percent completion: {{ (total * 100).toFixed(2) }}%
+
+ Code decompiled: {{ total }}
+
+
-
+
-
-
-
+ GitHub
diff --git a/src/app/games/game-summaries/game-summary/game-summary.component.scss b/src/app/games/game-summaries/game-summary/game-summary.component.scss
index 0c6c651..9bb6823 100644
--- a/src/app/games/game-summaries/game-summary/game-summary.component.scss
+++ b/src/app/games/game-summaries/game-summary/game-summary.component.scss
@@ -10,3 +10,11 @@ img[mat-card-avatar] {
mat-card-actions {
text-align: center;
}
+
+mat-card-actions fa-icon {
+ vertical-align: center;
+}
+
+mat-card-actions a {
+ color: white;
+}
diff --git a/src/app/games/game-summaries/game-summary/game-summary.component.ts b/src/app/games/game-summaries/game-summary/game-summary.component.ts
index 79b9556..36bb79a 100644
--- a/src/app/games/game-summaries/game-summary/game-summary.component.ts
+++ b/src/app/games/game-summaries/game-summary/game-summary.component.ts
@@ -22,29 +22,20 @@ export class GameSummaryComponent implements OnChanges {
/**
* The percentage to display for completion.
*/
- total: number = null;
+ total?: string;
/**
- * Icon definition.
+ * Icon definitions.
*/
github = faGithub;
- /**
- * Icon definition.
- */
trello = faTrello;
ngOnChanges(): void {
- // Matching is second
- this.gamesService.getGameCSV(this.data.csvs.matching).subscribe(
- res => {
- const points = res.split("\n").filter((line) => line != "");
- const latestPoint = points[points.length - 1];
- const column = latestPoint.split(",");
-
- let i = this.data.progressMeta[0].index;
- this.total = +column[i] / +column[i + 1];
- },
- err => console.error(err)
- );
+ if (this.data.shieldURL) {
+ this.gamesService.getGameShield(`${this.data.shieldURL}`).subscribe(
+ res => this.total = res.message,
+ err => console.error(err)
+ );
+ }
}
}
diff --git a/src/app/games/game/game.component.ts b/src/app/games/game/game.component.ts
index 8cb1f55..726613c 100644
--- a/src/app/games/game/game.component.ts
+++ b/src/app/games/game/game.component.ts
@@ -39,13 +39,15 @@ export class GameComponent implements OnInit {
}
else {
this.game = res[index];
- forkJoin([
- this.gamesService.getGameCSV(this.game.csvs.nonmatching),
- this.gamesService.getGameCSV(this.game.csvs.matching)
- ]).subscribe(
- res => this.csvData = res,
- err => console.error(err)
- );
+ if (this.game.progressMeta.length > 0) {
+ forkJoin([
+ this.gamesService.getGameCSV(this.game.csvs.nonmatching),
+ this.gamesService.getGameCSV(this.game.csvs.matching)
+ ]).subscribe(
+ res => this.csvData = res,
+ err => console.error(err)
+ );
+ }
}
},
err => {
diff --git a/src/app/games/games.module.ts b/src/app/games/games.module.ts
index fa0ce9b..b4120fa 100644
--- a/src/app/games/games.module.ts
+++ b/src/app/games/games.module.ts
@@ -5,6 +5,7 @@ import { GamesService } from './games.service';
import { GameSummariesComponent } from './game-summaries/game-summaries.component';
import { GameSummaryComponent } from './game-summaries/game-summary/game-summary.component';
import { GameComponent } from './game/game.component';
+import { ExternalGameSummaryComponent } from './game-summaries/external-game-summary/external-game-summary.component'
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatGridListModule } from '@angular/material/grid-list';
@@ -17,6 +18,8 @@ import { ChartModule } from 'angular-highcharts';
import { GameFAQComponent } from './game/game-faq/game-faq.component';
import { MarkdownModule, MarkedOptions, MarkedRenderer } from 'ngx-markdown';
+
+
// function that returns `MarkedOptions` with renderer override
export function markedOptionsFactory(): MarkedOptions {
const renderer = new MarkedRenderer();
@@ -48,7 +51,8 @@ export function markedOptionsFactory(): MarkedOptions {
GameComponent,
GameProgressComponent,
GameChartComponent,
- GameFAQComponent
+ GameFAQComponent,
+ ExternalGameSummaryComponent
],
imports: [
CommonModule,
diff --git a/src/app/games/games.service.interface.ts b/src/app/games/games.service.interface.ts
index 0918ea2..1379595 100644
--- a/src/app/games/games.service.interface.ts
+++ b/src/app/games/games.service.interface.ts
@@ -12,6 +12,14 @@ export interface IGame {
* The game's full title.
*/
title: string;
+ /**
+ * The URL for the card title to link to.
+ */
+ routingURL: string;
+ /**
+ * The URL of the shield json to read.
+ */
+ shieldURL: string;
/**
* The name of the "FAQ" MD to read.
*/
@@ -34,6 +42,32 @@ export interface IGame {
progressMeta: IChart[];
}
+/**
+ * Game information to display and for project progress processing.
+ */
+export interface IExternalGame {
+ /**
+ * The string to use to identify the game.
+ */
+ slug: string;
+ /**
+ * The game's full title.
+ */
+ title: string;
+ /**
+ * The URL for the card title to link to.
+ */
+ routingURL: string;
+ /**
+ * Links to display as an icon. The key defines the icon, and the value is the link href.
+ */
+ links: {[type: string]: string};
+ /**
+ * The URL of the shield json to read.
+ */
+ shieldURL: string;
+}
+
/**
* Resources to display within the Resources tab.
*/
@@ -133,6 +167,25 @@ export interface ISubdivision {
description: string;
}
+/**
+ * Describes a game's shield output.
+ */
+export interface IShield {
+ /**
+ * Static label in left half.
+ */
+ label: string;
+ /**
+ * Generally the progress number.
+ */
+ message: string;
+ /**
+ * Colour of the message background.
+ */
+ color: string;
+}
+
+
/**
* Functions to implement within "GamesService".
*/
@@ -143,6 +196,11 @@ export interface IGamesService {
*/
getGames(): Observable;
+ /**
+ * Get the information for all supported external games.
+ */
+ getExternalGames(): Observable;
+
/**
* Get all of the resources.
*/
@@ -155,6 +213,13 @@ export interface IGamesService {
*/
getGameCSV(filename: string): Observable;
+ /**
+ * Get the Shield data for a game.
+ *
+ * @param filename The filename of the desired CSV.
+ */
+ getGameShield(filename: string): Observable;
+
/**
* Get the FAQ data for a game.
*
diff --git a/src/app/games/games.service.ts b/src/app/games/games.service.ts
index b16125d..3649d2c 100644
--- a/src/app/games/games.service.ts
+++ b/src/app/games/games.service.ts
@@ -1,7 +1,7 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
-import { IGame, IGamesService, IResource } from './games.service.interface';
+import { IExternalGame, IGame, IGamesService, IResource, IShield } from './games.service.interface';
/**
* Obtains data relating to the games.
@@ -17,6 +17,10 @@ export class GamesService implements IGamesService {
return this.http.get("/assets/json/games.json");
}
+ getExternalGames(): Observable {
+ return this.http.get("/assets/json/external-games.json");
+ }
+
getResources(): Observable {
return this.http.get("/assets/json/resources.json");
}
@@ -25,6 +29,10 @@ export class GamesService implements IGamesService {
return this.http.get(`/assets/csv/${filename}.csv`, { responseType: "text" });
}
+ getGameShield(filename: string): Observable {
+ return this.http.get(filename);
+ }
+
getGameFAQ(filename: string): Observable {
return this.http.get(`/assets/md/${filename}.md`, { responseType: "text" });
}
diff --git a/src/assets/json/external-games.json b/src/assets/json/external-games.json
new file mode 100644
index 0000000..da1a4dc
--- /dev/null
+++ b/src/assets/json/external-games.json
@@ -0,0 +1,12 @@
+[
+ {
+ "slug": "botw",
+ "title": "Breath of the Wild",
+ "routingURL": "https://botw.link",
+ "links": {
+ "github": "https://github.com/zeldaret/botw",
+ "progress": "https://botw.link/progress"
+ },
+ "shieldURL": "https://botw.link/badges/progress.json"
+ }
+]
\ No newline at end of file
diff --git a/src/assets/json/games.json b/src/assets/json/games.json
index 8f5149a..707693d 100644
--- a/src/assets/json/games.json
+++ b/src/assets/json/games.json
@@ -2,9 +2,11 @@
{
"slug": "oot",
"title": "Ocarina of Time",
+ "routingURL": "games/oot",
"links": {
"github": "https://www.github.com/zeldaret/oot"
},
+ "shieldURL": "assets/csv/progress-oot-shield.json",
"faq": "oot_faq",
"csvs": {
"matching": "progress-oot-matching",
@@ -44,9 +46,11 @@
{
"slug": "mm",
"title": "Majora's Mask",
+ "routingURL": "games/mm",
"links": {
"github": "https://www.github.com/zeldaret/mm"
},
+ "shieldURL": "assets/csv/progress-mm-shield.json",
"faq": "mm_faq",
"csvs": {
"matching": "progress-mm-matching",
@@ -130,9 +134,11 @@
{
"slug": "tmc",
"title": "The Minish Cap",
+ "routingURL": "games/tmc",
"links": {
"github": "https://www.github.com/zeldaret/tmc"
},
+ "shieldURL": "assets/csv/progress-tmc-shield.json",
"faq": "tmc_faq",
"csvs": {
"matching": "progress-tmc-matching",
@@ -175,6 +181,7 @@
{
"slug": "tp",
"title": "Twilight Princess",
+ "routingURL": "games/tp",
"links": {
"github": "https://www.github.com/zeldaret/tp"
},