Merge pull request 'issue-5-gamesystems' (#6) from issue-5-gamesystems into main
All checks were successful
E2E Testing / test (push) Successful in 1m27s
All checks were successful
E2E Testing / test (push) Successful in 1m27s
Reviewed-on: #6
This commit is contained in:
commit
1cde7f2939
@ -57,7 +57,7 @@ function createWindow(): BrowserWindow {
|
|||||||
{
|
{
|
||||||
label: "Gamesystem",
|
label: "Gamesystem",
|
||||||
click: () => {
|
click: () => {
|
||||||
win!.webContents.send('context-menu', "new-location");
|
win!.webContents.send('context-menu', "new-gamesystem");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
53
e2e/game-model/gamesystems/CreateGamesystem.spec.ts
Normal file
53
e2e/game-model/gamesystems/CreateGamesystem.spec.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { BrowserContext, ElectronApplication, Page, _electron as electron } from 'playwright';
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import * as PATH from 'path';
|
||||||
|
import {GameModel} from "../../../src/app/game-model/GameModel";
|
||||||
|
import {Gamesystem} from "../../src/app/game-model/gamesystems/Gamesystem";
|
||||||
|
import {ScriptAccount} from "../../../src/app/game-model/scriptAccounts/ScriptAccount";
|
||||||
|
import {ModelComponentType} from "../../../src/app/game-model/ModelComponentType";
|
||||||
|
import {SimpleGamesystem} from "../../../src/app/game-model/gamesystems/SimpleGamesystem";
|
||||||
|
import exp = require("node:constants");
|
||||||
|
import {end} from "electron-debug";
|
||||||
|
import {GamesystemTrainer} from "./GamesystemTrainer";
|
||||||
|
import {ProductGamesystem} from "../../../src/app/game-model/gamesystems/ProductGamesystem";
|
||||||
|
test.describe('Test Create Gamesystems', () => {
|
||||||
|
|
||||||
|
test('Test creating gamesystem with invalid name', async => {
|
||||||
|
const gameModel = GamesystemTrainer.givenEmptyGameModel();
|
||||||
|
let result = gameModel.createGamesystem(undefined, undefined);
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
|
||||||
|
result = gameModel.createGamesystem(null, undefined);
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
})
|
||||||
|
|
||||||
|
test("Test creating gamesystem with valid name but without parent", async => {
|
||||||
|
const gameModel = GamesystemTrainer.givenEmptyGameModel();
|
||||||
|
let result = gameModel.createGamesystem(GamesystemTrainer.SIMPLEGAMESYSTEMNAME, undefined);
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
expect(gameModel.gamesystems.length).toEqual(1);
|
||||||
|
expect(gameModel.findGamesystem(GamesystemTrainer.SIMPLEGAMESYSTEMNAME)).toBeDefined();
|
||||||
|
})
|
||||||
|
|
||||||
|
test("Test creating Gamesystem with valid name but with Product Parent", async => {
|
||||||
|
const gameModel = GamesystemTrainer.givenGameModelWithProductGamesytemOnTopLayer();
|
||||||
|
let result = gameModel.createGamesystem(GamesystemTrainer.SIMPLEGAMESYSTEM_LEAF_LEFT, GamesystemTrainer.TOP_PRODUCT_GAMESYSTEM_NAME);
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
expect(result.parentGamesystem!.componentName).toEqual(GamesystemTrainer.TOP_PRODUCT_GAMESYSTEM_NAME);
|
||||||
|
expect(result.parentGamesystem!.innerGamesystems.length).toEqual(3);
|
||||||
|
expect(result.parentGamesystem!.innerGamesystems.includes(result)).toBeTruthy();
|
||||||
|
})
|
||||||
|
|
||||||
|
test("Test creating Gamesystem with valid name but with Simple Parent", async() => {
|
||||||
|
const gameModel = GamesystemTrainer.givenGameModelWithSimpleGamesystemOnTopLayer();
|
||||||
|
let result = gameModel.createGamesystem(GamesystemTrainer.SIMPLEGAMESYSTEM_LEAF_LEFT, GamesystemTrainer.SIMPLEGAMESYSTEMNAME);
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
expect(gameModel.gamesystems.length).toEqual(1);
|
||||||
|
expect(gameModel.gamesystems[0]).toBeInstanceOf(ProductGamesystem);
|
||||||
|
expect(gameModel.gamesystems[0]).toEqual(result.parentGamesystem);
|
||||||
|
expect((gameModel.gamesystems[0] as ProductGamesystem).innerGamesystems.length).toEqual(1);
|
||||||
|
expect((gameModel.gamesystems[0] as ProductGamesystem).innerGamesystems.includes(result)).toBeTruthy();
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
});
|
50
e2e/game-model/gamesystems/FindGamesystem.spec.ts
Normal file
50
e2e/game-model/gamesystems/FindGamesystem.spec.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { BrowserContext, ElectronApplication, Page, _electron as electron } from 'playwright';
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import * as PATH from 'path';
|
||||||
|
import {GameModel} from "../../../src/app/game-model/GameModel";
|
||||||
|
import {Gamesystem} from "../../src/app/game-model/gamesystems/Gamesystem";
|
||||||
|
import {ScriptAccount} from "../../../src/app/game-model/scriptAccounts/ScriptAccount";
|
||||||
|
import {ModelComponentType} from "../../../src/app/game-model/ModelComponentType";
|
||||||
|
import {SimpleGamesystem} from "../../../src/app/game-model/gamesystems/SimpleGamesystem";
|
||||||
|
import exp = require("node:constants");
|
||||||
|
import {end} from "electron-debug";
|
||||||
|
import {GamesystemTrainer} from "./GamesystemTrainer";
|
||||||
|
test.describe('Test Find Gamesystems', () => {
|
||||||
|
const GAMEMODELNAME: string = "GameModel";
|
||||||
|
const SIMPLEGAMESYSTEM_NAME: string = "Simple Gamesystem";
|
||||||
|
|
||||||
|
|
||||||
|
test('Find null or undefined Gamesystem', async () => {
|
||||||
|
const gameModel = new GameModel(GAMEMODELNAME);
|
||||||
|
expect(gameModel.findGamesystem(null)).toBeUndefined();
|
||||||
|
expect(gameModel.findGamesystem(undefined)).toBeUndefined();
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Find non existend Gamesystem', async () => {
|
||||||
|
const gameModel = new GameModel(GAMEMODELNAME);
|
||||||
|
expect(gameModel.findGamesystem("Gamesystem")).toBeUndefined();
|
||||||
|
})
|
||||||
|
|
||||||
|
test("Find existend simple Gamesystem on top layer", async () => {
|
||||||
|
const gameModel = new GameModel(GAMEMODELNAME);
|
||||||
|
const gamesystem = new SimpleGamesystem(SIMPLEGAMESYSTEM_NAME);
|
||||||
|
gameModel.addGamesystem(gamesystem);
|
||||||
|
|
||||||
|
expect(gameModel.findGamesystem(SIMPLEGAMESYSTEM_NAME)).toBeDefined();
|
||||||
|
expect(gameModel.findGamesystem(SIMPLEGAMESYSTEM_NAME)).toEqual(gamesystem);
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Find existent product gamesystem on top layer', async () => {
|
||||||
|
const gameModel = GamesystemTrainer.givenGameModelWithProductGamesytemOnTopLayer();
|
||||||
|
const result = gameModel.findGamesystem(GamesystemTrainer.TOP_PRODUCT_GAMESYSTEM_NAME);
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Find existent simple gamesystem on lower layer', async () => {
|
||||||
|
const gameModel = GamesystemTrainer.givenGameModelWithProductGamesytemOnTopLayer();
|
||||||
|
const result = gameModel.findGamesystem(GamesystemTrainer.SIMPLEGAMESYSTEMNAME);
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
})
|
||||||
|
});
|
55
e2e/game-model/gamesystems/GamesystemTrainer.ts
Normal file
55
e2e/game-model/gamesystems/GamesystemTrainer.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import {GameModel} from "../../../src/app/game-model/GameModel";
|
||||||
|
import {SimpleGamesystem} from "../../../src/app/game-model/gamesystems/SimpleGamesystem";
|
||||||
|
import {ProductGamesystem} from "../../../src/app/game-model/gamesystems/ProductGamesystem";
|
||||||
|
|
||||||
|
export class GamesystemTrainer {
|
||||||
|
|
||||||
|
static GAMEMODELNAME: string = "GAMEMODEL";
|
||||||
|
static SIMPLEGAMESYSTEMNAME: string = "SIMPLE GAMESYSTEM";
|
||||||
|
static TOP_PRODUCT_GAMESYSTEM_NAME: string = "Top Product Gamesystem"
|
||||||
|
|
||||||
|
static SIMPLEGAMESYSTEM2: string = "Simple Gamesystem Leaf 2";
|
||||||
|
static SIMPLEGAMESYSTEM_LEAF_LEFT: string = "Leaf Gamesystem Left"
|
||||||
|
static SIMPLEGAMESYSTEM_LEAF_RIGHT: string = "Leaf Gamesystem Right";
|
||||||
|
|
||||||
|
static givenEmptyGameModel() {
|
||||||
|
return new GameModel(GamesystemTrainer.GAMEMODELNAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
static givenGameModelWithSimpleGamesystemOnTopLayer() {
|
||||||
|
const gameModel = new GameModel(GamesystemTrainer.GAMEMODELNAME);
|
||||||
|
const gamesytem = new SimpleGamesystem(GamesystemTrainer.SIMPLEGAMESYSTEMNAME);
|
||||||
|
gameModel.addGamesystem(gamesytem);
|
||||||
|
|
||||||
|
return gameModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
static givenGameModelWithProductGamesytemOnTopLayer() {
|
||||||
|
const gameModel = new GameModel(GamesystemTrainer.GAMEMODELNAME);
|
||||||
|
const productGamesystem = new ProductGamesystem(this.TOP_PRODUCT_GAMESYSTEM_NAME);
|
||||||
|
const leaf1 = new SimpleGamesystem(this.SIMPLEGAMESYSTEMNAME);
|
||||||
|
const leaf2 = new SimpleGamesystem(this.SIMPLEGAMESYSTEM2);
|
||||||
|
productGamesystem.innerGamesystems.push(leaf1);
|
||||||
|
productGamesystem.innerGamesystems.push(leaf2);
|
||||||
|
gameModel.addGamesystem(productGamesystem);
|
||||||
|
|
||||||
|
return gameModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
static givenGameModelWithProductGamesystemOnLowerLayer() {
|
||||||
|
const gameModel = new GameModel(GamesystemTrainer.GAMEMODELNAME);
|
||||||
|
const top_productGamesystem = new ProductGamesystem(this.TOP_PRODUCT_GAMESYSTEM_NAME);
|
||||||
|
const leaf1 = new ProductGamesystem(this.SIMPLEGAMESYSTEMNAME);
|
||||||
|
const leaf2 = new SimpleGamesystem(this.SIMPLEGAMESYSTEM2);
|
||||||
|
top_productGamesystem.innerGamesystems.push(leaf1);
|
||||||
|
top_productGamesystem.innerGamesystems.push(leaf2);
|
||||||
|
gameModel.addGamesystem(top_productGamesystem);
|
||||||
|
|
||||||
|
const leaf_1_1 = new SimpleGamesystem(this.SIMPLEGAMESYSTEM_LEAF_LEFT);
|
||||||
|
leaf1.addChildGamesystem(leaf_1_1);
|
||||||
|
const leaf_1_2 = new SimpleGamesystem(this.SIMPLEGAMESYSTEM_LEAF_RIGHT);
|
||||||
|
leaf1.addChildGamesystem(leaf_1_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
172
e2e/game-model/gamesystems/SimpleGamesystem.spec.ts
Normal file
172
e2e/game-model/gamesystems/SimpleGamesystem.spec.ts
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
import { BrowserContext, ElectronApplication, Page, _electron as electron } from 'playwright';
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import * as PATH from 'path';
|
||||||
|
import {GameModel} from "../../../src/app/game-model/GameModel";
|
||||||
|
import {Gamesystem} from "../../src/app/game-model/gamesystems/Gamesystem";
|
||||||
|
import {ScriptAccount} from "../../../src/app/game-model/scriptAccounts/ScriptAccount";
|
||||||
|
import {ModelComponentType} from "../../../src/app/game-model/ModelComponentType";
|
||||||
|
import {SimpleGamesystem} from "../../../src/app/game-model/gamesystems/SimpleGamesystem";
|
||||||
|
import exp = require("node:constants");
|
||||||
|
import {end} from "electron-debug";
|
||||||
|
|
||||||
|
test.describe('Test SimpleGamesystem', () => {
|
||||||
|
|
||||||
|
test("Create SimpleState", async () => {
|
||||||
|
const gamesystem = new SimpleGamesystem();
|
||||||
|
let state = gamesystem.createState("Test", "Test2");
|
||||||
|
expect(state.stateLabel).toEqual("Test");
|
||||||
|
expect(state.stateDescription).toEqual("Test2");
|
||||||
|
expect(state.incomingTransitions.length).toEqual(0);
|
||||||
|
expect(state.outgoingTransitions.length).toEqual(0);
|
||||||
|
expect(gamesystem.states.includes(state)).toBeTruthy();
|
||||||
|
|
||||||
|
state = gamesystem.createState(null, null);
|
||||||
|
expect(state).toBeUndefined();
|
||||||
|
expect(gamesystem.states.includes(state)).toBeFalsy();
|
||||||
|
|
||||||
|
state = gamesystem.createState(null, "test2");
|
||||||
|
expect(state).toBeUndefined()
|
||||||
|
expect(gamesystem.states.includes(state)).toBeFalsy();
|
||||||
|
|
||||||
|
state = gamesystem.createState("test2", null);
|
||||||
|
expect(state).toBeDefined();
|
||||||
|
expect(state.stateLabel).toEqual("test2");
|
||||||
|
expect(state.stateDescription).toEqual("");
|
||||||
|
expect(gamesystem.states.includes(state)).toBeTruthy();
|
||||||
|
|
||||||
|
state = gamesystem.createState(undefined, "State");
|
||||||
|
expect(state).toBeUndefined();
|
||||||
|
expect(gamesystem.states.includes(state)).toBeFalsy();
|
||||||
|
|
||||||
|
state = gamesystem.createState("Test3", undefined);
|
||||||
|
expect(state).toBeDefined();
|
||||||
|
expect(state.stateLabel).toEqual("Test3");
|
||||||
|
expect(state.stateDescription).toEqual("");
|
||||||
|
expect(gamesystem.states.includes(state)).toBeTruthy();
|
||||||
|
|
||||||
|
state = gamesystem.createState("", "");
|
||||||
|
expect(state).toBeDefined();
|
||||||
|
expect(state.stateLabel).toEqual("");
|
||||||
|
expect(state.stateDescription).toEqual("");
|
||||||
|
expect(gamesystem.states.includes(state)).toBeTruthy();
|
||||||
|
|
||||||
|
state = gamesystem.createState("Test3", "");
|
||||||
|
expect(state).toBeUndefined();
|
||||||
|
expect(gamesystem.states.includes(state)).toBeFalsy();
|
||||||
|
})
|
||||||
|
|
||||||
|
test("Create SimpleTransition", async () => {
|
||||||
|
const gamesystem = new SimpleGamesystem();
|
||||||
|
const startingState = gamesystem.createState("StartingState", "")!;
|
||||||
|
const endingState = gamesystem.createState("EndingState", "")!
|
||||||
|
|
||||||
|
let transition = gamesystem.createTransition(startingState, endingState);
|
||||||
|
expect(transition).toBeDefined();
|
||||||
|
expect(transition.startingState).toEqual(startingState);
|
||||||
|
expect(transition.endingState).toEqual(endingState);
|
||||||
|
expect(startingState.outgoingTransitions.includes(transition)).toBeTruthy();
|
||||||
|
expect(endingState.incomingTransitions.includes(transition)).toBeTruthy();
|
||||||
|
expect(gamesystem.transitions.includes(transition)).toBeTruthy();
|
||||||
|
|
||||||
|
transition = gamesystem.createTransition(null, null);
|
||||||
|
expect(transition).toBeUndefined();
|
||||||
|
|
||||||
|
transition = gamesystem.createTransition(null, endingState);
|
||||||
|
expect(transition).toBeUndefined();
|
||||||
|
|
||||||
|
transition = gamesystem.createTransition(undefined, undefined);
|
||||||
|
expect(transition).toBeUndefined();
|
||||||
|
|
||||||
|
transition = gamesystem.createTransition(undefined, endingState);
|
||||||
|
expect(transition).toBeUndefined();
|
||||||
|
|
||||||
|
transition = gamesystem.createTransition(startingState, null);
|
||||||
|
expect(transition).toBeUndefined();
|
||||||
|
|
||||||
|
transition = gamesystem.createTransition(startingState, undefined);
|
||||||
|
expect(transition).toBeUndefined();
|
||||||
|
|
||||||
|
transition = gamesystem.createTransition(startingState, startingState);
|
||||||
|
expect(transition).toBeUndefined();
|
||||||
|
|
||||||
|
transition = gamesystem.createTransition(startingState, endingState);
|
||||||
|
expect(transition).toBeUndefined();
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
test("Remove SimpleState", async () => {
|
||||||
|
const gamesystem = new SimpleGamesystem("Test");
|
||||||
|
const state1 = gamesystem.createState("State1", "");
|
||||||
|
|
||||||
|
const state1_delete = gamesystem.removeState(state1);
|
||||||
|
expect(state1_delete).toBeTruthy();
|
||||||
|
expect(gamesystem.states.includes(state1)).toBeFalsy();
|
||||||
|
|
||||||
|
let startingState = gamesystem.createState("Start", "End");
|
||||||
|
let endingState = gamesystem.createState("End", "");
|
||||||
|
let transition = gamesystem.createTransition(startingState, endingState);
|
||||||
|
let result = gamesystem.removeState(startingState);
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(gamesystem.states.includes(startingState)).toBeFalsy();
|
||||||
|
expect(gamesystem.states.includes(endingState)).toBeTruthy();
|
||||||
|
expect(gamesystem.transitions.length).toEqual(0);
|
||||||
|
|
||||||
|
startingState = gamesystem.createState("Start");
|
||||||
|
transition = gamesystem.createTransition(startingState, endingState);
|
||||||
|
gamesystem.removeState(endingState);
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(gamesystem.states.includes(startingState)).toBeTruthy();
|
||||||
|
expect(gamesystem.states.includes(endingState)).toBeFalsy();
|
||||||
|
expect(gamesystem.transitions.length).toEqual(0);
|
||||||
|
|
||||||
|
endingState = gamesystem.createState("End");
|
||||||
|
transition = gamesystem.createTransition(startingState, endingState);
|
||||||
|
const testingState = gamesystem.createState("TestingState", "");
|
||||||
|
result = gamesystem.removeState(testingState);
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(gamesystem.transitions.includes(transition)).toBeTruthy();
|
||||||
|
|
||||||
|
const gamesystem2 = new SimpleGamesystem("test2");
|
||||||
|
const state2 = gamesystem2.createState("Test", "");
|
||||||
|
result = gamesystem.removeState(state2);
|
||||||
|
expect(result).toBeFalsy();
|
||||||
|
|
||||||
|
result = gamesystem.removeState(null);
|
||||||
|
expect(result).toBeFalsy();
|
||||||
|
|
||||||
|
result = gamesystem.removeState(undefined);
|
||||||
|
expect(result).toBeFalsy();
|
||||||
|
})
|
||||||
|
|
||||||
|
test("Remove SimpleTransition", async () => {
|
||||||
|
const gamesystem = new SimpleGamesystem("Gamesystem");
|
||||||
|
const A = gamesystem.createState("A");
|
||||||
|
const B = gamesystem.createState("B");
|
||||||
|
let AB = gamesystem.createTransition(A, B);
|
||||||
|
|
||||||
|
let result = gamesystem.removeTransition(AB);
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(gamesystem.transitions.includes(AB)).toBeFalsy();
|
||||||
|
|
||||||
|
AB = gamesystem.createTransition(A, B);
|
||||||
|
const C = gamesystem.createState("C");
|
||||||
|
const D = gamesystem.createState("D");
|
||||||
|
let CD = gamesystem.createTransition(C, D);
|
||||||
|
|
||||||
|
result = gamesystem.removeTransition(AB);
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(gamesystem.transitions.includes(AB)).toBeFalsy();
|
||||||
|
expect(gamesystem.transitions.includes(CD)).toBeTruthy();
|
||||||
|
|
||||||
|
let BA = gamesystem.createTransition(B, A);
|
||||||
|
AB = gamesystem.createTransition(A, B);
|
||||||
|
result = gamesystem.removeTransition(AB);
|
||||||
|
expect(result).toBeTruthy();
|
||||||
|
expect(gamesystem.transitions.includes(AB)).toBeFalsy();
|
||||||
|
expect(gamesystem.transitions.includes(BA)).toBeTruthy();
|
||||||
|
|
||||||
|
result = gamesystem.removeTransition(AB);
|
||||||
|
expect(result).toBeFalsy();
|
||||||
|
|
||||||
|
})
|
||||||
|
});
|
@ -21,24 +21,24 @@ test.describe('Test ScriptAccounts', () => {
|
|||||||
test("Test Adding ScriptAccounts", async () => {
|
test("Test Adding ScriptAccounts", async () => {
|
||||||
const gameModel: GameModel = new GameModel("GameModel");
|
const gameModel: GameModel = new GameModel("GameModel");
|
||||||
|
|
||||||
let scriptAccount =gameModel.addScriptAccount("ScriptAccount");
|
let scriptAccount =gameModel.createScriptAccount("ScriptAccount");
|
||||||
expect(scriptAccount).toBeDefined();
|
expect(scriptAccount).toBeDefined();
|
||||||
expect(gameModel.scriptAccounts.length).toEqual(1);
|
expect(gameModel.scriptAccounts.length).toEqual(1);
|
||||||
expect(gameModel.scriptAccounts.includes(scriptAccount)).toBeTruthy();
|
expect(gameModel.scriptAccounts.includes(scriptAccount)).toBeTruthy();
|
||||||
|
|
||||||
//Test adding scriptAccount with already existing name
|
//Test adding scriptAccount with already existing name
|
||||||
const scriptAccount2 = gameModel.addScriptAccount("ScriptAccount")
|
const scriptAccount2 = gameModel.createScriptAccount("ScriptAccount")
|
||||||
expect(scriptAccount2).toBeUndefined();
|
expect(scriptAccount2).toBeUndefined();
|
||||||
expect(gameModel.scriptAccounts.length).toEqual(1);
|
expect(gameModel.scriptAccounts.length).toEqual(1);
|
||||||
|
|
||||||
//Test for adding invalid names as scriptaccount names (null/undefined/empty)
|
//Test for adding invalid names as scriptaccount names (null/undefined/empty)
|
||||||
let result = gameModel.addScriptAccount(null);
|
let result = gameModel.createScriptAccount(null);
|
||||||
expect(result).toBeUndefined();
|
expect(result).toBeUndefined();
|
||||||
expect(gameModel.scriptAccounts.length).toEqual(1);
|
expect(gameModel.scriptAccounts.length).toEqual(1);
|
||||||
result = gameModel.addScriptAccount(undefined);
|
result = gameModel.createScriptAccount(undefined);
|
||||||
expect(result).toBeUndefined();
|
expect(result).toBeUndefined();
|
||||||
expect(gameModel.scriptAccounts.length).toEqual(1);
|
expect(gameModel.scriptAccounts.length).toEqual(1);
|
||||||
result = gameModel.addScriptAccount("");
|
result = gameModel.createScriptAccount("");
|
||||||
expect(result).toBeUndefined();
|
expect(result).toBeUndefined();
|
||||||
expect(gameModel.scriptAccounts.length).toEqual(1);
|
expect(gameModel.scriptAccounts.length).toEqual(1);
|
||||||
})
|
})
|
||||||
@ -50,7 +50,7 @@ test.describe('Test ScriptAccounts', () => {
|
|||||||
gameModel.removeScriptAccount(scriptAccount);
|
gameModel.removeScriptAccount(scriptAccount);
|
||||||
expect(gameModel.scriptAccounts.length).toEqual(0);
|
expect(gameModel.scriptAccounts.length).toEqual(0);
|
||||||
|
|
||||||
scriptAccount = gameModel.addScriptAccount("ScriptAccount");
|
scriptAccount = gameModel.createScriptAccount("ScriptAccount");
|
||||||
gameModel.removeScriptAccount(scriptAccount);
|
gameModel.removeScriptAccount(scriptAccount);
|
||||||
expect(gameModel.scriptAccounts.length).toEqual(0);
|
expect(gameModel.scriptAccounts.length).toEqual(0);
|
||||||
|
|
||||||
@ -60,8 +60,8 @@ test.describe('Test ScriptAccounts', () => {
|
|||||||
gameModel.removeScriptAccount(null);
|
gameModel.removeScriptAccount(null);
|
||||||
expect(gameModel.scriptAccounts.length).toEqual(0);
|
expect(gameModel.scriptAccounts.length).toEqual(0);
|
||||||
|
|
||||||
scriptAccount = gameModel.addScriptAccount(scriptAccount);
|
scriptAccount = gameModel.createScriptAccount(scriptAccount);
|
||||||
let scriptAccount2 = gameModel.addScriptAccount("ScriptAccount 2");
|
let scriptAccount2 = gameModel.createScriptAccount("ScriptAccount 2");
|
||||||
|
|
||||||
gameModel.removeScriptAccount(scriptAccount);
|
gameModel.removeScriptAccount(scriptAccount);
|
||||||
expect(gameModel.scriptAccounts.length).toEqual(1);
|
expect(gameModel.scriptAccounts.length).toEqual(1);
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
<div class="full-height-container">
|
<div class="full-height-container">
|
||||||
<button mat-icon-button class="small-icon-button" [ngClass]="openContent === ModelComponentType.SCRIPTACCOUNT ? 'selected':''"
|
<button mat-icon-button class="small-icon-button" [ngClass]="openContent === ModelComponentType.SCRIPTACCOUNT ? 'selected':''"
|
||||||
(click)="openScriptAccountsOverview()"><mat-icon>inventory_2</mat-icon></button>
|
(click)="openScriptAccountsOverview()"><mat-icon>inventory_2</mat-icon></button>
|
||||||
|
<button mat-icon-button class="small-icon-button" [ngClass]="openContent === ModelComponentType.GAMESYTEM ? 'selected':''"
|
||||||
|
(click)="openGamesystemsOverview()"><mat-icon>manufacturing</mat-icon></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -17,13 +19,15 @@
|
|||||||
<button mat-icon-button class="small-icon-button close-sidenav-btn" (click)="closeContentOverview()"><mat-icon>close</mat-icon></button>
|
<button mat-icon-button class="small-icon-button close-sidenav-btn" (click)="closeContentOverview()"><mat-icon>close</mat-icon></button>
|
||||||
<mat-menu #contentMenu="matMenu">
|
<mat-menu #contentMenu="matMenu">
|
||||||
<button mat-menu-item (click)="openScriptAccountsOverview()">{{ModelComponentTypeUtillities.toString(ModelComponentType.SCRIPTACCOUNT)}}</button>
|
<button mat-menu-item (click)="openScriptAccountsOverview()">{{ModelComponentTypeUtillities.toString(ModelComponentType.SCRIPTACCOUNT)}}</button>
|
||||||
|
<button mat-menu-item (click)="openGamesystemsOverview()">{{ModelComponentTypeUtillities.toString(ModelComponentType.GAMESYTEM)}}</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
</div>
|
</div>
|
||||||
<app-script-account-overview #scriptAccountOverview [gameModel]="gameModel" (onOpenScriptAccount)="openModelComponent($event)"></app-script-account-overview>
|
<app-script-account-overview *ngIf="openContent == ModelComponentType.SCRIPTACCOUNT" #scriptAccountOverview [gameModel]="gameModel" (onOpenScriptAccount)="openModelComponent($event)"></app-script-account-overview>
|
||||||
|
<app-gamescript-overview *ngIf="openContent == ModelComponentType.GAMESYTEM" #gamesystemOverview [gameModel]="gameModel" (openGamesystemEditor)="openModelComponent($event)"></app-gamescript-overview>
|
||||||
</mat-drawer>
|
</mat-drawer>
|
||||||
|
|
||||||
<div class="example-sidenav-content">
|
<div class="example-sidenav-content">
|
||||||
<app-editor #editor></app-editor>
|
<app-editor #editor (onModelNameUpdate)="onModelNameUpdate()"></app-editor>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</mat-drawer-container>
|
</mat-drawer-container>
|
||||||
|
@ -13,6 +13,8 @@ import {
|
|||||||
import {MatDialog} from "@angular/material/dialog";
|
import {MatDialog} from "@angular/material/dialog";
|
||||||
import {DeleteConfirmationDialogComponent} from "./delete-confirmation-dialog/delete-confirmation-dialog.component";
|
import {DeleteConfirmationDialogComponent} from "./delete-confirmation-dialog/delete-confirmation-dialog.component";
|
||||||
import {ScriptAccount} from "./game-model/scriptAccounts/ScriptAccount";
|
import {ScriptAccount} from "./game-model/scriptAccounts/ScriptAccount";
|
||||||
|
import {GamescriptOverviewComponent} from "./side-overviews/gamescript-overview/gamescript-overview.component";
|
||||||
|
import {SimpleGamesystem} from "./game-model/gamesystems/SimpleGamesystem";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
@ -25,6 +27,7 @@ export class AppComponent implements OnInit{
|
|||||||
@ViewChild('drawer') drawer: MatDrawerContainer|undefined
|
@ViewChild('drawer') drawer: MatDrawerContainer|undefined
|
||||||
@ViewChild('editor') editor: EditorComponent|undefined
|
@ViewChild('editor') editor: EditorComponent|undefined
|
||||||
@ViewChild('scriptAccountOverview') scriptAccountOverview: ScriptAccountOverviewComponent | undefined
|
@ViewChild('scriptAccountOverview') scriptAccountOverview: ScriptAccountOverviewComponent | undefined
|
||||||
|
@ViewChild('gamesystemOverview') gamesystemOverview: GamescriptOverviewComponent | undefined
|
||||||
|
|
||||||
gameModel: GameModel | undefined
|
gameModel: GameModel | undefined
|
||||||
|
|
||||||
@ -42,29 +45,89 @@ export class AppComponent implements OnInit{
|
|||||||
|
|
||||||
electronService.ipcRenderer.on('context-menu', (event: any, message: string) => {
|
electronService.ipcRenderer.on('context-menu', (event: any, message: string) => {
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
if(message == "edit") {
|
this.onContextMenuMessageRecieved(message);
|
||||||
if(this.openContent == ModelComponentType.SCRIPTACCOUNT && this.scriptAccountOverview != undefined && this.scriptAccountOverview.selectedScriptAccount != undefined) {
|
});
|
||||||
this.editor!.openGameModelComponent(this.scriptAccountOverview.selectedScriptAccount!);
|
|
||||||
}
|
|
||||||
} else if(message == "delete") {
|
|
||||||
const affectedModelComponent = this.getSelectedModelComponent();
|
|
||||||
const dialogRef = this.dialog.open(DeleteConfirmationDialogComponent, {data: affectedModelComponent, minWidth: "400px"});
|
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(res => {
|
|
||||||
if(res != undefined && res) {
|
|
||||||
if(affectedModelComponent instanceof ScriptAccount) {
|
|
||||||
this.gameModel!.removeScriptAccount(affectedModelComponent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
console.log('Run in browser');
|
console.log('Run in browser');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onContextMenuMessageRecieved(message: string) {
|
||||||
|
if(message == "edit") {
|
||||||
|
this.onEditModelComponent();
|
||||||
|
} else if(message == "delete") {
|
||||||
|
this.onDeleteModelComponent();
|
||||||
|
} else if(message.startsWith("new")) {
|
||||||
|
const splittedMessage = message.split("-");
|
||||||
|
const modelComponentType = ModelComponentTypeUtillities.fromString(splittedMessage[1]);
|
||||||
|
if(modelComponentType != undefined) {
|
||||||
|
this.onCreateModelComponent(modelComponentType);
|
||||||
|
} else {
|
||||||
|
console.log("[ERROR] [App-Component] Unknown Context-Menu Command!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onEditModelComponent() {
|
||||||
|
switch (this.openContent!) {
|
||||||
|
case ModelComponentType.SCRIPTACCOUNT: {
|
||||||
|
if(this.scriptAccountOverview!.selectedScriptAccount != undefined) {
|
||||||
|
this.editor!.openGameModelComponent(this.scriptAccountOverview!.selectedScriptAccount);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case ModelComponentType.GAMESYTEM: {
|
||||||
|
if(this.gamesystemOverview!.selectedGamesystem != undefined) {
|
||||||
|
const gamesystem = this.gameModel!.findGamesystem(this.gamesystemOverview!.selectedGamesystemName!);
|
||||||
|
this.editor!.openGameModelComponent(gamesystem!);
|
||||||
|
}
|
||||||
|
} break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onDeleteModelComponent() {
|
||||||
|
const affectedModelComponent = this.getSelectedModelComponent();
|
||||||
|
const dialogRef = this.dialog.open(DeleteConfirmationDialogComponent, {data: affectedModelComponent, minWidth: "400px"});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(res => {
|
||||||
|
if(res != undefined && res) {
|
||||||
|
if(affectedModelComponent instanceof ScriptAccount) {
|
||||||
|
this.gameModel!.removeScriptAccount(affectedModelComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private onCreateModelComponent(modelComponentType: ModelComponentType) {
|
||||||
|
switch (modelComponentType) {
|
||||||
|
case ModelComponentType.SCRIPTACCOUNT: this.onCreateNewScriptAccount(); break
|
||||||
|
case ModelComponentType.GAMESYTEM: this.onCreateNewGamesystem(); break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onCreateNewScriptAccount() {
|
||||||
|
const createdScriptAccount = this.gameModel!.createScriptAccount("New ScriptAccount");
|
||||||
|
if(createdScriptAccount != undefined) {
|
||||||
|
this.editor?.openGameModelComponent(createdScriptAccount);
|
||||||
|
} else {
|
||||||
|
console.log("[DEBUG] [App-Component] ScriptAccount could not be created (Name not unique)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onCreateNewGamesystem() {
|
||||||
|
let parentGamesystemName = undefined
|
||||||
|
if(this.openContent != ModelComponentType.GAMESYTEM) {
|
||||||
|
this.openGamesystemsOverview();
|
||||||
|
} else {
|
||||||
|
parentGamesystemName = this.gamesystemOverview!.selectedGamesystemName;
|
||||||
|
}
|
||||||
|
const createdGamesystem = this.gameModel!.createGamesystem("New Gamesystem", parentGamesystemName);
|
||||||
|
if(createdGamesystem != undefined) {
|
||||||
|
this.gamesystemOverview!.refresh();
|
||||||
|
this.editor?.openGameModelComponent(createdGamesystem!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private getSelectedModelComponent(): ModelComponent | undefined {
|
private getSelectedModelComponent(): ModelComponent | undefined {
|
||||||
if(this.openContent == ModelComponentType.SCRIPTACCOUNT) {
|
if(this.openContent == ModelComponentType.SCRIPTACCOUNT) {
|
||||||
if(this.scriptAccountOverview != undefined) {
|
if(this.scriptAccountOverview != undefined) {
|
||||||
@ -78,8 +141,19 @@ export class AppComponent implements OnInit{
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.gameModel = new GameModel("No More");
|
this.gameModel = new GameModel("No More");
|
||||||
this.gameModel.addScriptAccount("Temperature");
|
this.gameModel.createScriptAccount("Temperature");
|
||||||
this.gameModel.addScriptAccount("Luftfeuchtigkeit");
|
this.gameModel.createScriptAccount("Luftfeuchtigkeit");
|
||||||
|
|
||||||
|
const weather = new SimpleGamesystem("Weather");
|
||||||
|
const season = new SimpleGamesystem("Season");
|
||||||
|
|
||||||
|
const springState = season.createState("Spring", "Spring, also known as springtime, is one of the four temperate seasons, succeeding winter and preceding summer.");
|
||||||
|
const summerState = season.createState("Summer", "Summer is the hottest and brightest of the four temperate seasons, occurring after spring and before autumn. ");
|
||||||
|
|
||||||
|
season.createTransition(springState!, summerState!);
|
||||||
|
|
||||||
|
this.gameModel.addGamesystem(weather)
|
||||||
|
this.gameModel.addGamesystem(season);
|
||||||
}
|
}
|
||||||
|
|
||||||
openScriptAccountsOverview() {
|
openScriptAccountsOverview() {
|
||||||
@ -87,6 +161,11 @@ export class AppComponent implements OnInit{
|
|||||||
this.drawer!.open();
|
this.drawer!.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openGamesystemsOverview() {
|
||||||
|
this.openContent = ModelComponentType.GAMESYTEM;
|
||||||
|
this.drawer!.open();
|
||||||
|
}
|
||||||
|
|
||||||
protected readonly ModelComponentType = ModelComponentType;
|
protected readonly ModelComponentType = ModelComponentType;
|
||||||
|
|
||||||
closeContentOverview() {
|
closeContentOverview() {
|
||||||
@ -102,6 +181,11 @@ export class AppComponent implements OnInit{
|
|||||||
} else {
|
} else {
|
||||||
console.log("[WARN] [App.Component] Editor is undefined")
|
console.log("[WARN] [App.Component] Editor is undefined")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onModelNameUpdate() {
|
||||||
|
if(this.openContent == ModelComponentType.GAMESYTEM) {
|
||||||
|
this.gamesystemOverview!.refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
|||||||
import {MatIcon} from "@angular/material/icon";
|
import {MatIcon} from "@angular/material/icon";
|
||||||
import {MatToolbar} from "@angular/material/toolbar";
|
import {MatToolbar} from "@angular/material/toolbar";
|
||||||
import {MatButton, MatIconButton, MatMiniFabButton} from "@angular/material/button";
|
import {MatButton, MatIconButton, MatMiniFabButton} from "@angular/material/button";
|
||||||
import {MatError, MatFormField, MatLabel} from "@angular/material/form-field";
|
import {MatError, MatFormField, MatHint, MatLabel} from "@angular/material/form-field";
|
||||||
import {MatInput} from "@angular/material/input";
|
import {MatInput} from "@angular/material/input";
|
||||||
import {MatDrawer, MatDrawerContainer} from "@angular/material/sidenav";
|
import {MatDrawer, MatDrawerContainer} from "@angular/material/sidenav";
|
||||||
import {MatMenu, MatMenuItem, MatMenuTrigger} from "@angular/material/menu";
|
import {MatMenu, MatMenuItem, MatMenuTrigger} from "@angular/material/menu";
|
||||||
@ -28,6 +28,29 @@ import {ScriptAccountEditorComponent} from "./editor/script-account-editor/scrip
|
|||||||
import {ModelComponentEditorComponent} from "./editor/model-component-editor/model-component-editor.component";
|
import {ModelComponentEditorComponent} from "./editor/model-component-editor/model-component-editor.component";
|
||||||
import {DeleteConfirmationDialogComponent} from "./delete-confirmation-dialog/delete-confirmation-dialog.component";
|
import {DeleteConfirmationDialogComponent} from "./delete-confirmation-dialog/delete-confirmation-dialog.component";
|
||||||
import {MatDialogActions, MatDialogContent, MatDialogTitle} from "@angular/material/dialog";
|
import {MatDialogActions, MatDialogContent, MatDialogTitle} from "@angular/material/dialog";
|
||||||
|
import {GamescriptOverviewComponent} from "./side-overviews/gamescript-overview/gamescript-overview.component";
|
||||||
|
import {MatTree, MatTreeModule} from "@angular/material/tree";
|
||||||
|
import {GamesystemEditorComponent} from "./editor/gamesystem-editor/gamesystem-editor.component";
|
||||||
|
import {
|
||||||
|
SimpleGamesystemEditorComponent
|
||||||
|
} from "./editor/gamesystem-editor/simple-gamesystem-editor/simple-gamesystem-editor.component";
|
||||||
|
import {
|
||||||
|
SimpleStateEditorComponent
|
||||||
|
} from "./editor/gamesystem-editor/state-editor/simple-state-editor/simple-state-editor.component";
|
||||||
|
import {
|
||||||
|
MatCell,
|
||||||
|
MatCellDef,
|
||||||
|
MatColumnDef,
|
||||||
|
MatHeaderCell,
|
||||||
|
MatHeaderCellDef,
|
||||||
|
MatHeaderRow, MatHeaderRowDef, MatRow, MatRowDef,
|
||||||
|
MatTable
|
||||||
|
} from "@angular/material/table";
|
||||||
|
import {MatCheckbox} from "@angular/material/checkbox";
|
||||||
|
import {
|
||||||
|
SimpleTransitionEditorComponent
|
||||||
|
} from "./editor/gamesystem-editor/transition-editor/simple-transition-editor/simple-transition-editor.component";
|
||||||
|
import {MatOption, MatSelect} from "@angular/material/select";
|
||||||
|
|
||||||
// AoT requires an exported function for factories
|
// AoT requires an exported function for factories
|
||||||
const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new TranslateHttpLoader(http, './assets/i18n/', '.json');
|
const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new TranslateHttpLoader(http, './assets/i18n/', '.json');
|
||||||
@ -39,7 +62,12 @@ const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new Transl
|
|||||||
EditorComponent,
|
EditorComponent,
|
||||||
ScriptAccountEditorComponent,
|
ScriptAccountEditorComponent,
|
||||||
ModelComponentEditorComponent,
|
ModelComponentEditorComponent,
|
||||||
DeleteConfirmationDialogComponent
|
DeleteConfirmationDialogComponent,
|
||||||
|
GamescriptOverviewComponent,
|
||||||
|
GamesystemEditorComponent,
|
||||||
|
SimpleGamesystemEditorComponent,
|
||||||
|
SimpleStateEditorComponent,
|
||||||
|
SimpleTransitionEditorComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
@ -79,6 +107,21 @@ const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new Transl
|
|||||||
MatDialogContent,
|
MatDialogContent,
|
||||||
MatDialogActions,
|
MatDialogActions,
|
||||||
MatMiniFabButton,
|
MatMiniFabButton,
|
||||||
|
MatTreeModule,
|
||||||
|
MatTable,
|
||||||
|
MatColumnDef,
|
||||||
|
MatHeaderCell,
|
||||||
|
MatHeaderCellDef,
|
||||||
|
MatCellDef,
|
||||||
|
MatCell,
|
||||||
|
MatHeaderRow,
|
||||||
|
MatRow,
|
||||||
|
MatHeaderRowDef,
|
||||||
|
MatRowDef,
|
||||||
|
MatCheckbox,
|
||||||
|
MatSelect,
|
||||||
|
MatOption,
|
||||||
|
MatHint
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
<mat-tab-group>
|
<mat-tab-group>
|
||||||
<mat-tab *ngFor="let modelComponent of gameModelComponents">
|
<mat-tab *ngFor="let modelComponent of gameModelComponents">
|
||||||
<ng-template mat-tab-label>
|
<ng-template mat-tab-label>
|
||||||
<mat-icon class="example-tab-icon unsaved" [ngClass]="modelComponent.unsaved? 'unsaved':'saved'">inventory_2</mat-icon>
|
<mat-icon class="example-tab-icon unsaved" *ngIf="modelComponent.type === ModelComponentType.SCRIPTACCOUNT" [ngClass]="modelComponent.unsaved? 'unsaved':'saved'">inventory_2</mat-icon>
|
||||||
|
<mat-icon class="example-tab-icon unsaved" *ngIf="modelComponent.type === ModelComponentType.GAMESYTEM" [ngClass]="modelComponent.unsaved? 'unsaved':'saved'">code</mat-icon>
|
||||||
|
|
||||||
<span [ngClass]="modelComponent.unsaved? 'unsaved':'saved'">{{modelComponent.componentName}}</span>
|
<span [ngClass]="modelComponent.unsaved? 'unsaved':'saved'">{{modelComponent.componentName}}</span>
|
||||||
<button class="content-label close-btn" mat-icon-button (click)="closeGameModelComponent(modelComponent)"><mat-icon>close</mat-icon></button>
|
<button class="content-label close-btn" mat-icon-button (click)="closeGameModelComponent(modelComponent)"><mat-icon>close</mat-icon></button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<app-model-component-editor [modelComponent]="modelComponent"></app-model-component-editor>
|
<app-model-component-editor [modelComponent]="modelComponent" (onModelNameUpdated)="onModelNameUpdate()"></app-model-component-editor>
|
||||||
<app-script-account-editor *ngIf="modelComponent.type === ModelComponentType.SCRIPTACCOUNT"
|
<app-script-account-editor *ngIf="modelComponent.type === ModelComponentType.SCRIPTACCOUNT"
|
||||||
[scriptAccount]="convertModelComponentToScriptAccount(modelComponent)"></app-script-account-editor>
|
[scriptAccount]="convertModelComponentToScriptAccount(modelComponent)"></app-script-account-editor>
|
||||||
|
<app-gamesystem-editor *ngIf="modelComponent.type == ModelComponentType.GAMESYTEM"
|
||||||
|
[gamesystem]="convertModelComponentToGamesystem(modelComponent)"></app-gamesystem-editor>
|
||||||
|
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
</mat-tab-group>
|
</mat-tab-group>
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import {Component, Input} from '@angular/core';
|
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
||||||
import {GameModel} from "../game-model/GameModel";
|
import {GameModel} from "../game-model/GameModel";
|
||||||
import {ModelComponent} from "../game-model/ModelComponent";
|
import {ModelComponent} from "../game-model/ModelComponent";
|
||||||
import {ModelComponentType} from "../game-model/ModelComponentType";
|
import {ModelComponentType} from "../game-model/ModelComponentType";
|
||||||
import {ScriptAccount} from "../game-model/scriptAccounts/ScriptAccount";
|
import {ScriptAccount} from "../game-model/scriptAccounts/ScriptAccount";
|
||||||
|
import {Gamesystem} from "../game-model/gamesystems/Gamesystem";
|
||||||
|
import {State} from "../game-model/gamesystems/State";
|
||||||
|
import {Transition} from "../game-model/gamesystems/Transition";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-editor',
|
selector: 'app-editor',
|
||||||
@ -11,6 +14,7 @@ import {ScriptAccount} from "../game-model/scriptAccounts/ScriptAccount";
|
|||||||
})
|
})
|
||||||
export class EditorComponent {
|
export class EditorComponent {
|
||||||
gameModelComponents: ModelComponent[] = [];
|
gameModelComponents: ModelComponent[] = [];
|
||||||
|
@Output("onModelNameUpdate") onModelNameUpdateEmitter = new EventEmitter<boolean>();
|
||||||
|
|
||||||
openGameModelComponent(gameModelComponent: ModelComponent) {
|
openGameModelComponent(gameModelComponent: ModelComponent) {
|
||||||
if(!this.gameModelComponents.includes(gameModelComponent)) {
|
if(!this.gameModelComponents.includes(gameModelComponent)) {
|
||||||
@ -28,5 +32,15 @@ export class EditorComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
convertModelComponentToGamesystem(modelComponent: ModelComponent) {
|
||||||
|
if(modelComponent instanceof Gamesystem) {
|
||||||
|
return modelComponent as Gamesystem<State<any>, Transition<any>>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected readonly ModelComponentType = ModelComponentType;
|
protected readonly ModelComponentType = ModelComponentType;
|
||||||
|
|
||||||
|
onModelNameUpdate() {
|
||||||
|
this.onModelNameUpdateEmitter.emit(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
<app-simple-gamesystem-editor *ngIf="isSimpleGamesystem()" [simpleGamesystem]="convertGamesystemToSimpleGamesystem()"></app-simple-gamesystem-editor>
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { GamesystemEditorComponent } from './gamesystem-editor.component';
|
||||||
|
|
||||||
|
describe('GamesystemEditorComponent', () => {
|
||||||
|
let component: GamesystemEditorComponent;
|
||||||
|
let fixture: ComponentFixture<GamesystemEditorComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [GamesystemEditorComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(GamesystemEditorComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,33 @@
|
|||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {GameModel} from "../../game-model/GameModel";
|
||||||
|
import {Gamesystem} from "../../game-model/gamesystems/Gamesystem";
|
||||||
|
import {State} from "../../game-model/gamesystems/State";
|
||||||
|
import {Transition} from "../../game-model/gamesystems/Transition";
|
||||||
|
import {SimpleGamesystem} from "../../game-model/gamesystems/SimpleGamesystem";
|
||||||
|
import {ProductGamesystem} from "../../game-model/gamesystems/ProductGamesystem";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-gamesystem-editor',
|
||||||
|
templateUrl: './gamesystem-editor.component.html',
|
||||||
|
styleUrl: './gamesystem-editor.component.scss'
|
||||||
|
})
|
||||||
|
export class GamesystemEditorComponent {
|
||||||
|
|
||||||
|
@Input() gamesystem: Gamesystem<State<any>, Transition<any>> | undefined
|
||||||
|
|
||||||
|
isSimpleGamesystem() {
|
||||||
|
return this.gamesystem instanceof SimpleGamesystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
convertGamesystemToSimpleGamesystem() {
|
||||||
|
if(this.gamesystem instanceof SimpleGamesystem) {
|
||||||
|
return this.gamesystem as SimpleGamesystem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
convertGamesystemToProductGamesystem() {
|
||||||
|
if(this.gamesystem instanceof ProductGamesystem) {
|
||||||
|
return this.gamesystem as ProductGamesystem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
<app-simple-state-editor [states]="simpleGamesystem!.states" [gamesystem]="simpleGamesystem"></app-simple-state-editor>
|
||||||
|
<app-simple-transition-editor class="transition-editor" [gamesystem]="simpleGamesystem"></app-simple-transition-editor>
|
@ -0,0 +1,3 @@
|
|||||||
|
.transition-editor {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SimpleGamesystemEditorComponent } from './simple-gamesystem-editor.component';
|
||||||
|
|
||||||
|
describe('SimpleGamesystemEditorComponent', () => {
|
||||||
|
let component: SimpleGamesystemEditorComponent;
|
||||||
|
let fixture: ComponentFixture<SimpleGamesystemEditorComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [SimpleGamesystemEditorComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(SimpleGamesystemEditorComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,16 @@
|
|||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {SimpleGamesystem} from "../../../game-model/gamesystems/SimpleGamesystem";
|
||||||
|
import {MatTableDataSource} from "@angular/material/table";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-simple-gamesystem-editor',
|
||||||
|
templateUrl: './simple-gamesystem-editor.component.html',
|
||||||
|
styleUrl: './simple-gamesystem-editor.component.scss'
|
||||||
|
})
|
||||||
|
export class SimpleGamesystemEditorComponent {
|
||||||
|
|
||||||
|
@Input() simpleGamesystem: SimpleGamesystem | undefined
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" multiTemplateDataRows>
|
||||||
|
<ng-container matColumnDef="name">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Label</th>
|
||||||
|
<td mat-cell *matCellDef="let state">
|
||||||
|
<span *ngIf="editedElement !== state"> {{state.stateLabel}}</span>
|
||||||
|
<mat-form-field appearance="fill" class="long-form" *ngIf="editedElement === state">
|
||||||
|
<input matInput [(ngModel)]="state.stateLabel" (ngModelChange)="onStateChange()">
|
||||||
|
<mat-hint class="mat-error" *ngIf="editedStateLabelError"><mat-icon class="warning-icon">warning</mat-icon>Missing State-Label Information</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Expanded Content Column - The detail row is made up of this one column that spans across all columns -->
|
||||||
|
<ng-container matColumnDef="expandedDetail">
|
||||||
|
<td mat-cell *matCellDef="let element" [attr.colspan]="columnsToDisplayWithExpand.length">
|
||||||
|
<div class="example-element-detail" [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'">
|
||||||
|
<p *ngIf="editedElement !== element">{{element.stateDescription}}</p>
|
||||||
|
<mat-form-field appearance="fill" class="long-form" *ngIf="element === editedElement">
|
||||||
|
<mat-label>Description</mat-label>
|
||||||
|
<textarea matInput [(ngModel)]="element.stateDescription" rows="3" (ngModelChange)="onStateChange()"></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<ng-container matColumnDef="initial">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Initial</th>
|
||||||
|
<td mat-cell *matCellDef="let state">
|
||||||
|
<div *ngIf="editedElement !== state">
|
||||||
|
<mat-icon *ngIf="state.initial">done</mat-icon>
|
||||||
|
<mat-icon *ngIf="!state.initial">close</mat-icon>
|
||||||
|
</div>
|
||||||
|
<mat-checkbox *ngIf="editedElement === state" [(ngModel)]="state.initial" (ngModelChange)="onStateChange()"></mat-checkbox>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="edit">
|
||||||
|
<th mat-header-cell *matHeaderCellDef></th>
|
||||||
|
<td mat-cell *matCellDef="let state">
|
||||||
|
<button mat-icon-button color="primary" *ngIf="editedElement !== state" (click)="editState(state)" [disabled]="editedElement !== null"><mat-icon>edit</mat-icon></button>
|
||||||
|
<button mat-icon-button color="primary" (click)="finishEditing()" *ngIf="editedElement === state"><mat-icon>done</mat-icon></button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="delete">
|
||||||
|
<th mat-header-cell *matHeaderCellDef></th>
|
||||||
|
<td mat-cell *matCellDef="let state">
|
||||||
|
<button mat-icon-button color="warn" (click)="deleteState(state)"><mat-icon>delete</mat-icon></button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="expand">
|
||||||
|
<th mat-header-cell *matHeaderCellDef aria-label="row actions">
|
||||||
|
<button mat-icon-button (click)="addState()"><mat-icon>add</mat-icon></button>
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<button mat-icon-button aria-label="expand row" (click)="(expandedElement = expandedElement === element ? null : element); $event.stopPropagation()">
|
||||||
|
@if (expandedElement === element) {
|
||||||
|
<mat-icon>keyboard_arrow_up</mat-icon>
|
||||||
|
} @else {
|
||||||
|
<mat-icon>keyboard_arrow_down</mat-icon>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="columnsToDisplayWithExpand"></tr>
|
||||||
|
<tr mat-row *matRowDef="let element; columns: columnsToDisplayWithExpand;"
|
||||||
|
class="example-element-row"
|
||||||
|
[class.example-expanded-row]="expandedElement === element"
|
||||||
|
(click)="expandedElement = expandedElement === element ? null : element">
|
||||||
|
</tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: ['expandedDetail']" class="example-detail-row"></tr>
|
||||||
|
</table>
|
@ -0,0 +1,63 @@
|
|||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.example-detail-row {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.example-element-row:not(.example-expanded-row):hover {
|
||||||
|
background: #545456
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.example-element-row:not(.example-expanded-row):active {
|
||||||
|
background: #545456;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-element-row td {
|
||||||
|
border-bottom-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-element-detail {
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-element-diagram {
|
||||||
|
min-width: 80px;
|
||||||
|
border: 2px solid black;
|
||||||
|
padding: 8px;
|
||||||
|
font-weight: lighter;
|
||||||
|
margin: 8px 0;
|
||||||
|
height: 104px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-element-symbol {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 40px;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-element-description {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-element-description-attribution {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-column-edit, .mat-column-delete, .mat-column-expand {
|
||||||
|
width: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.long-form {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-icon {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SimpleStateEditorComponent } from './simple-state-editor.component';
|
||||||
|
|
||||||
|
describe('SimpleStateEditorComponent', () => {
|
||||||
|
let component: SimpleStateEditorComponent;
|
||||||
|
let fixture: ComponentFixture<SimpleStateEditorComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [SimpleStateEditorComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(SimpleStateEditorComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,86 @@
|
|||||||
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
|
import {SimpleState} from "../../../../game-model/gamesystems/SimpleState";
|
||||||
|
import {MatTableDataSource} from "@angular/material/table";
|
||||||
|
import {animate, state, style, transition, trigger} from "@angular/animations";
|
||||||
|
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||||
|
import {SimpleGamesystem} from "../../../../game-model/gamesystems/SimpleGamesystem";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-simple-state-editor',
|
||||||
|
templateUrl: './simple-state-editor.component.html',
|
||||||
|
styleUrl: './simple-state-editor.component.scss',
|
||||||
|
animations: [
|
||||||
|
trigger('detailExpand', [
|
||||||
|
state('collapsed,void', style({height: '0px', minHeight: '0'})),
|
||||||
|
state('expanded', style({height: '*'})),
|
||||||
|
transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class SimpleStateEditorComponent implements OnInit{
|
||||||
|
|
||||||
|
@Input() states: SimpleState[] = [];
|
||||||
|
@Input() gamesystem: SimpleGamesystem | undefined
|
||||||
|
dataSource = new MatTableDataSource();
|
||||||
|
displayedColumns = ["name", "initial", "edit", "delete"];
|
||||||
|
columnsToDisplayWithExpand = [...this.displayedColumns, 'expand'];
|
||||||
|
expandedElement: SimpleState | null = null;
|
||||||
|
editedElement: SimpleState | null = null;
|
||||||
|
|
||||||
|
editedStateLabelError: boolean = false;
|
||||||
|
|
||||||
|
constructor(private snackbar: MatSnackBar) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.dataSource.data = this.states;
|
||||||
|
}
|
||||||
|
|
||||||
|
editState(state: SimpleState) {
|
||||||
|
if(this.editedElement === state) {
|
||||||
|
this.editedElement = null;
|
||||||
|
} else {
|
||||||
|
this.editedElement = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finishEditing() {
|
||||||
|
if(this.isEditedStateValid()) {
|
||||||
|
this.editedElement = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isEditedStateValid(): boolean {
|
||||||
|
if(this.editedElement!.stateLabel.length > 0) {
|
||||||
|
this.editedStateLabelError = false;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
this.editedStateLabelError = true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteState(state: SimpleState) {
|
||||||
|
if(this.gamesystem == undefined || this.gamesystem.parentGamesystem == undefined) {
|
||||||
|
this.gamesystem?.removeState(state);
|
||||||
|
this.dataSource.data = this.gamesystem!.states
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addState() {
|
||||||
|
if(this.gamesystem != undefined) {
|
||||||
|
const simpleState = this.gamesystem.createState("New State", "");
|
||||||
|
if(simpleState != undefined) {
|
||||||
|
this.dataSource.data = this.gamesystem.states;
|
||||||
|
this.editedElement = simpleState;
|
||||||
|
this.expandedElement = simpleState;
|
||||||
|
this.onStateChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onStateChange() {
|
||||||
|
this.gamesystem!.onModifyContent();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" multiTemplateDataRows>
|
||||||
|
<ng-container matColumnDef="starting-state">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Starting State</th>
|
||||||
|
<td mat-cell *matCellDef="let transition">
|
||||||
|
<span *ngIf="editedTransition !== transition">{{transition.startingState.stateLabel}}</span>
|
||||||
|
<mat-form-field appearance="fill" class="long-form" *ngIf="editedTransition == transition">
|
||||||
|
<mat-select [(ngModel)]="editedTransition!.startingState" (ngModelChange)="validateEditedTransition()">
|
||||||
|
<mat-option *ngFor="let state of gamesystem!.states" [value]="state"
|
||||||
|
(onSelectionChange)="validateEditedTransition()">{{state.stateLabel}}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-hint class="mat-error" *ngIf="transitionError"><mat-icon class="warning-icon">warning</mat-icon> Starting and Ending State cannot be the same!</mat-hint>
|
||||||
|
<mat-hint class="mat-error" *ngIf="!transitionError && transitionStartingStateError"><mat-icon class="warning-icon">warning</mat-icon> Select a valid Starting State!</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="ending-state">
|
||||||
|
<th mat-header-cell *matHeaderCellDef>Ending State</th>
|
||||||
|
<td mat-cell *matCellDef="let transition">
|
||||||
|
<span *ngIf="editedTransition !== transition">{{transition.endingState.stateLabel}}</span>
|
||||||
|
<mat-form-field appearance="fill" class="long-form" *ngIf="editedTransition == transition">
|
||||||
|
<mat-select [(ngModel)]="editedTransition!.endingState" (ngModelChange)="validateEditedTransition()">
|
||||||
|
<mat-option *ngFor="let state of gamesystem!.states" [value]="state"
|
||||||
|
(onSelectionChange)="validateEditedTransition()">{{state.stateLabel}}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
<mat-hint class="mat-error" *ngIf="transitionError"><mat-icon class="warning-icon">warning</mat-icon> Starting and Ending State cannot be the same!</mat-hint>
|
||||||
|
<mat-hint class="mat-error" *ngIf="!transitionError && transitionEndingStateError"> <mat-icon class="warning-icon">warning</mat-icon> Select a valid Ending State!</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="expandedDetail">
|
||||||
|
<td mat-cell *matCellDef="let element" [attr.colspan]="columnsToDisplayWithExpand.length">
|
||||||
|
<div class="example-element-detail"
|
||||||
|
[@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'">
|
||||||
|
<p>Expanded Detail</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="edit">
|
||||||
|
<th mat-header-cell *matHeaderCellDef></th>
|
||||||
|
<td mat-cell *matCellDef="let transition">
|
||||||
|
<button mat-icon-button color="primary" *ngIf="editedTransition !== transition" [disabled]="editedTransition != undefined"><mat-icon>edit</mat-icon></button>
|
||||||
|
<button mat-icon-button color="primary" *ngIf="editedTransition === transition"
|
||||||
|
[disabled]="transitionError || transitionStartingStateError || transitionEndingStateError" (click)="finishEditing()"
|
||||||
|
><mat-icon>done</mat-icon></button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="delete">
|
||||||
|
<th mat-header-cell *matHeaderCellDef></th>
|
||||||
|
<td mat-cell *matCellDef="let transition">
|
||||||
|
<button mat-icon-button color="warn" (click)="deleteTransition(transition)"><mat-icon>delete</mat-icon></button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="expand">
|
||||||
|
<th mat-header-cell *matHeaderCellDef aria-label="row actions">
|
||||||
|
<button mat-icon-button (click)="addTransition()" [disabled]="editedTransition != undefined"><mat-icon>add</mat-icon></button>
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<button mat-icon-button aria-label="expand row" (click)="(expandedElement = expandedElement === element ? null : element); $event.stopPropagation()">
|
||||||
|
@if (expandedElement === element) {
|
||||||
|
<mat-icon>keyboard_arrow_up</mat-icon>
|
||||||
|
} @else {
|
||||||
|
<mat-icon>keyboard_arrow_down</mat-icon>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="columnsToDisplayWithExpand"></tr>
|
||||||
|
<tr mat-row *matRowDef="let element; columns: columnsToDisplayWithExpand;"
|
||||||
|
class="example-element-row"
|
||||||
|
[class.example-expanded-row]="expandedElement === element"
|
||||||
|
(click)="expandedElement = expandedElement === element ? null : element">
|
||||||
|
</tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: ['expandedDetail']" class="example-detail-row"></tr>
|
||||||
|
</table>
|
@ -0,0 +1,63 @@
|
|||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.mat-column-edit, .mat-column-delete, .mat-column-expand {
|
||||||
|
width: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.example-detail-row {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.example-element-row:not(.example-expanded-row):hover {
|
||||||
|
background: #545456;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.example-element-row:not(.example-expanded-row):active {
|
||||||
|
background: #545456;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-element-row td {
|
||||||
|
border-bottom-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-element-detail {
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-element-diagram {
|
||||||
|
min-width: 80px;
|
||||||
|
border: 2px solid black;
|
||||||
|
padding: 8px;
|
||||||
|
font-weight: lighter;
|
||||||
|
margin: 8px 0;
|
||||||
|
height: 104px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-element-symbol {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 40px;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-element-description {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-element-description-attribution {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.long-form {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-icon {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-error {
|
||||||
|
color: red;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SimpleTransitionEditorComponent } from './simple-transition-editor.component';
|
||||||
|
|
||||||
|
describe('SimpleTransitionEditorComponent', () => {
|
||||||
|
let component: SimpleTransitionEditorComponent;
|
||||||
|
let fixture: ComponentFixture<SimpleTransitionEditorComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [SimpleTransitionEditorComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(SimpleTransitionEditorComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,86 @@
|
|||||||
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
|
import {GameModel} from "../../../../game-model/GameModel";
|
||||||
|
import {SimpleGamesystem} from "../../../../game-model/gamesystems/SimpleGamesystem";
|
||||||
|
import {
|
||||||
|
MatCell,
|
||||||
|
MatCellDef,
|
||||||
|
MatColumnDef,
|
||||||
|
MatHeaderCell,
|
||||||
|
MatHeaderCellDef, MatHeaderRow, MatHeaderRowDef, MatRow, MatRowDef,
|
||||||
|
MatTable,
|
||||||
|
MatTableDataSource
|
||||||
|
} from "@angular/material/table";
|
||||||
|
import {SimpleTransition} from "../../../../game-model/gamesystems/SimpleTransition";
|
||||||
|
import {animate, state, style, transition, trigger} from "@angular/animations";
|
||||||
|
import {SimpleState} from "../../../../game-model/gamesystems/SimpleState";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-simple-transition-editor',
|
||||||
|
templateUrl: './simple-transition-editor.component.html',
|
||||||
|
styleUrl: './simple-transition-editor.component.scss',
|
||||||
|
animations: [
|
||||||
|
trigger('detailExpand', [
|
||||||
|
state('collapsed,void', style({height: '0px', minHeight: '0'})),
|
||||||
|
state('expanded', style({height: '*'})),
|
||||||
|
transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class SimpleTransitionEditorComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() gamesystem: SimpleGamesystem | undefined
|
||||||
|
displayedColumns: string[] = ["starting-state", "ending-state", "edit", "delete"];
|
||||||
|
dataSource = new MatTableDataSource<SimpleTransition>();
|
||||||
|
columnsToDisplayWithExpand = [...this.displayedColumns, 'expand'];
|
||||||
|
expandedElement: SimpleTransition | null = null;
|
||||||
|
editedTransition: SimpleTransition | undefined
|
||||||
|
editedTransitionIndex = -1;
|
||||||
|
|
||||||
|
defaultStartingState: SimpleState = new SimpleState("None", "");
|
||||||
|
defaultEndingState: SimpleState = new SimpleState("None", "");
|
||||||
|
|
||||||
|
transitionError: boolean = false;
|
||||||
|
transitionStartingStateError = true;
|
||||||
|
transitionEndingStateError = true;
|
||||||
|
ngOnInit() {
|
||||||
|
this.dataSource.data = this.gamesystem!.transitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
addTransition() {
|
||||||
|
this.editedTransition = new SimpleTransition(this.defaultStartingState, this.defaultEndingState);
|
||||||
|
const updatedData = this.dataSource.data;
|
||||||
|
updatedData.push(this.editedTransition);
|
||||||
|
this.dataSource.data = updatedData;
|
||||||
|
this.editedTransitionIndex = this.dataSource.data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
validateEditedTransition() {
|
||||||
|
this.transitionError = false;
|
||||||
|
this.transitionStartingStateError = false;
|
||||||
|
this.transitionEndingStateError = false;
|
||||||
|
if(this.editedTransition!.startingState === this.editedTransition!.endingState) {
|
||||||
|
this.transitionError = true;
|
||||||
|
} else if(this.editedTransition!.startingState == this.defaultStartingState) {
|
||||||
|
this.transitionStartingStateError = true;
|
||||||
|
} else if(this.editedTransition!.endingState == this.defaultEndingState) {
|
||||||
|
this.transitionEndingStateError = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finishEditing() {
|
||||||
|
this.validateEditedTransition();
|
||||||
|
if(this.transitionError || this.transitionStartingStateError || this.transitionEndingStateError) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.gamesystem?.createTransition(this.editedTransition!.startingState, this.editedTransition!.endingState);
|
||||||
|
this.dataSource.data = this.gamesystem!.transitions;
|
||||||
|
this.editedTransition = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteTransition(transition: SimpleTransition) {
|
||||||
|
if(this.gamesystem!.parentGamesystem == undefined) {
|
||||||
|
this.gamesystem!.removeTransition(transition);
|
||||||
|
this.dataSource.data = this.gamesystem!.transitions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import {Component, Input, OnInit} from '@angular/core';
|
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||||
import {ModelComponent} from "../../game-model/ModelComponent";
|
import {ModelComponent} from "../../game-model/ModelComponent";
|
||||||
import {FormControl, Validators} from "@angular/forms";
|
import {FormControl, Validators} from "@angular/forms";
|
||||||
|
|
||||||
@ -9,6 +9,7 @@ import {FormControl, Validators} from "@angular/forms";
|
|||||||
})
|
})
|
||||||
export class ModelComponentEditorComponent implements OnInit{
|
export class ModelComponentEditorComponent implements OnInit{
|
||||||
@Input('modelComponent') modelComponent: ModelComponent | undefined
|
@Input('modelComponent') modelComponent: ModelComponent | undefined
|
||||||
|
@Output("onModelNameUpdated") onModelNameUpdateEmitter = new EventEmitter<boolean>();
|
||||||
|
|
||||||
nameCtrl: FormControl = new FormControl('', [Validators.required]);
|
nameCtrl: FormControl = new FormControl('', [Validators.required]);
|
||||||
descriptionCtrl: FormControl = new FormControl('' );
|
descriptionCtrl: FormControl = new FormControl('' );
|
||||||
@ -21,6 +22,7 @@ export class ModelComponentEditorComponent implements OnInit{
|
|||||||
onUpdateName() {
|
onUpdateName() {
|
||||||
this.modelComponent!.componentName = this.nameCtrl.value;
|
this.modelComponent!.componentName = this.nameCtrl.value;
|
||||||
this.modelComponent!.onModifyContent();
|
this.modelComponent!.onModifyContent();
|
||||||
|
this.onModelNameUpdateEmitter.emit(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdateDescription() {
|
onUpdateDescription() {
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
import {Gamesystem} from "./gamesystems/Gamesystem";
|
import {Gamesystem} from "./gamesystems/Gamesystem";
|
||||||
import {ScriptAccount} from "./scriptAccounts/ScriptAccount";
|
import {ScriptAccount} from "./scriptAccounts/ScriptAccount";
|
||||||
|
import {Transition} from "./gamesystems/Transition";
|
||||||
|
import {State} from "./gamesystems/State";
|
||||||
|
import {ProductGamesystem} from "./gamesystems/ProductGamesystem";
|
||||||
|
import {SimpleGamesystem} from "./gamesystems/SimpleGamesystem";
|
||||||
|
|
||||||
export class GameModel {
|
export class GameModel {
|
||||||
private readonly _gameModelName: string
|
private readonly _gameModelName: string
|
||||||
|
|
||||||
private _gamesystems: Gamesystem[] = [];
|
private _gamesystems: Gamesystem<any, any>[] = [];
|
||||||
private _scriptAccounts: ScriptAccount[] = [];
|
private _scriptAccounts: ScriptAccount[] = [];
|
||||||
|
|
||||||
constructor(gameModelName: string) {
|
constructor(gameModelName: string) {
|
||||||
@ -13,26 +17,25 @@ export class GameModel {
|
|||||||
get gameModelName(): string {
|
get gameModelName(): string {
|
||||||
return this._gameModelName;
|
return this._gameModelName;
|
||||||
}
|
}
|
||||||
|
get gamesystems(): Gamesystem<any, any>[] {
|
||||||
|
|
||||||
get gamesystems(): Gamesystem[] {
|
|
||||||
return this._gamesystems;
|
return this._gamesystems;
|
||||||
}
|
}
|
||||||
get scriptAccounts(): ScriptAccount[] {
|
get scriptAccounts(): ScriptAccount[] {
|
||||||
return this._scriptAccounts;
|
return this._scriptAccounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
addGamesystem(gamesystem: Gamesystem) {
|
addGamesystem(gamesystem: Gamesystem<any, any>) {
|
||||||
if(!this.gamesystems.includes(gamesystem)) {
|
if(this.findGamesystem(gamesystem.componentName) == undefined) {
|
||||||
this._gamesystems.push(gamesystem);
|
this._gamesystems.push(gamesystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
removeGamesystem(gamesystem : Gamesystem) {
|
removeGamesystem(gamesystem : Gamesystem<any, any>) {
|
||||||
this._gamesystems = this._gamesystems.filter(g => g !== gamesystem);
|
this._gamesystems = this._gamesystems.filter(g => g !== gamesystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
addScriptAccount(scriptAccountName: string) {
|
createScriptAccount(scriptAccountName: string) {
|
||||||
if(scriptAccountName != undefined && scriptAccountName.length > 0) {
|
if(scriptAccountName != undefined && scriptAccountName.length > 0) {
|
||||||
const scriptAccount = new ScriptAccount(scriptAccountName, "");
|
const scriptAccount = new ScriptAccount(scriptAccountName, "");
|
||||||
const searchedScriptAccount = this.scriptAccounts.find(s => s.componentName === scriptAccount.componentName);
|
const searchedScriptAccount = this.scriptAccounts.find(s => s.componentName === scriptAccount.componentName);
|
||||||
@ -44,10 +47,47 @@ export class GameModel {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createGamesystem(gamesystemName: string, parentGamesystemName: string | undefined) {
|
||||||
|
if(gamesystemName != undefined && this.findGamesystem(gamesystemName) == undefined) {
|
||||||
|
const simpleGamesystem = new SimpleGamesystem(gamesystemName);
|
||||||
|
if(parentGamesystemName != undefined) {
|
||||||
|
const parentGamesystem = this.findGamesystem(parentGamesystemName);
|
||||||
|
if(parentGamesystem instanceof SimpleGamesystem) {
|
||||||
|
const parentProductGamesystem = ProductGamesystem.constructFromSimpleGamesystem(parentGamesystem, this);
|
||||||
|
parentProductGamesystem.addChildGamesystem(simpleGamesystem);
|
||||||
|
simpleGamesystem.parentGamesystem = parentProductGamesystem;
|
||||||
|
} else {
|
||||||
|
const productParentGamesystem = parentGamesystem as ProductGamesystem;
|
||||||
|
productParentGamesystem.addChildGamesystem(simpleGamesystem);
|
||||||
|
simpleGamesystem.parentGamesystem = productParentGamesystem;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.gamesystems.push(simpleGamesystem);
|
||||||
|
|
||||||
|
}
|
||||||
|
return simpleGamesystem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
removeScriptAccount(scriptAccount: ScriptAccount) {
|
removeScriptAccount(scriptAccount: ScriptAccount) {
|
||||||
if(scriptAccount != undefined) {
|
if(scriptAccount != undefined) {
|
||||||
this._scriptAccounts = this.scriptAccounts.filter(s => s != scriptAccount);
|
this._scriptAccounts = this.scriptAccounts.filter(s => s != scriptAccount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findGamesystem(gamesystemName: string) {
|
||||||
|
const gamesystemQueue : Gamesystem<State<any>, Transition<any>>[] = [];
|
||||||
|
this.gamesystems.forEach(gamesystem => gamesystemQueue.push(gamesystem));
|
||||||
|
|
||||||
|
while(gamesystemQueue.length > 0) {
|
||||||
|
const currentGamesystem = gamesystemQueue.shift()!;
|
||||||
|
if(currentGamesystem.componentName == gamesystemName) {
|
||||||
|
return currentGamesystem;
|
||||||
|
} else if(currentGamesystem instanceof ProductGamesystem) {
|
||||||
|
const currentProductGamesystem = currentGamesystem as ProductGamesystem;
|
||||||
|
currentProductGamesystem.innerGamesystems.forEach(gamesystem => gamesystemQueue.push(gamesystem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export enum ModelComponentType {
|
export enum ModelComponentType {
|
||||||
SCRIPTACCOUNT
|
SCRIPTACCOUNT,
|
||||||
|
GAMESYTEM
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import {ModelComponentType} from "./ModelComponentType";
|
import {ModelComponentType} from "./ModelComponentType";
|
||||||
import {ModelComponent} from "./ModelComponent";
|
|
||||||
|
|
||||||
export class ModelComponentTypeUtillities {
|
export class ModelComponentTypeUtillities {
|
||||||
static toString(modelComponentType: ModelComponentType | undefined): string {
|
static toString(modelComponentType: ModelComponentType | undefined): string {
|
||||||
switch (modelComponentType) {
|
switch (modelComponentType) {
|
||||||
case ModelComponentType.SCRIPTACCOUNT: return "ScriptAccounts";
|
case ModelComponentType.SCRIPTACCOUNT: return "ScriptAccounts";
|
||||||
|
case ModelComponentType.GAMESYTEM: return "Gamesystems";
|
||||||
default: return "Undefined";
|
default: return "Undefined";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -12,7 +12,15 @@ export class ModelComponentTypeUtillities {
|
|||||||
static toSingleString(modelComponentType: ModelComponentType | undefined): string {
|
static toSingleString(modelComponentType: ModelComponentType | undefined): string {
|
||||||
switch (modelComponentType) {
|
switch (modelComponentType) {
|
||||||
case ModelComponentType.SCRIPTACCOUNT: return "ScriptAccount";
|
case ModelComponentType.SCRIPTACCOUNT: return "ScriptAccount";
|
||||||
|
case ModelComponentType.GAMESYTEM: return "Gamesystem";
|
||||||
default: return "Undefined";
|
default: return "Undefined";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static fromString(string: string) : ModelComponentType | undefined {
|
||||||
|
switch (string) {
|
||||||
|
case "gamesystem": return ModelComponentType.GAMESYTEM;
|
||||||
|
case "scriptaccount": return ModelComponentType.SCRIPTACCOUNT;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,35 @@
|
|||||||
export class Gamesystem {
|
import {SimpleGamesystem} from "./SimpleGamesystem";
|
||||||
gamesystemName: string
|
import {ProductGamesystem} from "./ProductGamesystem";
|
||||||
|
import {ModelComponent} from "../ModelComponent";
|
||||||
|
import {ModelComponentType} from "../ModelComponentType";
|
||||||
|
|
||||||
|
export abstract class Gamesystem<S, T> extends ModelComponent{
|
||||||
|
|
||||||
|
states: S[] = [];
|
||||||
|
transitions: T[] = [];
|
||||||
constructor(gamesystemName: string) {
|
constructor(gamesystemName: string) {
|
||||||
this.gamesystemName = gamesystemName;
|
super(gamesystemName, "", ModelComponentType.GAMESYTEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract createState(label: string, description: string): S|undefined;
|
||||||
|
|
||||||
|
abstract createTransition(startingState: S, endingState: S): T|undefined;
|
||||||
|
|
||||||
|
abstract removeState(state: S): boolean;
|
||||||
|
|
||||||
|
removeTransition(transition: T): boolean {
|
||||||
|
const updatedTransitions = this.transitions.filter(t => t !== transition);
|
||||||
|
if(updatedTransitions.length == this.transitions.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.transitions = updatedTransitions;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
save() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
60
src/app/game-model/gamesystems/ProductGamesystem.ts
Normal file
60
src/app/game-model/gamesystems/ProductGamesystem.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import {Gamesystem} from "./Gamesystem";
|
||||||
|
import {ProductState} from "./ProductState";
|
||||||
|
import {ProductTransition} from "./ProductTransition";
|
||||||
|
import {State} from "./State";
|
||||||
|
import {Transition} from "./Transition";
|
||||||
|
import {SimpleState} from "./SimpleState";
|
||||||
|
import {SimpleGamesystem} from "./SimpleGamesystem";
|
||||||
|
import {GameModel} from "../GameModel";
|
||||||
|
|
||||||
|
export class ProductGamesystem extends Gamesystem<ProductState, ProductTransition> {
|
||||||
|
|
||||||
|
innerGamesystems: Gamesystem<State<any>, Transition<any>>[] = [];
|
||||||
|
parentGamesystem: ProductGamesystem | undefined
|
||||||
|
|
||||||
|
static constructFromSimpleGamesystem(simpleGamesystem: SimpleGamesystem, gameModel: GameModel) {
|
||||||
|
const productGamesystem = new ProductGamesystem(simpleGamesystem.componentName);
|
||||||
|
const parentGamesystem = simpleGamesystem.parentGamesystem;
|
||||||
|
|
||||||
|
if(simpleGamesystem.states.length > 0) {
|
||||||
|
simpleGamesystem.componentName += "(Child)";
|
||||||
|
productGamesystem.addChildGamesystem(simpleGamesystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(parentGamesystem != undefined) {
|
||||||
|
parentGamesystem.removeChildGamesystem(simpleGamesystem);
|
||||||
|
parentGamesystem.addChildGamesystem(productGamesystem);
|
||||||
|
} else {
|
||||||
|
gameModel.removeGamesystem(simpleGamesystem);
|
||||||
|
gameModel.addGamesystem(productGamesystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return productGamesystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
createState(label: string, description: string): ProductState | undefined {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
createTransition(startingState: ProductState, endingState: ProductState): ProductTransition | undefined {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeState(state: ProductState): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeInnerState(state: SimpleState) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
addChildGamesystem(gamesystem: Gamesystem<State<any>, Transition<any>>) {
|
||||||
|
this.innerGamesystems.push(gamesystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private removeChildGamesystem(gamesystem: Gamesystem<State<any>, Transition<any>>) {
|
||||||
|
this.innerGamesystems = this.innerGamesystems.filter(childSystem => childSystem != gamesystem);
|
||||||
|
}
|
||||||
|
}
|
7
src/app/game-model/gamesystems/ProductState.ts
Normal file
7
src/app/game-model/gamesystems/ProductState.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import {ProductTransition} from "./ProductTransition";
|
||||||
|
import {State} from "./State";
|
||||||
|
import {SimpleState} from "./SimpleState";
|
||||||
|
|
||||||
|
export class ProductState extends State<ProductTransition> {
|
||||||
|
innerStates: SimpleState[] = [];
|
||||||
|
}
|
6
src/app/game-model/gamesystems/ProductTransition.ts
Normal file
6
src/app/game-model/gamesystems/ProductTransition.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import {Transition} from "./Transition";
|
||||||
|
import {ProductState} from "./ProductState";
|
||||||
|
|
||||||
|
export class ProductTransition extends Transition<ProductState> {
|
||||||
|
|
||||||
|
}
|
60
src/app/game-model/gamesystems/SimpleGamesystem.ts
Normal file
60
src/app/game-model/gamesystems/SimpleGamesystem.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import {Gamesystem} from "./Gamesystem";
|
||||||
|
import {SimpleState} from "./SimpleState";
|
||||||
|
import {SimpleTransition} from "./SimpleTransition";
|
||||||
|
import {State} from "./State";
|
||||||
|
import {Transition} from "./Transition";
|
||||||
|
import {ProductState} from "./ProductState";
|
||||||
|
import {ProductTransition} from "./ProductTransition";
|
||||||
|
import {ProductGamesystem} from "./ProductGamesystem";
|
||||||
|
export class SimpleGamesystem extends Gamesystem<SimpleState, SimpleTransition> {
|
||||||
|
|
||||||
|
parentGamesystem: ProductGamesystem | undefined
|
||||||
|
|
||||||
|
createState(label: string, description: string): SimpleState | undefined {
|
||||||
|
if(label == null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(description == null) {
|
||||||
|
description = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = new SimpleState(label, description);
|
||||||
|
if(this.states.find(s => s.stateLabel == label) == undefined) {
|
||||||
|
this.states.push(state);
|
||||||
|
return state;
|
||||||
|
} else {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createTransition(startingState: SimpleState, endingState: SimpleState): SimpleTransition | undefined{
|
||||||
|
if((startingState == null || endingState == null) || startingState === endingState) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transition = new SimpleTransition(startingState, endingState);
|
||||||
|
if(this.transitions.find(t => t.startingState === startingState && t.endingState === endingState) == undefined) {
|
||||||
|
this.transitions.push(transition)
|
||||||
|
return transition;
|
||||||
|
} else {
|
||||||
|
startingState.removeOutgoingTransition(transition);
|
||||||
|
endingState.removeIncomingTransition(transition);
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
removeState(state: SimpleState): boolean {
|
||||||
|
const updatedStates = this.states.filter(s => s !== state);
|
||||||
|
const updated = updatedStates.length != this.states.length;
|
||||||
|
this.states = updatedStates;
|
||||||
|
|
||||||
|
this.transitions = this.transitions.filter(t => t.startingState !== state && t.endingState !== state);
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
6
src/app/game-model/gamesystems/SimpleState.ts
Normal file
6
src/app/game-model/gamesystems/SimpleState.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import {State} from "./State";
|
||||||
|
import {SimpleTransition} from "./SimpleTransition";
|
||||||
|
|
||||||
|
export class SimpleState extends State<SimpleTransition> {
|
||||||
|
|
||||||
|
}
|
6
src/app/game-model/gamesystems/SimpleTransition.ts
Normal file
6
src/app/game-model/gamesystems/SimpleTransition.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import {SimpleState} from "./SimpleState";
|
||||||
|
import {Transition} from "./Transition";
|
||||||
|
|
||||||
|
export class SimpleTransition extends Transition<SimpleState> {
|
||||||
|
|
||||||
|
}
|
32
src/app/game-model/gamesystems/State.ts
Normal file
32
src/app/game-model/gamesystems/State.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import {Transition} from "./Transition";
|
||||||
|
|
||||||
|
export abstract class State<T extends Transition<any>> {
|
||||||
|
stateLabel: string = "";
|
||||||
|
stateDescription: string = "";
|
||||||
|
incomingTransitions: T[] =[];
|
||||||
|
outgoingTransitions: T[] =[];
|
||||||
|
|
||||||
|
initial: boolean = false;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(stateLabel: string, stateDescription: string) {
|
||||||
|
this.stateLabel = stateLabel;
|
||||||
|
this.stateDescription = stateDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
addIncomingTransition(transition: T) {
|
||||||
|
this.incomingTransitions.push(transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
addOutgoingTransition(transition: T) {
|
||||||
|
this.outgoingTransitions.push(transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeIncomingTransition(transition: T) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
removeOutgoingTransition(transition: T) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
15
src/app/game-model/gamesystems/Transition.ts
Normal file
15
src/app/game-model/gamesystems/Transition.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import {State} from "./State";
|
||||||
|
|
||||||
|
export abstract class Transition<S extends State<any>> {
|
||||||
|
startingState: S
|
||||||
|
endingState: S
|
||||||
|
|
||||||
|
|
||||||
|
constructor(startingState: S, endingState: S) {
|
||||||
|
this.startingState = startingState;
|
||||||
|
this.endingState = endingState;
|
||||||
|
|
||||||
|
this.startingState.addOutgoingTransition(this);
|
||||||
|
this.endingState.addIncomingTransition(this);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl" (contextmenu)="onContextMenu($event)">
|
||||||
|
<!-- This is the tree node template for leaf nodes -->
|
||||||
|
<mat-tree-node *matTreeNodeDef="let node" matTreeNodePadding matTreeNodePaddingIndent="10" [ngClass]="selectedGamesystem === node ? 'selected-node':''"
|
||||||
|
(click)="onSelectGamesystem(node)" (contextmenu)="onSelectGamesystem(node)" (dblclick)="openGamesystemEditor(node)">
|
||||||
|
<!-- use a disabled button to provide padding for tree leaf -->
|
||||||
|
<button mat-icon-button class="small-icon-button" [disabled]="true"></button>
|
||||||
|
<div>
|
||||||
|
<button mat-icon-button class="small-icon-button"><mat-icon>code</mat-icon></button>
|
||||||
|
<span>{{node.name}}</span>
|
||||||
|
</div>
|
||||||
|
</mat-tree-node>
|
||||||
|
<!-- This is the tree node template for expandable nodes -->
|
||||||
|
<mat-tree-node *matTreeNodeDef="let node;when: hasChild" matTreeNodePadding matTreeNodePaddingIndent="10"
|
||||||
|
(click)="onSelectGamesystem(node)" (contextmenu)="onSelectGamesystem(node)" [ngClass]="selectedGamesystem === node ? 'selected-node':''"
|
||||||
|
(dblclick)="openGamesystemEditor(node)">
|
||||||
|
<button mat-icon-button matTreeNodeToggle
|
||||||
|
[attr.aria-label]="'Toggle ' + node.name" class="small-icon-button">
|
||||||
|
<mat-icon class="mat-icon-rtl-mirror">
|
||||||
|
{{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
|
||||||
|
</mat-icon>
|
||||||
|
</button>
|
||||||
|
<div>
|
||||||
|
<button mat-icon-button class="small-icon-button"><mat-icon>code</mat-icon></button>
|
||||||
|
<span>{{node.name}}</span>
|
||||||
|
</div>
|
||||||
|
</mat-tree-node>
|
||||||
|
</mat-tree>
|
@ -0,0 +1,40 @@
|
|||||||
|
.mat-tree-node {
|
||||||
|
min-height: 1.8em !important;
|
||||||
|
height: 1.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.small-icon-button {
|
||||||
|
width: 26px !important;
|
||||||
|
height: 26px !important;
|
||||||
|
padding: 0px !important;
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
|
||||||
|
& > *[role=img] {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
font-size: 18px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-button-touch-target {
|
||||||
|
width: 22px !important;
|
||||||
|
height: 22px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-icon-button mat-icon {
|
||||||
|
color: whitesmoke;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-node {
|
||||||
|
background-color: #545456
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { GamescriptOverviewComponent } from './gamescript-overview.component';
|
||||||
|
|
||||||
|
describe('GamescriptOverviewComponent', () => {
|
||||||
|
let component: GamescriptOverviewComponent;
|
||||||
|
let fixture: ComponentFixture<GamescriptOverviewComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [GamescriptOverviewComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(GamescriptOverviewComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,106 @@
|
|||||||
|
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||||
|
import {Gamesystem} from "../../game-model/gamesystems/Gamesystem";
|
||||||
|
import {State} from "../../game-model/gamesystems/State";
|
||||||
|
import {Transition} from "../../game-model/gamesystems/Transition";
|
||||||
|
import {ProductGamesystem} from "../../game-model/gamesystems/ProductGamesystem";
|
||||||
|
import {FlatTreeControl} from "@angular/cdk/tree";
|
||||||
|
import {
|
||||||
|
MatTree,
|
||||||
|
MatTreeFlatDataSource,
|
||||||
|
MatTreeFlattener,
|
||||||
|
MatTreeNode, MatTreeNodeDef,
|
||||||
|
MatTreeNodePadding, MatTreeNodeToggle
|
||||||
|
} from "@angular/material/tree";
|
||||||
|
import {MatIcon} from "@angular/material/icon";
|
||||||
|
import {MatIconButton} from "@angular/material/button";
|
||||||
|
import {SimpleGamesystem} from "../../game-model/gamesystems/SimpleGamesystem";
|
||||||
|
import {GameModel} from "../../game-model/GameModel";
|
||||||
|
import {ElectronService} from "../../core/services";
|
||||||
|
|
||||||
|
|
||||||
|
interface FlatNode {
|
||||||
|
expandable: boolean,
|
||||||
|
name: string,
|
||||||
|
level: number
|
||||||
|
}
|
||||||
|
@Component({
|
||||||
|
selector: 'app-gamescript-overview',
|
||||||
|
templateUrl: './gamescript-overview.component.html',
|
||||||
|
styleUrl: './gamescript-overview.component.scss'
|
||||||
|
})
|
||||||
|
export class GamescriptOverviewComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input('gameModel') gameModel: GameModel | undefined
|
||||||
|
@Output('openGamesystemEditor') openGamesystemEmitter : EventEmitter<Gamesystem<State<any>, Transition<any>>> = new EventEmitter<Gamesystem<State<any>, Transition<any>>>();
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.dataSource.data = this.gameModel!.gamesystems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _transformer = (node: Gamesystem<State<any>, Transition<any>>, level: number) => {
|
||||||
|
return {
|
||||||
|
expandable: this.isProductGamesystem(node) && !!(node as ProductGamesystem).innerGamesystems && (node as ProductGamesystem).innerGamesystems.length > 0,
|
||||||
|
name: node.componentName,
|
||||||
|
level: level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
treeControl = new FlatTreeControl<FlatNode>(
|
||||||
|
node => node.level,
|
||||||
|
node => node.expandable
|
||||||
|
)
|
||||||
|
|
||||||
|
treeFlattener = new MatTreeFlattener(
|
||||||
|
this._transformer,
|
||||||
|
node => node.level,
|
||||||
|
node => node.expandable,
|
||||||
|
node => this.isSimpleGamesystem(node)? []: (node as ProductGamesystem).innerGamesystems
|
||||||
|
);
|
||||||
|
|
||||||
|
dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
|
||||||
|
selectedGamesystem: FlatNode | undefined;
|
||||||
|
|
||||||
|
constructor(private electronService: ElectronService) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
hasChild = (_: number, node: FlatNode) => node.expandable;
|
||||||
|
|
||||||
|
|
||||||
|
isSimpleGamesystem(gamesystem: Gamesystem<State<any>, Transition<any>>) {
|
||||||
|
return gamesystem instanceof SimpleGamesystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
isProductGamesystem(gamesystem: Gamesystem<State<any>, Transition<any>>) {
|
||||||
|
return gamesystem instanceof ProductGamesystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectGamesystem(node: FlatNode) {
|
||||||
|
this.selectedGamesystem = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
onContextMenu(event: MouseEvent) {
|
||||||
|
this.electronService.ipcRenderer.send('context-menu', {x: event.x, y: event.y});
|
||||||
|
}
|
||||||
|
|
||||||
|
openGamesystemEditor(node: FlatNode) {
|
||||||
|
const gamesystem: Gamesystem<State<any>, Transition<any>>| undefined= this.gameModel!.findGamesystem(node.name);
|
||||||
|
if(gamesystem != undefined) {
|
||||||
|
gamesystem.unsaved = false;
|
||||||
|
this.openGamesystemEmitter.emit(gamesystem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get selectedGamesystemName() {
|
||||||
|
if(this.selectedGamesystem == undefined) {
|
||||||
|
return undefined
|
||||||
|
} else {
|
||||||
|
return this.selectedGamesystem!.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this.dataSource.data = this.gameModel!.gamesystems;
|
||||||
|
}
|
||||||
|
}
|
@ -14,17 +14,7 @@ export class ScriptAccountOverviewComponent {
|
|||||||
|
|
||||||
selectedScriptAccount: ScriptAccount | undefined
|
selectedScriptAccount: ScriptAccount | undefined
|
||||||
|
|
||||||
constructor(private electronService: ElectronService,
|
constructor() {
|
||||||
private zone: NgZone) {
|
|
||||||
if(electronService.isElectron) {
|
|
||||||
this.electronService.ipcRenderer.on('context-menu', (event: any, message: string) => {
|
|
||||||
this.zone.run(() => {
|
|
||||||
if(message == "new-scriptaccount") {
|
|
||||||
this.onCreateNewScriptAccount()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onOpenScriptAccount(scriptAccount: ScriptAccount) {
|
onOpenScriptAccount(scriptAccount: ScriptAccount) {
|
||||||
@ -32,12 +22,6 @@ export class ScriptAccountOverviewComponent {
|
|||||||
this.openScriptAccountEmitter.emit(scriptAccount);
|
this.openScriptAccountEmitter.emit(scriptAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
onCreateNewScriptAccount() {
|
|
||||||
const scriptAccount = this.gameModel!.addScriptAccount("New ScriptAccount");
|
|
||||||
if(scriptAccount != undefined) {
|
|
||||||
this.openScriptAccountEmitter.emit(scriptAccount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
selectScriptAccount(scriptAccount: ScriptAccount) {
|
selectScriptAccount(scriptAccount: ScriptAccount) {
|
||||||
|
Loading…
Reference in New Issue
Block a user