diff --git a/package.json b/package.json
index 2ad6534..4a9fbdb 100644
--- a/package.json
+++ b/package.json
@@ -13,8 +13,10 @@
},
"dependencies": {
"@quasar/extras": "^1.16.8",
+ "class-transformer": "^0.5.1",
"pinia": "^2.1.7",
"quasar": "^2.13.1",
+ "reflect-metadata": "^0.1.13",
"vue": "^3.3.4",
"vue-router": "^4.2.5"
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a178784..b00cbdf 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,12 +8,18 @@ dependencies:
'@quasar/extras':
specifier: ^1.16.8
version: 1.16.8
+ class-transformer:
+ specifier: ^0.5.1
+ version: 0.5.1
pinia:
specifier: ^2.1.7
version: 2.1.7(typescript@5.2.2)(vue@3.3.8)
quasar:
specifier: ^2.13.1
version: 2.13.1
+ reflect-metadata:
+ specifier: ^0.1.13
+ version: 0.1.13
vue:
specifier: ^3.3.4
version: 3.3.8(typescript@5.2.2)
@@ -1490,6 +1496,10 @@ packages:
fsevents: 2.3.3
dev: true
+ /class-transformer@0.5.1:
+ resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==}
+ dev: false
+
/color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
dependencies:
@@ -2667,6 +2677,10 @@ packages:
picomatch: 2.3.1
dev: true
+ /reflect-metadata@0.1.13:
+ resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==}
+ dev: false
+
/requires-port@1.0.0:
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
dev: true
diff --git a/src/js/Bingo.ts b/src/js/Bingo.ts
new file mode 100644
index 0000000..91926c9
--- /dev/null
+++ b/src/js/Bingo.ts
@@ -0,0 +1,164 @@
+import {
+ Transform,
+ Type,
+ instanceToPlain, plainToInstance
+} from 'class-transformer';
+
+function stringCompare(a: string, b: string) {
+ return a.localeCompare(b, undefined, {
+ sensitivity: 'accent'
+ }) === 0;
+}
+
+interface IBingoGame {
+ id: string;
+ name: string;
+ description: string;
+ generator: 'simple' | 'srl_v5' | 'srl_v8';
+ goals: IBingoGoal[];
+ groups: IBingoGroup[];
+}
+
+interface IBingoGoal {
+ name: string;
+ group?: IBingoGroup;
+ tags: string[];
+ possible_spaces: number[];
+}
+
+interface IBingoGroup {
+ name: string;
+ parent?: IBingoGroup;
+}
+
+class BingoGame implements IBingoGame {
+ id: string;
+
+ name: string;
+
+ description: string;
+
+ generator: 'simple' | 'srl_v5' | 'srl_v8' = 'simple';
+
+ @Type(() => BingoGoal)
+ goals: IBingoGoal[] = [];
+
+ @Type(() => BingoGroup)
+ groups: IBingoGroup[] = [];
+
+ constructor(id: string, name: string, description: string) {
+ this.id = id;
+ this.name = name;
+ this.description = description;
+ }
+
+ addGoal(goal: IBingoGoal): void {
+ if (this.goals.some(_goal => stringCompare(_goal.name, goal.name)))
+ return;
+
+ this.goals.push(goal);
+ }
+
+ removeGoal(goal: IBingoGoal): void {
+ this.goals = this.goals.filter(_goal => !stringCompare(_goal.name, goal.name));
+ }
+
+ addGroup(group: IBingoGroup): void {
+ if (this.groups.some(_group => stringCompare(_group.name, group.name)))
+ return;
+
+ this.groups.push(group);
+ }
+
+ removeGroup(group: IBingoGroup): boolean {
+ // Group has parent, don't delete
+ if (this.groups.some(_group => _group.parent && stringCompare(_group.parent.name, group.name)))
+ return false;
+
+ if (this.goals.some(goal => goal.group && stringCompare(goal.group.name, group.name)))
+ return false;
+ // TODO: Make sure no goal is still using group;
+ // boolean override "force_remove" which will remove the group from all goals
+ console.log('deltee', group);
+ this.groups = this.groups.filter(_group => !stringCompare(_group.name, group.name));
+ console.log(this.groups);
+
+ return true;
+ }
+}
+
+class BingoGoal implements IBingoGoal {
+ // id: string; // uuid-by-string maybe? https://www.npmjs.com/package/uuid-by-string
+
+ name: string;
+
+ group?: IBingoGroup;
+
+ tags: string[] = [];
+
+ possible_spaces: number[] = [];
+
+ constructor(name: string, group?: BingoGroup) {
+ this.name = name;
+ this.group = group;
+ }
+
+ addTag(tag: string): IBingoGoal {
+ if (this.tags.includes(tag))
+ return this;
+
+ this.tags.push(tag);
+
+ return this;
+ }
+
+ removeTag(tag: string): void {
+ this.tags = this.tags.filter(_tag => !stringCompare(_tag, tag));
+ }
+
+ setGroup(group: BingoGroup) {
+ this.group = group;
+ }
+
+ setPossibleSpaces(possible_spaces: number[]) {
+ this.possible_spaces = possible_spaces;
+ }
+}
+
+class BingoGroup implements IBingoGroup {
+ name: string;
+
+ @Type(() => BingoGroup)
+ parent?: IBingoGroup;
+
+ constructor(name: string, parent?: BingoGroup) {
+ this.name = name;
+ this.parent = parent;
+ }
+}
+
+const game = new BingoGame('yakuza_0', 'Yakuza 0', 'The funny game we used for all bingos.');
+const battle_group = new BingoGroup('Battle');
+const defeat_enemies = new BingoGroup('Defeat enemies on the street', battle_group);
+game.addGroup(battle_group);
+game.addGroup(defeat_enemies);
+
+const easy = new BingoGoal('Defeat 50 enemies on the street', defeat_enemies).addTag('Easy');
+const normal = new BingoGoal('Defeat 150 enemies on the street', defeat_enemies).addTag('Normal');
+const hard = new BingoGoal('Defeat 300 enemies on the street', defeat_enemies).addTag('Hard');
+
+game.addGoal(easy);
+game.addGoal(normal);
+game.addGoal(hard);
+
+const jsonifiedInstance = instanceToPlain(game);
+// console.log(JSON.stringify(jsonifiedInstance));
+// console.log(JSON.stringify(game));
+
+const json_string = '{"id":"yakuza_0","name":"Yakuza 0","description":"The funny game we used for all bingos.","generator":"simple","goals":[{"name":"Defeat 50 enemies on the street","tags":["Easy"],"group":{"name":"Defeat enemies on the street","parent":{"name":"Battle"}},"possible_spaces":[]},{"name":"Defeat 150 enemies on the street","tags":["Normal"],"group":{"name":"Defeat enemies on the street","parent":{"name":"Battle"}},"possible_spaces":[]},{"name":"Defeat 300 enemies on the street","tags":["Hard"],"group":{"name":"Defeat enemies on the street","parent":{"name":"Battle"}},"possible_spaces":[]}],"groups":[{"name":"Battle"},{"name":"Defeat enemies on the street","parent":{"name":"Battle"}}]}';
+const parsed: JSON = JSON.parse(json_string);
+const parsed_game = plainToInstance(BingoGame, parsed);
+// console.log(JSON.stringify(other_game) === JSON.stringify(jsonifiedInstance));
+
+parsed_game.removeGroup(defeat_enemies);
+console.log(parsed_game.goals);
diff --git a/src/main.ts b/src/main.ts
index 83cd36d..99c69a3 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -17,6 +17,8 @@ import '@quasar/extras/material-icons/material-icons.css';
// Import Quasar css
import 'quasar/src/css/index.sass';
+import 'reflect-metadata';
+
import App from './App.vue';
import router from './router';
diff --git a/src/views/MainPage.vue b/src/views/MainPage.vue
index 1599c78..2047e41 100644
--- a/src/views/MainPage.vue
+++ b/src/views/MainPage.vue
@@ -1,3 +1,7 @@
Hello world
+
+
diff --git a/tsconfig.app.json b/tsconfig.app.json
index 836379c..c04b223 100644
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -17,6 +17,9 @@
"@/*": [
"./src/*"
]
- }
- }
+ },
+ "target": "ESNext",
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true
+ },
}
\ No newline at end of file