Compare commits

...

18 Commits

Author SHA1 Message Date
sebastian
189b77b8ef Implement navigation (forward) in interaction sequence
All checks were successful
E2E Testing / test (push) Successful in 1m18s
2024-06-17 22:22:12 +02:00
sebastian
3d899251eb Convert Interaction into Sequence
All checks were successful
E2E Testing / test (push) Successful in 1m13s
2024-06-17 22:16:49 +02:00
sebastian
685270bd4f Add Implementation of Sequence Editor
Some checks failed
E2E Testing / test (push) Failing after 53s
2024-06-17 21:57:35 +02:00
6bbba380bb Remove GamesystemAction Tab
All checks were successful
E2E Testing / test (push) Successful in 1m20s
2024-06-15 14:59:53 +02:00
64786efce0 Implement InventoryActions for Interactions
All checks were successful
E2E Testing / test (push) Successful in 1m16s
2024-06-15 14:44:20 +02:00
bc0617eb85 Assign ScriptAccountActions to expanded ineraction
All checks were successful
E2E Testing / test (push) Successful in 1m17s
2024-06-15 14:17:08 +02:00
fe63b30115 Merge pull request 'refactor-scriptaccount-action-editor' (#52) from refactor-scriptaccount-action-editor into character-interactions
All checks were successful
E2E Testing / test (push) Successful in 1m18s
Reviewed-on: #52
2024-06-15 14:04:53 +02:00
224468f088 Include ScriptAccountEditor in ProductTransitionEditor
All checks were successful
E2E Testing / test (push) Successful in 1m18s
2024-06-15 14:03:18 +02:00
5d4c9a6e58 Include ScriptAccountActions in character-interaction-editor
All checks were successful
E2E Testing / test (push) Successful in 1m19s
2024-06-15 13:54:13 +02:00
sebastian
e8e3c83b45 Move logic for deleting and adding actions into simple-transition-editor
All checks were successful
E2E Testing / test (push) Successful in 1m20s
2024-06-02 19:56:21 +02:00
sebastian
2a01f45bf6 Move scriptaccount-action-editor.component to different directory
All checks were successful
E2E Testing / test (push) Successful in 1m25s
2024-06-02 19:43:10 +02:00
sebastian
e6218e107e Make ScriptAccountConditionEditor ready for InteractionConditions
All checks were successful
E2E Testing / test (push) Successful in 1m18s
2024-06-02 19:38:22 +02:00
sebastian
d010dfbce6 InventoryConditionEditor for Interactions
All checks were successful
E2E Testing / test (push) Successful in 1m29s
2024-06-02 19:27:47 +02:00
sebastian
8c4d2ad5ca Provide interaction to GamesystemConditionEditor
All checks were successful
E2E Testing / test (push) Successful in 1m20s
2024-06-02 17:23:29 +02:00
sebastian
1ffc5c1ecf GamesystemConditionEditor
All checks were successful
E2E Testing / test (push) Successful in 1m20s
2024-06-02 17:05:52 +02:00
sebastian
1c60504565 Include Condition and Action Overview in InteractionEditor
All checks were successful
E2E Testing / test (push) Successful in 1m17s
2024-06-01 19:38:20 +02:00
sebastian
e45a3a77ea Basic Interaction Editor
All checks were successful
E2E Testing / test (push) Successful in 2m8s
2024-06-01 19:07:43 +02:00
Sebastian Böckelmann
bcd06d894f Implement Interactionstructure
All checks were successful
E2E Testing / test (push) Successful in 1m32s
2024-05-11 17:11:13 +02:00
61 changed files with 1931 additions and 117 deletions

View File

@ -3,7 +3,8 @@
"cli": { "cli": {
"schematicCollections": [ "schematicCollections": [
"@angular-eslint/schematics" "@angular-eslint/schematics"
] ],
"analytics": false
}, },
"version": 1, "version": 1,
"newProjectRoot": "projects", "newProjectRoot": "projects",

View File

@ -64,7 +64,7 @@ import {MatTooltip} from "@angular/material/tooltip";
import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from "@angular/material/card"; import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from "@angular/material/card";
import { import {
ScriptaccountActionEditorComponent ScriptaccountActionEditorComponent
} from "./editor/gamesystem-editor/transition-editor/scriptaccount-action-editor/scriptaccount-action-editor.component"; } from "./editor/gamesystem-editor/scriptaccount-action-editor/scriptaccount-action-editor.component";
import { import {
ScriptaccountConditionEditorComponent ScriptaccountConditionEditorComponent
} from "./editor/gamesystem-editor/scriptaccount-condition-editor/scriptaccount-condition-editor.component"; } from "./editor/gamesystem-editor/scriptaccount-condition-editor/scriptaccount-condition-editor.component";
@ -107,6 +107,22 @@ import {
import { import {
RequieredInheritancesCreatorComponent RequieredInheritancesCreatorComponent
} from "./editor/character-editor/inventory-slot-editor/requiered-inheritances-editor/requiered-inheritances-creator/requiered-inheritances-creator.component"; } from "./editor/character-editor/inventory-slot-editor/requiered-inheritances-editor/requiered-inheritances-creator/requiered-inheritances-creator.component";
import {
CharacterInteractionEditorComponent
} from "./editor/character-editor/character-interaction-editor/character-interaction-editor.component";
import {
GamesystemConditionEditorComponent
} from "./editor/interaction-editor/conditions/gamesystem-condition-editor/gamesystem-condition-editor.component";
import {
ItemConditionEditorComponent
} from "./editor/interaction-editor/conditions/item-condition-editor/item-condition-editor.component";
import {
ItemSelectorComponent
} from "./editor/interaction-editor/conditions/item-condition-editor/item-selector/item-selector.component";
import {ItemActionEditorComponent} from "./editor/interaction-editor/item-action-editor/item-action-editor.component";
import {
InteractionSequenceEditorComponent
} from "./editor/interaction-editor/interaction-sequence-editor/interaction-sequence-editor.component";
// 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');
@ -141,7 +157,13 @@ const httpLoaderFactory = (http: HttpClient): TranslateHttpLoader => new Transl
InventorySlotCharacteristicEditorComponent, InventorySlotCharacteristicEditorComponent,
CharacteristicSelectorComponent, CharacteristicSelectorComponent,
RequieredInheritancesEditorComponent, RequieredInheritancesEditorComponent,
RequieredInheritancesCreatorComponent RequieredInheritancesCreatorComponent,
CharacterInteractionEditorComponent,
GamesystemConditionEditorComponent,
ItemConditionEditorComponent,
ItemSelectorComponent,
ItemActionEditorComponent,
InteractionSequenceEditorComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,

View File

@ -66,3 +66,12 @@
<app-inventory-slot-editor [character]="character" [itemgroups]="gameModel!.itemgroupsAsList"></app-inventory-slot-editor> <app-inventory-slot-editor [character]="character" [itemgroups]="gameModel!.itemgroupsAsList"></app-inventory-slot-editor>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
<mat-card>
<mat-card-header>
<mat-card-title>Character Interactions</mat-card-title>
</mat-card-header>
<mat-card-content>
<app-character-interaction-editor [character]="character" [gameModel]="gameModel"></app-character-interaction-editor>
</mat-card-content>
</mat-card>

View File

@ -0,0 +1,161 @@
<table mat-table [dataSource]="interactionDatasource" multiTemplateDataRows class="mat-elevation-z8">
<ng-container matColumnDef="sequence">
<th mat-header-cell *matHeaderCellDef>Sequence</th>
<td mat-cell *matCellDef="let interaction">
@if(isInteractionSequence(interaction)) {
<mat-icon>done</mat-icon>
} @else {
<mat-icon>close</mat-icon>
}
</td>
</ng-container>
<ng-container matColumnDef="source">
<th mat-header-cell *matHeaderCellDef>Source</th>
<td mat-cell *matCellDef="let interaction">
@if(interaction != editedElement) {
{{interaction.sourceCharacter.componentName}}
} @else {
<mat-form-field appearance="fill" class="long-form">
<mat-label>Source Character</mat-label>
<mat-select [(ngModel)]="editedElement!.sourceCharacter">
<mat-option *ngFor="let character of gameModel!.characters" [value]="character">{{character.componentName}}</mat-option>
</mat-select>
</mat-form-field>
}
</td>
</ng-container>
<ng-container matColumnDef="target">
<th mat-header-cell *matHeaderCellDef>Target</th>
<td mat-cell *matCellDef="let interaction">
@if(interaction != editedElement) {
@if(interaction.targetCharacter != undefined) {
{{interaction!.targetCharacter!.componentName}}
} @else {
<p class="warning">UNKNOWN CHARACTER</p>
}
} @else {
<mat-form-field appearance="fill" class="long-form">
<mat-label>Target Character</mat-label>
<mat-select [(ngModel)]="editedElement!.targetCharacter">
<mat-option *ngFor="let character of gameModel!.characters" [value]="character">{{character.componentName}}</mat-option>
</mat-select>
</mat-form-field>
}
</td>
</ng-container>
<ng-container matColumnDef="label">
<th mat-header-cell *matHeaderCellDef>Label</th>
<td mat-cell *matCellDef="let interaction">
@if(interaction != editedElement) {
{{interaction.interactionLabel}}
} @else {
<mat-form-field appearance="fill" class="long-form">
<mat-label>Label</mat-label>
<input matInput [(ngModel)]="editedElement!.interactionLabel">
</mat-form-field>
}
</td>
</ng-container>
<ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let interaction">
<button mat-icon-button *ngIf="editedElement !== interaction" [disabled]="editedElement != null" (click)="editInteraction(interaction)"><mat-icon>edit</mat-icon></button>
<button mat-icon-button *ngIf="editedElement === interaction" (click)="submitInteraction()"><mat-icon>done</mat-icon></button>
</td>
</ng-container>
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let interaction">
<button mat-icon-button color="warn" (click)="deleteInteraction(interaction)"><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)="addInteraction()"><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>
<!-- 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'">
<mat-accordion>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>Conditions</mat-panel-title>
</mat-expansion-panel-header>
<mat-tab-group>
<mat-tab label="ScriptAccount Conditions">
<app-scriptaccount-condition-editor [scriptAccounts]="gameModel!.scriptAccounts" [conditions]="element.scriptAccountConditions" [enableEditiong]="true"
(onCreateCondition)="onAddCondition(element, $event)" (onDeleteCondition)="onDeleteCondition(element, $event)"></app-scriptaccount-condition-editor>
</mat-tab>
<mat-tab label="Inventory Itemgroup Conditions">
<app-item-condition-editor [interaction]="element" [gameModel]="gameModel" [group]="true"></app-item-condition-editor>
</mat-tab>
<mat-tab label="Inventory Item Conditions">
<app-item-condition-editor [interaction]="element" [gameModel]="gameModel" [group]="false"></app-item-condition-editor>
</mat-tab>
<mat-tab label="Gamesystem Conditions">
<app-gamesystem-condition-editor [intgeraction]="element" [gamesystems]="gameModel!.gamesystemsAsList"
></app-gamesystem-condition-editor>
</mat-tab>
</mat-tab-group>
</mat-expansion-panel>
<mat-expansion-panel *ngIf="!isInteractionSequence(element)">
<mat-expansion-panel-header>
<mat-panel-title>Actions</mat-panel-title>
</mat-expansion-panel-header>
<mat-tab-group>
<mat-tab label="ScriptAccount Actions">
<app-scriptaccount-action-editor [scriptAccounts]="gameModel!.scriptAccounts" [actions]="getInterActionAsSolidInteraction(element)!.scriptAccountActions"
(onAddAction)="onAddAction(element, $event)" (onRemoveAction)="onRemoveAction(element, $event)"
[enableEditing]="true"
></app-scriptaccount-action-editor>
</mat-tab>
<mat-tab label="Inventory Itemgroup Actions">
<app-item-action-editor [interaction]="element" [gameModel]="gameModel" [group]="true"></app-item-action-editor>
</mat-tab>
<mat-tab label="Inventory Item Actions">
<app-item-action-editor [interaction]="element" [gameModel]="gameModel" [group]="false"></app-item-action-editor>
</mat-tab>
</mat-tab-group>
</mat-expansion-panel>
<mat-expansion-panel *ngIf="isInteractionSequence(element)">
<mat-expansion-panel-header>
<mat-panel-title>Sequence Elements</mat-panel-title>
</mat-expansion-panel-header>
<app-interaction-sequence-editor [interactionSequence]="element" [gameModel]="gameModel" [character]="character"></app-interaction-sequence-editor>
</mat-expansion-panel>
</mat-accordion>
<button mat-raised-button color="accent" *ngIf="!isInteractionSequence(element)" (click)="convertToSequence(element)">Convert to Sequence</button>
</div>
</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>

View File

@ -0,0 +1,55 @@
table {
width: 100%;
}
tr.example-detail-row {
height: 0;
}
tr.example-element-row:not(.example-expanded-row):hover {
background: #4e5157;
}
tr.example-element-row:not(.example-expanded-row):active {
background: #545456;
}
.example-element-row td {
border-bottom-width: 0;
}
.example-element-detail {
overflow: hidden;
width: 100%;
}
.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-delete, .mat-column-edit, .mat-column-expand, .mat-column-sequence {
width: 32px;
}
.warning {
color: red;
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CharacterInteractionEditorComponent } from './character-interaction-editor.component';
describe('CharacterInteractionEditorComponent', () => {
let component: CharacterInteractionEditorComponent;
let fixture: ComponentFixture<CharacterInteractionEditorComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CharacterInteractionEditorComponent]
})
.compileComponents();
fixture = TestBed.createComponent(CharacterInteractionEditorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,114 @@
import {Component, Input, OnInit} from '@angular/core';
import {Character} from "../../../project/game-model/characters/Character";
import {GameModel} from "../../../project/game-model/GameModel";
import {AbstractInteraction} from "../../../project/game-model/interactions/AbstractInteraction";
import {MatColumnDef, MatTable, MatTableDataSource} from "@angular/material/table";
import {InteractionSequences} from "../../../project/game-model/interactions/InteractionSequences";
import {animate, state, style, transition, trigger} from "@angular/animations";
import {Interaction} from "../../../project/game-model/interactions/Interaction";
import {Condition} from "../../../project/game-model/interactions/condition/Condition";
import {MatSnackBar} from "@angular/material/snack-bar";
import {ScriptAccountCondition} from "../../../project/game-model/gamesystems/conditions/ScriptAccountCondition";
import {ScriptAccountAction} from "../../../project/game-model/gamesystems/actions/ScriptAccountAction";
@Component({
selector: 'app-character-interaction-editor',
templateUrl: './character-interaction-editor.component.html',
styleUrl: './character-interaction-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 CharacterInteractionEditorComponent implements OnInit{
@Input() character: Character | undefined
@Input() gameModel: GameModel | undefined
displayedColumns: string[] = ['sequence', 'source', 'target', 'label', 'edit', 'delete']
columnsToDisplayWithExpand = [... this.displayedColumns, 'expand'];
expandedElement: AbstractInteraction | null = null;
editedElement: AbstractInteraction | null = null;
interactionDatasource: MatTableDataSource<AbstractInteraction> = new MatTableDataSource();
constructor(private snackbar: MatSnackBar) {
}
ngOnInit() {
this.interactionDatasource.data = this.gameModel!.getCharacterInteractionsByCharacter(this.character!);
}
isInteractionSequence(interaction: AbstractInteraction) {
return interaction instanceof InteractionSequences
}
getInterActionAsSolidInteraction(interaction: AbstractInteraction) {
if(interaction instanceof Interaction) {
return interaction as Interaction;
}
return undefined;
}
addInteraction() {
const interaction = new Interaction(this.character!, undefined, "")
this.editedElement = interaction;
const interactions = this.interactionDatasource.data;
interactions.push(interaction);
this.interactionDatasource.data = interactions;
}
submitInteraction() {
if(this.editedElement == undefined) {
return;
}
if(this.editedElement!.validate(this.character!)) {
this.gameModel!.addCharacterInteraction(this.editedElement);
this.editedElement = null;
} else {
this.snackbar.open("Invalid Interaction", "", {duration: 2000});
}
}
deleteInteraction(interaction: AbstractInteraction) {
this.gameModel!.removeCharacterInteraction(interaction);
this.interactionDatasource.data = this.gameModel!.characterInteractions;
}
editInteraction(interaction: AbstractInteraction) {
this.editedElement = interaction;
}
onAddCondition(interaction: AbstractInteraction, condition: ScriptAccountCondition) {
interaction!.addConditon(condition);
}
onDeleteCondition(interaction: AbstractInteraction, condition: ScriptAccountCondition) {
interaction!.removeCondition(condition);
}
onAddAction(interaction: AbstractInteraction, action: ScriptAccountAction) {
if(interaction instanceof Interaction) {
interaction.addAction(action);
}
}
onRemoveAction(interaction: AbstractInteraction, action: ScriptAccountAction) {
if(interaction instanceof Interaction) {
interaction.removeAction(action);
}
}
convertToSequence(interaction: Interaction) {
const interactionSequence = new InteractionSequences(interaction, interaction.interactionLabel);
this.gameModel!.removeCharacterInteraction(interaction);
this.gameModel!.addCharacterInteraction(interactionSequence);
this.interactionDatasource.data = this.gameModel!.characterInteractions;
}
}

View File

@ -0,0 +1,64 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {MatTableDataSource} from "@angular/material/table";
import {ScriptAccount} from "../../../project/game-model/scriptAccounts/ScriptAccount";
import {Transition} from "../../../project/game-model/gamesystems/transitions/Transition";
import {ScriptAccountAction} from "../../../project/game-model/gamesystems/actions/ScriptAccountAction";
import {TemplateElement} from "../../../project/game-model/templates/TemplateElement";
import {
SimpleTemplateTransition
} from "../../../project/game-model/templates/simpleGamesystem/SimpleTemplateTransition";
import {ScriptAccountCondition} from "../../../project/game-model/gamesystems/conditions/ScriptAccountCondition";
@Component({
selector: 'app-scriptaccount-action-editor',
templateUrl: './scriptaccount-action-editor.component.html',
styleUrl: './scriptaccount-action-editor.component.scss'
})
export class ScriptaccountActionEditorComponent implements OnInit{
@Input() scriptAccounts: ScriptAccount[] = []
@Input() enableEditing: boolean = false;
@Input() actions: ScriptAccountAction[] = []
@Output() onAddAction: EventEmitter<ScriptAccountAction> = new EventEmitter<ScriptAccountAction>();
@Output() onRemoveAction: EventEmitter<ScriptAccountAction> = new EventEmitter<ScriptAccountAction>();
dataSource: MatTableDataSource<ScriptAccountAction> = new MatTableDataSource();
displayedColumns: string[] = ['scriptAccount', "valueChange", 'edit', 'delete'];
editedAction: ScriptAccountAction | undefined
addedAction: ScriptAccountAction | undefined
ngOnInit() {
this.dataSource.data = this.actions
if(!this.enableEditing) {
this.displayedColumns = this.displayedColumns.slice(0, -2);
}
}
editAction(scriptAccountAction: ScriptAccountAction) {
this.editedAction = scriptAccountAction;
}
createNewAction() {
this.addedAction = new ScriptAccountAction(new ScriptAccount("", ""), 0);
this.editedAction = this.addedAction;
this.dataSource.data = this.dataSource.data.concat(this.addedAction);
}
finishEditing() {
if(this.addedAction != undefined && this.addedAction.scriptAccount.componentName !== '') {
this.onAddAction.emit(this.addedAction);
this.addedAction = undefined;
}
this.editedAction = undefined;
}
deleteAction(deletedAction: ScriptAccountAction) {
this.dataSource.data = this.dataSource.data.filter(action => action !== deletedAction);
this.onRemoveAction.emit(deletedAction);
}
}

View File

@ -39,13 +39,7 @@ export class ScriptaccountConditionEditorComponent implements OnInit{
finishEditing() { finishEditing() {
if(this.addedCondition != undefined) { if(this.addedCondition != undefined) {
const createdCondition = ScriptAccountCondition.constructScriptAccountCondition(this.addedCondition.scriptAccount, this.addedCondition.minValue, this.addedCondition.maxValue); this.onCreateCondition.emit(this.addedCondition);
if(createdCondition != undefined) {
console.log(createdCondition)
this.onCreateCondition.emit(createdCondition);
console.log(this.conditions)
this.dataSource.data = this.conditions;
}
this.addedCondition = undefined; this.addedCondition = undefined;
} }
this.editedCondition = undefined; this.editedCondition = undefined;
@ -55,13 +49,8 @@ export class ScriptaccountConditionEditorComponent implements OnInit{
this.editedCondition = condition; this.editedCondition = condition;
} }
deleteCondition(condition: ScriptAccountCondition) { deleteCondition(deletedCondition: ScriptAccountCondition) {
if(this.addedCondition === condition) { this.onDeleteCondition.emit(deletedCondition);
this.addedCondition = undefined; this.dataSource.data = this.dataSource.data.filter(condition => condition !== deletedCondition);
this.dataSource.data = this.conditions.concat();
} else {
this.onDeleteCondition.emit(condition);
this.dataSource.data = this.conditions.concat();
}
} }
} }

View File

@ -63,6 +63,7 @@
<div class="example-element-detail" <div class="example-element-detail"
[@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'"> [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'">
<app-scriptaccount-condition-editor [enableEditiong]="false" [conditions]="element.scriptAccountConditions" ></app-scriptaccount-condition-editor> <app-scriptaccount-condition-editor [enableEditiong]="false" [conditions]="element.scriptAccountConditions" ></app-scriptaccount-condition-editor>
<app-scriptaccount-action-editor [enableEditing]="false" [actions]="element.scriptAccountActions"></app-scriptaccount-action-editor>
</div> </div>
</td> </td>
</ng-container> </ng-container>

View File

@ -1,89 +0,0 @@
import {Component, Input, OnInit} from '@angular/core';
import {MatTableDataSource} from "@angular/material/table";
import {ScriptAccount} from "../../../../project/game-model/scriptAccounts/ScriptAccount";
import {Transition} from "../../../../project/game-model/gamesystems/transitions/Transition";
import {ScriptAccountAction} from "../../../../project/game-model/gamesystems/actions/ScriptAccountAction";
import {TemplateElement} from "../../../../project/game-model/templates/TemplateElement";
import {
SimpleTemplateTransition
} from "../../../../project/game-model/templates/simpleGamesystem/SimpleTemplateTransition";
@Component({
selector: 'app-scriptaccount-action-editor',
templateUrl: './scriptaccount-action-editor.component.html',
styleUrl: './scriptaccount-action-editor.component.scss'
})
export class ScriptaccountActionEditorComponent implements OnInit{
@Input() transition: Transition<any> | undefined
@Input() scriptAccounts: ScriptAccount[] = []
@Input() enableEditing: boolean = false;
@Input() templateElement: TemplateElement | undefined
dataSource: MatTableDataSource<ScriptAccountAction> = new MatTableDataSource();
displayedColumns: string[] = ['scriptAccount', "valueChange", 'edit', 'delete'];
editedAction: ScriptAccountAction | undefined
addedAction: ScriptAccountAction | undefined
ngOnInit() {
this.dataSource.data = this.getDisplayedActions()
if(!this.enableEditing) {
this.displayedColumns = this.displayedColumns.slice(0, -2);
}
}
editAction(scriptAccountAction: ScriptAccountAction) {
this.editedAction = scriptAccountAction;
}
createNewAction() {
this.addedAction = new ScriptAccountAction(new ScriptAccount("", ""), 0);
this.editedAction = this.addedAction;
this.dataSource.data = this.dataSource.data.concat(this.addedAction);
}
finishEditing() {
if(this.addedAction != undefined && this.addedAction.scriptAccount.componentName !== '') {
if(this.templateElement != undefined && this.transition instanceof SimpleTemplateTransition) {
if(this.transition.actionMap.has(this.templateElement!)) {
this.transition.actionMap.get(this.templateElement!)!.push(this.addedAction)
} else {
this.transition.actionMap.set(this.templateElement!, [this.addedAction])
}
} else {
this.transition?.addScriptAccountAction(this.addedAction)
}
this.dataSource.data = this.getDisplayedActions();
this.addedAction = undefined;
}
this.editedAction = undefined;
}
getDisplayedActions(): ScriptAccountAction[] {
if(this.templateElement == undefined) {
return this.transition!.scriptAccountActions.map(action => action);
} else if(this.transition instanceof SimpleTemplateTransition) {
return this.transition!.actionMap.get(this.templateElement)!
} else {
return []
}
}
deleteAction(action: ScriptAccountAction) {
if(this.templateElement != undefined && this.transition instanceof SimpleTemplateTransition && this.transition.actionMap.has(this.templateElement!)) {
const updatedAction = this.transition.actionMap.get(this.templateElement)!.filter(currentAction =>
currentAction.scriptAccount !== action.scriptAccount)
this.transition.actionMap.set(this.templateElement!, updatedAction)
} else {
this.transition!.removeScriptAccountAction(action.scriptAccount)
}
this.dataSource.data = this.getDisplayedActions()
}
}

View File

@ -42,7 +42,8 @@
[@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'"> [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'">
<div class="condition-action-container"> <div class="condition-action-container">
<div class="action-container"> <div class="action-container">
<app-scriptaccount-action-editor [templateElement]="templateElement" [transition]="element" [scriptAccounts]="scriptAccounts" [enableEditing]="true"></app-scriptaccount-action-editor> <app-scriptaccount-action-editor [actions]="getDisplayedActions(element)" [scriptAccounts]="scriptAccounts" [enableEditing]="true"
(onAddAction)="onAddAction(element, $event)" (onRemoveAction)="onDeleteAction(element, $event)"></app-scriptaccount-action-editor>
</div> </div>
<div class="condition-container"> <div class="condition-container">
<app-scriptaccount-condition-editor [conditions]="getDisplayedConditions(element)" [scriptAccounts]="scriptAccounts" [enableEditiong]="true" <app-scriptaccount-condition-editor [conditions]="getDisplayedConditions(element)" [scriptAccounts]="scriptAccounts" [enableEditiong]="true"

View File

@ -12,6 +12,7 @@ import {TemplateElement} from "../../../../project/game-model/templates/Template
import { import {
SimpleTemplateTransition SimpleTemplateTransition
} from "../../../../project/game-model/templates/simpleGamesystem/SimpleTemplateTransition"; } from "../../../../project/game-model/templates/simpleGamesystem/SimpleTemplateTransition";
import {ScriptAccountAction} from "../../../../project/game-model/gamesystems/actions/ScriptAccountAction";
@Component({ @Component({
selector: 'app-simple-transition-editor', selector: 'app-simple-transition-editor',
templateUrl: './simple-transition-editor.component.html', templateUrl: './simple-transition-editor.component.html',
@ -102,7 +103,6 @@ export class SimpleTransitionEditorComponent implements OnInit {
} else { } else {
transition.addScriptAccountCondition(condition); transition.addScriptAccountCondition(condition);
} }
} }
deleteCondition(trasition: SimpleTransition, condition: ScriptAccountCondition) { deleteCondition(trasition: SimpleTransition, condition: ScriptAccountCondition) {
@ -126,4 +126,32 @@ export class SimpleTransitionEditorComponent implements OnInit {
return []; return [];
} }
} }
onAddAction(transition: SimpleTransition, action: ScriptAccountAction) {
if(this.templateElement != undefined && transition instanceof SimpleTemplateTransition) {
transition.actionMap.get(this.templateElement)!.push(action)
} else {
transition.addScriptAccountAction(action);
}
}
onDeleteAction(transition: SimpleTransition, action: ScriptAccountAction) {
if(this.templateElement != undefined && transition instanceof SimpleTemplateTransition) {
let updatedActions = transition.actionMap.get(this.templateElement)!
updatedActions = updatedActions.filter(currentAction => action.scriptAccount !== currentAction.scriptAccount);
transition.actionMap.set(this.templateElement, updatedActions);
} else {
transition.removeScriptAccountAction(action.scriptAccount);
}
}
getDisplayedActions(transition: SimpleTransition) {
if(this.templateElement == undefined) {
return transition.scriptAccountActions
} else if(transition instanceof SimpleTemplateTransition) {
return transition.actionMap.get(this.templateElement)!
} else {
return [];
}
}
} }

View File

@ -0,0 +1,77 @@
<table mat-table [dataSource]="datasource" class="mat-elevation-z8">
<ng-container matColumnDef="characterDependency">
<th mat-header-cell *matHeaderCellDef>Dependency</th>
<td mat-cell *matCellDef="let condition">
@if(condition === editedCondition) {
<mat-form-field class="long-form" appearance="fill">
<mat-label>Character Dependency</mat-label>
<mat-select [(ngModel)]="editedCondition!.characterDependency">
<mat-option [value]="CharacterDependency.NONE">None</mat-option>
<mat-option [value]="CharacterDependency.SRC">Source</mat-option>
<mat-option [value]="CharacterDependency.TARGET">Target</mat-option>
</mat-select>
</mat-form-field>
} @else {
{{condition.characterDependency}}
}
</td>
</ng-container>
<ng-container matColumnDef="targetGamesystem">
<th mat-header-cell *matHeaderCellDef>Gamesystem</th>
<td mat-cell *matCellDef="let condition">
@if(condition === editedCondition) {
<mat-form-field class="long-form" appearance="fill">
<mat-label>Character Dependency</mat-label>
<mat-select [(ngModel)]="editedCondition!.targetGamesystem">
<mat-option *ngFor="let gamesystem of gamesystems" [value]="gamesystem">{{gamesystem.componentName}}</mat-option>
</mat-select>
</mat-form-field>
} @else {
@if(condition.targetGamesystem != undefined) {
{{condition.targetGamesystem.componentName}}
} @else {
<p>UNKNOWN GAMESYSTEM</p>
}
}
</td>
</ng-container>
<ng-container matColumnDef="requieredState">
<th mat-header-cell *matHeaderCellDef>State</th>
<td mat-cell *matCellDef="let condition">
@if(condition === editedCondition && editedCondition!.targetGamesystem != undefined) {
<mat-form-field appearance="fill" class="long-form">
<mat-select [(ngModel)]="editedCondition!.requieredState">
<mat-option *ngFor="let state of editedCondition!.targetGamesystem.states" [value]="state">{{state.computeStateLabel()}}</mat-option>
</mat-select>
</mat-form-field>
} @else {
@if(condition.requieredState != undefined) {
{{condition.requieredState.computeStateLabel()}}
} @else {
<p>UNKNOWN STATE</p>
}
}
</td>
</ng-container>
<ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let condition">
<button mat-icon-button *ngIf="editedCondition === condition" (click)="finishEditingCondition()"><mat-icon>done</mat-icon></button>
<button mat-icon-button *ngIf="editedCondition != condition" [disabled]="editedCondition != undefined" (click)="editCondition(condition)"><mat-icon>edit</mat-icon></button>
</td>
</ng-container>
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef>
<button mat-icon-button (click)="addCondition()"><mat-icon>addd</mat-icon></button>
</th>
<td mat-cell *matCellDef="let condition">
<button mat-icon-button color="warn" (click)="deleteCondition(condition)"><mat-icon>delete</mat-icon></button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

View File

@ -0,0 +1,3 @@
.mat-column-edit, .mat-column-delete {
width: 32px;
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GamesystemConditionEditorComponent } from './gamesystem-condition-editor.component';
describe('GamesystemConditionEditorComponent', () => {
let component: GamesystemConditionEditorComponent;
let fixture: ComponentFixture<GamesystemConditionEditorComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [GamesystemConditionEditorComponent]
})
.compileComponents();
fixture = TestBed.createComponent(GamesystemConditionEditorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,69 @@
import {Component, Input, OnInit} from '@angular/core';
import {GamesystemCondition} from "../../../../project/game-model/interactions/condition/GamesystemCondition";
import {MatSnackBar} from "@angular/material/snack-bar";
import {MatTableDataSource} from "@angular/material/table";
import {CharacterDependency} from "../../../../project/game-model/interactions/CharacterDependency";
import {Gamesystem} from "../../../../project/game-model/gamesystems/Gamesystem";
import {AbstractInteraction} from "../../../../project/game-model/interactions/AbstractInteraction";
@Component({
selector: 'app-gamesystem-condition-editor',
templateUrl: './gamesystem-condition-editor.component.html',
styleUrl: './gamesystem-condition-editor.component.scss'
})
export class GamesystemConditionEditorComponent implements OnInit{
@Input() intgeraction: AbstractInteraction | undefined
@Input() gamesystems: Gamesystem<any, any>[] = []
editedCondition: GamesystemCondition | undefined
addedCondition: GamesystemCondition | undefined
datasource: MatTableDataSource<GamesystemCondition> = new MatTableDataSource<GamesystemCondition>();
displayedColumns: string[] = ["characterDependency", "targetGamesystem", "requieredState", "edit", "delete"]
constructor(private snackbar: MatSnackBar) {
}
ngOnInit() {
this.datasource.data = this.intgeraction!.gamesystemConditions
}
editCondition(condition: GamesystemCondition) {
this.editedCondition = condition;
}
finishEditingCondition() {
if(this.editedCondition == undefined) {
return;
}
if(this.editedCondition!.validate()) {
if(this.editedCondition === this.addedCondition) {
this.intgeraction!.addConditon(this.addedCondition);
this.addedCondition = undefined
}
this.editedCondition = undefined;
} else {
this.snackbar.open("Invalid Condition", "", {duration: 2000});
}
}
deleteCondition(deletedCondition: GamesystemCondition) {
this.intgeraction!.removeCondition(deletedCondition);
this.datasource.data = this.datasource.data.filter(condition => condition !== deletedCondition);
}
addCondition() {
const condition = new GamesystemCondition(CharacterDependency.NONE, undefined, undefined);
const displayedConditions = this.datasource.data;
displayedConditions.push(condition);
this.editedCondition = condition;
this.addedCondition = condition;
this.datasource.data = displayedConditions;
}
protected readonly CharacterDependency = CharacterDependency;
}

View File

@ -0,0 +1,79 @@
<table mat-table class="mat-elevation-z8" [dataSource]="conditionDataSource">
<ng-container matColumnDef="item">
<th mat-header-cell *matHeaderCellDef>{{group? 'Itemgroup':'Item'}}</th>
<td mat-cell *matCellDef="let condition">
@if(group) {
@if(condition == editedCondition) {
<mat-form-field appearance="fill" class="long-form">
<mat-label>Itemgroup</mat-label>
<mat-select [(ngModel)]="conditionAsItemgroupCondition(condition)!.itemgroup">
<mat-option *ngFor="let itemgroup of gameModel!.itemgroupsAsList" [value]="itemgroup">{{itemgroup.componentName}}</mat-option>
</mat-select>
</mat-form-field>
} @else {
<p *ngIf="conditionAsItemgroupCondition(condition)!.itemgroup != undefined">
{{conditionAsItemgroupCondition(condition)!.itemgroup!.componentName}}
</p>
}
} @else {
@if(condition == editedCondition) {
<app-item-selector [itemgroups]="gameModel!.itemgroupsAsList" [selectedItem]="conditionAsItemCondition(condition)!.item"
(onSelectedItemChange)="onSelectedItemChange(condition, $event)"
></app-item-selector>
} @else {
<p *ngIf="conditionAsItemCondition(condition)!.item != undefined">
{{conditionAsItemCondition(condition)!.item!.componentName}}
</p>
}
}
</td>
</ng-container>
<ng-container matColumnDef="min">
<th mat-header-cell *matHeaderCellDef>Min</th>
<td mat-cell *matCellDef="let condition">
@if(condition == editedCondition) {
<mat-form-field appearance="fill" class="long-form">
<mat-label>Min Value</mat-label>
<input matInput [(ngModel)]="editedCondition!.minValue">
</mat-form-field>
} @else {
{{condition.minValue}}
}
</td>
</ng-container>
<ng-container matColumnDef="max">
<th mat-header-cell *matHeaderCellDef>Min</th>
<td mat-cell *matCellDef="let condition">
@if(condition == editedCondition) {
<mat-form-field appearance="fill" class="long-form">
<mat-label>Max Value</mat-label>
<input matInput [(ngModel)]="editedCondition!.maxValue">
</mat-form-field>
} @else {
{{condition.maxValue}}
}
</td>
</ng-container>
<ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let condition">
<button mat-icon-button *ngIf="condition !== editedCondition" [disabled]="editedCondition != undefined" (click)="editCondition(condition)"><mat-icon>edit</mat-icon></button>
<button mat-icon-button *ngIf="condition === editedCondition" (click)="finishEditing()"><mat-icon>done</mat-icon></button>
</td>
</ng-container>
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef>
<button mat-icon-button (click)="addCondition()"><mat-icon>add</mat-icon></button>
</th>
<td mat-cell *matCellDef="let condition">
<button mat-icon-button (click)="deleteCondition(condition)" color="warn"><mat-icon>delete</mat-icon></button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

View File

@ -0,0 +1,3 @@
.mat-column-edit, .mat-column-delete {
width: 32px;
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ItemConditionEditorComponent } from './item-condition-editor.component';
describe('ItemConditionEditorComponent', () => {
let component: ItemConditionEditorComponent;
let fixture: ComponentFixture<ItemConditionEditorComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ItemConditionEditorComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ItemConditionEditorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,90 @@
import {Component, Input, OnInit} from '@angular/core';
import {AbstractInteraction} from "../../../../project/game-model/interactions/AbstractInteraction";
import {GameModel} from "../../../../project/game-model/GameModel";
import {MatTable, MatTableDataSource} from "@angular/material/table";
import {InventoryCondition} from "../../../../project/game-model/interactions/condition/InventoryCondition";
import {InventoryItemCondition} from "../../../../project/game-model/interactions/condition/InventoryItemCondition";
import {CharacterDependency} from "../../../../project/game-model/interactions/CharacterDependency";
import {
InventoryItemgroupCondition
} from "../../../../project/game-model/interactions/condition/InventoryItemgroupCondition";
import {Item} from "../../../../project/game-model/inventory/Item";
@Component({
selector: 'app-item-condition-editor',
templateUrl: './item-condition-editor.component.html',
styleUrl: './item-condition-editor.component.scss'
})
export class ItemConditionEditorComponent implements OnInit{
@Input() interaction: AbstractInteraction | undefined
@Input() gameModel: GameModel | undefined
@Input() group: boolean = false;
conditionDataSource: MatTableDataSource<InventoryCondition> = new MatTableDataSource();
displayedColumns: string[] = ["item", "min", "max", "edit", "delete"];
editedCondition: InventoryCondition | undefined
addedCondition: InventoryCondition | undefined
ngOnInit() {
this.conditionDataSource.data = this.interaction!.inventoryConditions;
}
addCondition() {
if(this.group) {
this.addedCondition = new InventoryItemgroupCondition(CharacterDependency.NONE, 0, 0, undefined);
} else {
this.addedCondition = new InventoryItemCondition(CharacterDependency.NONE, 0, 0, undefined);
}
this.editedCondition = this.addedCondition;
const displayedColumns = this.conditionDataSource.data;
displayedColumns.push(this.addedCondition);
this.conditionDataSource.data = displayedColumns;
}
editCondition(condition: InventoryCondition) {
this.editedCondition = condition;
}
finishEditing() {
if(this.editedCondition == undefined) {
return;
}
if(this.editedCondition.validate()) {
if(this.addedCondition != undefined) {
this.interaction!.addConditon(this.addedCondition);
this.addedCondition = undefined;
}
this.editedCondition = undefined;
}
}
deleteCondition(deletedCondition: InventoryCondition) {
this.interaction!.removeCondition(deletedCondition);
this.conditionDataSource.data = this.conditionDataSource.data.filter(condition => condition !== deletedCondition);
}
conditionAsItemCondition(condition: InventoryCondition) {
if(condition instanceof InventoryItemCondition) {
return condition;
} else {
return undefined;
}
}
conditionAsItemgroupCondition(condition: InventoryCondition) {
if(condition instanceof InventoryItemgroupCondition) {
return condition;
} else {
return undefined;
}
}
onSelectedItemChange(condition: InventoryCondition, item: Item) {
(condition as InventoryItemCondition).item = item;
console.log(condition)
}
}

View File

@ -0,0 +1,13 @@
<mat-form-field appearance="fill">
<mat-label>Itemgroup</mat-label>
<mat-select [(ngModel)]="selectedItemgroup">
<mat-option *ngFor="let itemgroup of itemgroups" [value]="itemgroup">{{itemgroup.componentName}}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="fill" *ngIf="selectedItemgroup != undefined">
<mat-label>Item</mat-label>
<mat-select [(ngModel)]="selectedItem" (ngModelChange)="onItemChange($event)">
<mat-option *ngFor="let item of selectedItemgroup!.getInheritedItems()" [value]="item">{{item.componentName}}</mat-option>
</mat-select>
</mat-form-field>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ItemSelectorComponent } from './item-selector.component';
describe('ItemSelectorComponent', () => {
let component: ItemSelectorComponent;
let fixture: ComponentFixture<ItemSelectorComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ItemSelectorComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ItemSelectorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,30 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {ItemGroup} from "../../../../../project/game-model/inventory/ItemGroup";
import {Item} from "../../../../../project/game-model/inventory/Item";
import {MatFormField} from "@angular/material/form-field";
import {MatSelect} from "@angular/material/select";
@Component({
selector: 'app-item-selector',
templateUrl: './item-selector.component.html',
styleUrl: './item-selector.component.scss'
})
export class ItemSelectorComponent implements OnInit{
@Input() itemgroups: ItemGroup[] = []
@Input() selectedItem: Item | undefined
@Output() onSelectedItemChange = new EventEmitter<Item>();
selectedItemgroup: ItemGroup | undefined
ngOnInit() {
if(this.selectedItem != undefined) {
this.selectedItemgroup = this.itemgroups.find(itemgroup => itemgroup.getInheritedItems().includes(this.selectedItem!));
}
}
onItemChange(item: Item) {
this.selectedItem = item;
this.onSelectedItemChange.emit(item);
}
}

View File

@ -0,0 +1,141 @@
<table mat-table [dataSource]="sequenceDatasource" multiTemplateDataRows class="mat-elevation-z8">
<ng-container matColumnDef="source">
<th mat-header-cell *matHeaderCellDef>Source</th>
<td mat-cell *matCellDef="let interaction">
@if(interaction != editedElement) {
{{interaction.sourceCharacter.componentName}}
} @else {
<mat-form-field appearance="fill" class="long-form">
<mat-label>Source Character</mat-label>
<mat-select [(ngModel)]="editedElement!.sourceCharacter">
<mat-option *ngFor="let character of gameModel!.characters" [value]="character">{{character.componentName}}</mat-option>
</mat-select>
</mat-form-field>
}
</td>
</ng-container>
<ng-container matColumnDef="target">
<th mat-header-cell *matHeaderCellDef>Target</th>
<td mat-cell *matCellDef="let interaction">
@if(interaction != editedElement) {
@if(interaction.targetCharacter != undefined) {
{{interaction!.targetCharacter!.componentName}}
} @else {
<p class="warning">UNKNOWN CHARACTER</p>
}
} @else {
<mat-form-field appearance="fill" class="long-form">
<mat-label>Target Character</mat-label>
<mat-select [(ngModel)]="editedElement!.targetCharacter">
<mat-option *ngFor="let character of gameModel!.characters" [value]="character">{{character.componentName}}</mat-option>
</mat-select>
</mat-form-field>
}
</td>
</ng-container>
<ng-container matColumnDef="label">
<th mat-header-cell *matHeaderCellDef>Label</th>
<td mat-cell *matCellDef="let interaction">
@if(interaction != editedElement) {
<a class="navigation-link" (click)="onNavigateToInteraction(interaction)"> {{interaction.interactionLabel}}</a>
} @else {
<mat-form-field appearance="fill" class="long-form">
<mat-label>Label</mat-label>
<input matInput [(ngModel)]="editedElement!.interactionLabel">
</mat-form-field>
}
</td>
</ng-container>
<ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let interaction">
<button mat-icon-button *ngIf="editedElement !== interaction" [disabled]="editedElement !== undefined" (click)="editInteraction(interaction)"><mat-icon>edit</mat-icon></button>
<button mat-icon-button *ngIf="editedElement === interaction" (click)="submitInteraction()"><mat-icon>done</mat-icon></button>
</td>
</ng-container>
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let interaction">
<button mat-icon-button color="warn" (click)="deleteInteraction(interaction)"><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)="addInteraction()"><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>
<!-- 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'">
<mat-accordion>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>Conditions</mat-panel-title>
</mat-expansion-panel-header>
<mat-tab-group>
<mat-tab label="ScriptAccount Conditions">
<app-scriptaccount-condition-editor [scriptAccounts]="gameModel!.scriptAccounts" [conditions]="element.scriptAccountConditions" [enableEditiong]="true"
(onCreateCondition)="onAddCondition(element, $event)" (onDeleteCondition)="onDeleteCondition(element, $event)"></app-scriptaccount-condition-editor>
</mat-tab>
<mat-tab label="Inventory Itemgroup Conditions">
<app-item-condition-editor [interaction]="element" [gameModel]="gameModel" [group]="true"></app-item-condition-editor>
</mat-tab>
<mat-tab label="Inventory Item Conditions">
<app-item-condition-editor [interaction]="element" [gameModel]="gameModel" [group]="false"></app-item-condition-editor>
</mat-tab>
<mat-tab label="Gamesystem Conditions">
<app-gamesystem-condition-editor [intgeraction]="element" [gamesystems]="gameModel!.gamesystemsAsList"
></app-gamesystem-condition-editor>
</mat-tab>
</mat-tab-group>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>Actions</mat-panel-title>
</mat-expansion-panel-header>
<mat-tab-group>
<mat-tab label="ScriptAccount Actions">
<app-scriptaccount-action-editor [scriptAccounts]="gameModel!.scriptAccounts" [actions]="element.scriptAccountActions"
(onAddAction)="onAddAction(element, $event)" (onRemoveAction)="onRemoveAction(element, $event)"
[enableEditing]="true"
></app-scriptaccount-action-editor>
</mat-tab>
<mat-tab label="Inventory Itemgroup Actions">
<app-item-action-editor [interaction]="element" [gameModel]="gameModel" [group]="true"></app-item-action-editor>
</mat-tab>
<mat-tab label="Inventory Item Actions">
<app-item-action-editor [interaction]="element" [gameModel]="gameModel" [group]="false"></app-item-action-editor>
</mat-tab>
</mat-tab-group>
</mat-expansion-panel>
</mat-accordion>
</div>
</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>

View File

@ -0,0 +1,65 @@
table {
width: 100%;
}
tr.example-detail-row {
height: 0;
}
tr.example-element-row:not(.example-expanded-row):hover {
background: #4e5157;
}
tr.example-element-row:not(.example-expanded-row):active {
background: #545456;
}
.example-element-row td {
border-bottom-width: 0;
}
.example-element-detail {
overflow: hidden;
width: 100%;
}
.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-delete, .mat-column-edit, .mat-column-expand, .mat-column-sequence {
width: 32px;
}
.warning {
color: red;
}
.navigation-link {
text-decoration: none;
color: white;
}
.navigation-link:hover {
text-decoration: underline;
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { InteractionSequenceEditorComponent } from './interaction-sequence-editor.component';
describe('InteractionSequenceEditorComponent', () => {
let component: InteractionSequenceEditorComponent;
let fixture: ComponentFixture<InteractionSequenceEditorComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [InteractionSequenceEditorComponent]
})
.compileComponents();
fixture = TestBed.createComponent(InteractionSequenceEditorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,114 @@
import {Component, Input, OnInit} from '@angular/core';
import {InteractionSequences} from "../../../project/game-model/interactions/InteractionSequences";
import {Interaction} from "../../../project/game-model/interactions/Interaction";
import {MatTableDataSource} from "@angular/material/table";
import {GameModel} from "../../../project/game-model/GameModel";
import {Character} from "../../../project/game-model/characters/Character";
import {MatSnackBar} from "@angular/material/snack-bar";
import {AbstractInteraction} from "../../../project/game-model/interactions/AbstractInteraction";
import {ScriptAccountCondition} from "../../../project/game-model/gamesystems/conditions/ScriptAccountCondition";
import {ScriptAccountAction} from "../../../project/game-model/gamesystems/actions/ScriptAccountAction";
import {animate, state, style, transition, trigger} from "@angular/animations";
@Component({
selector: 'app-interaction-sequence-editor',
templateUrl: './interaction-sequence-editor.component.html',
styleUrl: './interaction-sequence-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 InteractionSequenceEditorComponent implements OnInit{
@Input() interactionSequence: InteractionSequences | undefined;
@Input() gameModel: GameModel | undefined
@Input() character: Character | undefined
currentInteractionNode: Interaction | undefined;
sequenceDatasource = new MatTableDataSource<Interaction>()
displayedColumns: string[] = ['source', 'target', 'label', 'edit', 'delete']
columnsToDisplayWithExpand = [... this.displayedColumns, 'expand'];
expandedElement: AbstractInteraction | null = null;
editedElement: Interaction | undefined
constructor(private snackbar: MatSnackBar) {
}
ngOnInit() {
if(this.interactionSequence != undefined) {
this.currentInteractionNode = this.interactionSequence!.rootInteraction.root;
this.assignData();
}
}
private assignData() {
if(this.currentInteractionNode != undefined) {
this.sequenceDatasource.data = this.interactionSequence!.findInteraction(this.currentInteractionNode)!.children.map(node => node.root);
}
}
editInteraction(interaction: Interaction) {
this.editedElement = interaction;
}
submitInteraction() {
if(this.editedElement == undefined) {
return;
}
if(this.editedElement!.validate(this.character!)) {
const node = this.interactionSequence!.findInteraction(this.currentInteractionNode!);
if(node != undefined) {
node.addInteraction(this.editedElement!);
}
this.editedElement = undefined;
} else {
this.snackbar.open("Invalid Interaction", "", {duration: 2000});
}
}
deleteInteraction(interaction: Interaction) {
if(this.currentInteractionNode != undefined) {
const node = this.interactionSequence?.findInteraction(this.currentInteractionNode);
if(node != undefined) {
node.removeChildInteraction(interaction);
this.assignData();
}
}
}
addInteraction() {
const interaction = new Interaction(this.character!, undefined, "")
this.editedElement = interaction;
const interactions = this.sequenceDatasource.data;
interactions.push(interaction);
this.sequenceDatasource.data = interactions;
}
onAddCondition(interaction: AbstractInteraction, condition: ScriptAccountCondition) {
interaction!.addConditon(condition);
}
onDeleteCondition(interaction: AbstractInteraction, condition: ScriptAccountCondition) {
interaction!.removeCondition(condition);
}
onAddAction(interaction: AbstractInteraction, action: ScriptAccountAction) {
if(interaction instanceof Interaction) {
interaction.addAction(action);
}
}
onRemoveAction(interaction: AbstractInteraction, action: ScriptAccountAction) {
if(interaction instanceof Interaction) {
interaction.removeAction(action);
}
}
onNavigateToInteraction(interaction: Interaction) {
this.currentInteractionNode = interaction;
this.assignData();
}
}

View File

@ -0,0 +1,65 @@
<table mat-table class="mat-elevation-z8" [dataSource]="actionDatasource">
<ng-container matColumnDef="item">
<th mat-header-cell *matHeaderCellDef>{{group? 'Itemgroup':'Item'}}</th>
<td mat-cell *matCellDef="let action">
@if(group) {
@if(action == editedAction) {
<mat-form-field appearance="fill" class="long-form">
<mat-label>Itemgroup</mat-label>
<mat-select [(ngModel)]="actionAsItemgroupAction(action)!.itemgroup">
<mat-option *ngFor="let itemgroup of gameModel!.itemgroupsAsList" [value]="itemgroup">{{itemgroup.componentName}}</mat-option>
</mat-select>
</mat-form-field>
} @else {
<p *ngIf="actionAsItemgroupAction(action)!.itemgroup != undefined">
{{actionAsItemgroupAction(action)!.itemgroup!.componentName}}
</p>
}
} @else {
@if(action == editedAction) {
<app-item-selector [itemgroups]="gameModel!.itemgroupsAsList" [selectedItem]="actionAsItemAction(action)!.item"
(onSelectedItemChange)="onSelectedItemChange(action, $event)"
></app-item-selector>
} @else {
<p *ngIf="actionAsItemAction(action)!.item != undefined">
{{actionAsItemAction(action)!.item!.componentName}}
</p>
}
}
</td>
</ng-container>
<ng-container matColumnDef="delta">
<th mat-header-cell *matHeaderCellDef>Min</th>
<td mat-cell *matCellDef="let action">
@if(action == editedAction) {
<mat-form-field appearance="fill" class="long-form">
<mat-label>Min Value</mat-label>
<input matInput [(ngModel)]="action!.valueChange">
</mat-form-field>
} @else {
{{action.valueChange}}
}
</td>
</ng-container>
<ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let action">
<button mat-icon-button *ngIf="action !== editedAction" [disabled]="editedAction != undefined" (click)="editAction(action)"><mat-icon>edit</mat-icon></button>
<button mat-icon-button *ngIf="action === editedAction" (click)="finishEditing()"><mat-icon>done</mat-icon></button>
</td>
</ng-container>
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef>
<button mat-icon-button (click)="addAction()"><mat-icon>add</mat-icon></button>
</th>
<td mat-cell *matCellDef="let action">
<button mat-icon-button (click)="deleteAction(action)" color="warn"><mat-icon>delete</mat-icon></button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

View File

@ -0,0 +1,3 @@
.mat-column-edit, .mat-column-delete {
width: 32px;
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ItemActionEditorComponent } from './item-action-editor.component';
describe('ItemActionEditorComponent', () => {
let component: ItemActionEditorComponent;
let fixture: ComponentFixture<ItemActionEditorComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ItemActionEditorComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ItemActionEditorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,109 @@
import {Component, Input} from '@angular/core';
import {
MatCell,
MatCellDef,
MatColumnDef,
MatHeaderCell,
MatHeaderRow,
MatHeaderRowDef,
MatRow, MatRowDef, MatTable, MatTableDataSource
} from "@angular/material/table";
import {MatFormField, MatLabel} from "@angular/material/form-field";
import {MatIcon} from "@angular/material/icon";
import {MatIconButton} from "@angular/material/button";
import {MatInput} from "@angular/material/input";
import {MatOption} from "@angular/material/autocomplete";
import {MatSelect} from "@angular/material/select";
import {NgForOf, NgIf} from "@angular/common";
import {ReactiveFormsModule} from "@angular/forms";
import {AbstractInteraction} from "../../../project/game-model/interactions/AbstractInteraction";
import {GameModel} from "../../../project/game-model/GameModel";
import {InventoryCondition} from "../../../project/game-model/interactions/condition/InventoryCondition";
import {
InventoryItemgroupCondition
} from "../../../project/game-model/interactions/condition/InventoryItemgroupCondition";
import {CharacterDependency} from "../../../project/game-model/interactions/CharacterDependency";
import {InventoryItemCondition} from "../../../project/game-model/interactions/condition/InventoryItemCondition";
import {Item} from "../../../project/game-model/inventory/Item";
import {Interaction} from "../../../project/game-model/interactions/Interaction";
import {InventoryAction} from "../../../project/game-model/interactions/actions/InventoryAction";
import {InventoryItemGroupAction} from "../../../project/game-model/interactions/actions/InventoryItemgroupAction";
import {InventoryItemAction} from "../../../project/game-model/interactions/actions/InventoryItemAction";
@Component({
selector: 'app-item-action-editor',
templateUrl: './item-action-editor.component.html',
styleUrl: './item-action-editor.component.scss'
})
export class ItemActionEditorComponent {
@Input() interaction: Interaction | undefined
@Input() gameModel: GameModel | undefined
@Input() group: boolean = false;
actionDatasource: MatTableDataSource<InventoryAction> = new MatTableDataSource();
displayedColumns: string[] = ["item", "delta", "edit", "delete"];
editedAction: InventoryAction | undefined
addedAction: InventoryAction | undefined
ngOnInit() {
this.actionDatasource.data = this.interaction!.inventoryActions;
}
addAction() {
if(this.group) {
this.addedAction = new InventoryItemGroupAction(CharacterDependency.NONE, 0, undefined);
} else {
this.addedAction = new InventoryItemAction(CharacterDependency.NONE, 0, undefined);
}
this.editedAction = this.addedAction;
const displayedColumns = this.actionDatasource.data;
displayedColumns.push(this.addedAction);
this.actionDatasource.data = displayedColumns;
}
editAction(action: InventoryAction) {
this.editedAction = action;
}
finishEditing() {
if(this.editedAction == undefined) {
return;
}
if(this.editedAction.validate()) {
if(this.addedAction != undefined) {
this.interaction!.addAction(this.addedAction);
this.addedAction = undefined;
}
this.editedAction = undefined;
}
}
deleteAction(deletedAction: InventoryAction) {
this.interaction!.removeAction(deletedAction);
this.actionDatasource.data = this.actionDatasource.data.filter(action => action !== deletedAction);
}
actionAsItemAction(action: InventoryAction) {
if(action instanceof InventoryItemAction) {
return action;
} else {
return undefined;
}
}
actionAsItemgroupAction(action: InventoryAction) {
if(action instanceof InventoryItemGroupAction) {
return action;
} else {
return undefined;
}
}
onSelectedItemChange(action: InventoryItemAction, item: Item) {
(action as InventoryItemAction).item = item;
}
}

View File

@ -17,6 +17,7 @@ import {ConcreteItemGroup} from "./inventory/ConcreteItemGroup";
import {Item} from "./inventory/Item"; import {Item} from "./inventory/Item";
import {ItemgroupUtilities} from "./utils/ItemgroupUtilities"; import {ItemgroupUtilities} from "./utils/ItemgroupUtilities";
import {ItemGroupCharacteristic} from "./inventory/ItemgroupCharacteristic"; import {ItemGroupCharacteristic} from "./inventory/ItemgroupCharacteristic";
import {AbstractInteraction} from "./interactions/AbstractInteraction";
export class GameModel { export class GameModel {
gameModelName: string gameModelName: string
@ -25,6 +26,7 @@ export class GameModel {
scriptAccounts: ScriptAccount[] = []; scriptAccounts: ScriptAccount[] = [];
characters: Character[] = [] characters: Character[] = []
itemgroups: ItemGroup[] = [] itemgroups: ItemGroup[] = []
characterInteractions: AbstractInteraction[] = []
constructor(gameModelName: string) { constructor(gameModelName: string) {
this.gameModelName = gameModelName; this.gameModelName = gameModelName;
@ -233,6 +235,21 @@ export class GameModel {
return result; return result;
} }
get gamesystemsAsList() {
let gamesystemQueue: Gamesystem<any, any>[] = this.gamesystems.concat();
const result: Gamesystem<any, any>[] = []
while(gamesystemQueue.length > 0) {
const currentGamesystem = gamesystemQueue.shift()!;
if(currentGamesystem instanceof ProductGamesystem) {
gamesystemQueue = gamesystemQueue.concat(currentGamesystem.innerGamesystems);
}
result.push(currentGamesystem);
}
return result;
}
addCharacter(character: Character) { addCharacter(character: Character) {
if(this.characters.find(c => c.componentName === character.componentName) === undefined) { if(this.characters.find(c => c.componentName === character.componentName) === undefined) {
this.characters.push(character) this.characters.push(character)
@ -240,4 +257,18 @@ export class GameModel {
} }
return false; return false;
} }
getCharacterInteractionsByCharacter(character: Character) {
return this.characterInteractions.filter(interaction => interaction.targetCharacter === character || interaction.sourceCharacter === character);
}
addCharacterInteraction(characterInteraction: AbstractInteraction) {
if(this.characterInteractions.find(interaction => interaction.equals(characterInteraction)) == undefined) {
this.characterInteractions.push(characterInteraction);
}
}
removeCharacterInteraction(characterInteraction: AbstractInteraction) {
this.characterInteractions = this.characterInteractions.filter(interaction => interaction != characterInteraction);
}
} }

View File

@ -1,12 +1,14 @@
import {ScriptAccount} from "../../scriptAccounts/ScriptAccount"; import {ScriptAccount} from "../../scriptAccounts/ScriptAccount";
import {ScriptAccountCondition} from "../conditions/ScriptAccountCondition"; import {Action} from "../../interactions/actions/Action";
import {CharacterDependency} from "../../interactions/CharacterDependency";
export class ScriptAccountAction { export class ScriptAccountAction extends Action{
scriptAccount: ScriptAccount scriptAccount: ScriptAccount
changingValue: number = 0; changingValue: number = 0;
constructor(scriptAccount: ScriptAccount, changingValue: number) { constructor(scriptAccount: ScriptAccount, changingValue: number) {
super(CharacterDependency.NONE);
this.scriptAccount = scriptAccount; this.scriptAccount = scriptAccount;
this.changingValue = changingValue; this.changingValue = changingValue;
} }

View File

@ -1,11 +1,15 @@
import {ScriptAccount} from "../../scriptAccounts/ScriptAccount"; import {ScriptAccount} from "../../scriptAccounts/ScriptAccount";
export class ScriptAccountCondition { import {Condition} from "../../interactions/condition/Condition";
import {CharacterDependency} from "../../interactions/CharacterDependency";
export class ScriptAccountCondition extends Condition{
scriptAccount: ScriptAccount scriptAccount: ScriptAccount
minValue: number minValue: number
maxValue: number maxValue: number
private constructor(scriptAccount: ScriptAccount, minValue: number, maxValue: number) { private constructor(scriptAccount: ScriptAccount, minValue: number, maxValue: number) {
super(CharacterDependency.NONE);
this.scriptAccount = scriptAccount; this.scriptAccount = scriptAccount;
this.minValue = minValue; this.minValue = minValue;
this.maxValue = maxValue; this.maxValue = maxValue;

View File

@ -2,6 +2,7 @@ import {ProductTransition} from "../transitions/ProductTransition";
import {State} from "./State"; import {State} from "./State";
import {SimpleState} from "./SimpleState"; import {SimpleState} from "./SimpleState";
import {Transition} from "../transitions/Transition"; import {Transition} from "../transitions/Transition";
import {state} from "@angular/animations";
export class ProductState extends State<ProductTransition> { export class ProductState extends State<ProductTransition> {
innerStates: State<any>[] = []; innerStates: State<any>[] = [];
@ -50,4 +51,17 @@ export class ProductState extends State<ProductTransition> {
return false; return false;
} }
computeStateLabel(): string {
let stateLabel = "("
for(let i=0; i<this.innerStates.length; i++) {
stateLabel += this.innerStates[i].computeStateLabel();
if(i+1 < this.innerStates.length) {
stateLabel += ", ";
}
}
return stateLabel += ")";
}
} }

View File

@ -20,5 +20,10 @@ export class SimpleState extends State<SimpleTransition> {
return this.stateLabel === (state as SimpleState).stateLabel; return this.stateLabel === (state as SimpleState).stateLabel;
} }
computeStateLabel(): string {
return this.stateLabel
}
} }

View File

@ -47,4 +47,6 @@ export abstract class State<T extends Transition<any>> {
} }
abstract equals(state: State<Transition<any>>): boolean; abstract equals(state: State<Transition<any>>): boolean;
abstract computeStateLabel(): string
} }

View File

@ -0,0 +1,46 @@
import {Character} from "../characters/Character";
import {Condition} from "./condition/Condition";
import {ScriptAccountCondition} from "../gamesystems/conditions/ScriptAccountCondition";
import {InventoryCondition} from "./condition/InventoryCondition";
import {GamesystemCondition} from "./condition/GamesystemCondition";
import {Action} from "./actions/Action";
export abstract class AbstractInteraction {
sourceCharacter: Character
targetCharacter: Character | undefined
conditions: Condition[] = []
interactionLabel: string
protected constructor(sourceCharacter: Character, targetCharacter: Character | undefined, interactionLabel: string, conditions: Condition[] = []) {
this.sourceCharacter = sourceCharacter;
this.targetCharacter = targetCharacter;
this.interactionLabel = interactionLabel;
}
abstract equals(other: AbstractInteraction): boolean
abstract validate(requieredCharacter: Character): boolean
get scriptAccountConditions() {
return this.conditions.filter(condition => condition instanceof ScriptAccountCondition);
}
get inventoryConditions() {
return this.conditions.filter(condition => condition instanceof InventoryCondition).map(condition => condition as InventoryCondition)
}
get gamesystemConditions(): GamesystemCondition[] {
return this.conditions.filter(condition => condition instanceof GamesystemCondition).map(condition => condition as GamesystemCondition)
}
addConditon(addedCondition: Condition) {
this.conditions.push(addedCondition);
}
removeCondition(removedCondition: Condition) {
this.conditions = this.conditions.filter(condition => condition !== removedCondition);
}
}

View File

@ -0,0 +1,5 @@
export enum CharacterDependency {
SRC,
TARGET,
NONE
}

View File

@ -0,0 +1,56 @@
import {Character} from "../characters/Character";
import {Condition} from "./condition/Condition";
import {Action} from "./actions/Action";
import {AbstractInteraction} from "./AbstractInteraction";
import {ScriptAccountCondition} from "../gamesystems/conditions/ScriptAccountCondition";
import {ScriptAccountAction} from "../gamesystems/actions/ScriptAccountAction";
import {InventoryCondition} from "./condition/InventoryCondition";
import {InventoryItemAction} from "./actions/InventoryItemAction";
export class Interaction extends AbstractInteraction{
actions: Action[] = []
constructor(sourceCharacter: Character, targetCharacter: Character | undefined, interactionLabel: string) {
super(sourceCharacter, targetCharacter, interactionLabel);
}
equals(other: AbstractInteraction): boolean {
if(!(other instanceof Interaction)) {
return false;
}
const equealCharacters = this.sourceCharacter === other.sourceCharacter && this.targetCharacter === other.targetCharacter;
const equalLabels = this.interactionLabel === other.interactionLabel;
const equalConditions = this.conditions.every(condition => other.conditions.includes(condition)) && other.conditions.every(condition => this.conditions.includes(condition));
const equalActions = this.actions.every(action => other.actions.includes(action) && other.actions.every(action => this.actions.includes(action)));
return equealCharacters && equalLabels && equalConditions && equalActions;
}
validate(requieredCharacter: Character): boolean {
const validCharacters = this.sourceCharacter == requieredCharacter || this.targetCharacter == requieredCharacter;
const validLabel = this.interactionLabel.length > 0;
//Todo: Check for contradicting conditions as well as double conditions and actions
return validCharacters && validLabel;
}
get scriptAccountActions() : ScriptAccountAction[]{
return this.actions.filter(condition => condition instanceof ScriptAccountAction).map(condition => condition as ScriptAccountAction)
}
get inventoryActions(): InventoryItemAction[] {
return this.actions.filter(action => action instanceof InventoryItemAction).map(action => action as InventoryItemAction);
}
addAction(action: Action) {
this.actions.push(action);
}
removeAction(deletedAction: Action) {
this.actions = this.actions.filter(action => action !== deletedAction);
}
}

View File

@ -0,0 +1,96 @@
import {Interaction} from "./Interaction";
import {AbstractInteraction} from "./AbstractInteraction";
import {Character} from "../characters/Character";
export class InteractionSequences extends AbstractInteraction {
rootInteraction: InteractionSequenceNode
constructor(interaction: Interaction, interactionLabel: string) {
super(interaction.sourceCharacter, interaction.targetCharacter, interactionLabel, interaction.conditions);
this.rootInteraction = new InteractionSequenceNode(interaction, []);
}
equals(other: AbstractInteraction): boolean {
if(!(other instanceof InteractionSequences)) {
return false;
}
const equealCharacters = this.sourceCharacter === other.sourceCharacter && this.targetCharacter === other.targetCharacter;
const equalLabels = this.interactionLabel === other.interactionLabel;
const equalConditions = this.conditions.every(condition => other.conditions.includes(condition)) && other.conditions.every(condition => this.conditions.includes(condition));
const equalSequenceTree = this.rootInteraction.equals(other.rootInteraction);
return equealCharacters && equalLabels && equalConditions && equalSequenceTree;
}
validate(requieredCharacter: Character): boolean {
const validCharacters = this.sourceCharacter == requieredCharacter || this.targetCharacter == requieredCharacter;
const validLabel = this.interactionLabel.length > 0;
const validSequenceTree = this.rootInteraction.validate(requieredCharacter)
return validCharacters && validLabel && validSequenceTree;
}
findInteraction(currentInteraction: Interaction) {
if(this.rootInteraction.root === currentInteraction) {
return this.rootInteraction;
} else {
let interactionQueue: InteractionSequenceNode[] = this.rootInteraction.children.concat();
while(interactionQueue.length > 0) {
const currentInteractionNode = interactionQueue.shift()!;
if(currentInteractionNode.root == currentInteraction) {
return currentInteractionNode;
}
interactionQueue = interactionQueue.concat(currentInteractionNode.children);
}
return undefined;
}
}
}
class InteractionSequenceNode {
root: Interaction
children: InteractionSequenceNode[] = [];
equals(other: InteractionSequenceNode) {
const equalsRoot = this.root.equals(other.root);
const equalsChildren = this.children.every(child => other.children.includes(child) && other.children.every(child => this.children.includes(child)));
return equalsRoot && equalsChildren;
}
validate(requiredCharacter: Character): boolean {
const validateRoot = this.root.validate(requiredCharacter);
let validateChildren = true;
for(let i=0; i<this.children.length; i++) {
if(!this.children[i].validate(requiredCharacter)) {
validateChildren = false;
break;
}
}
return validateRoot && validateChildren;
}
constructor(root: Interaction, children: InteractionSequenceNode[]) {
this.root = root;
this.children = children;
}
removeChildInteraction(interaction: Interaction) {
this.children = this.children.filter(child => child.root !== interaction);
}
addInteraction(interaction: Interaction) {
this.children.push(new InteractionSequenceNode(interaction, []));
}
}

View File

@ -0,0 +1,11 @@
import {CharacterDependency} from "../CharacterDependency";
export abstract class Action {
characterDependency: CharacterDependency
constructor(characterDependency: CharacterDependency) {
this.characterDependency = characterDependency;
}
}

View File

@ -0,0 +1,13 @@
import {Action} from "./Action";
import {CharacterDependency} from "../CharacterDependency";
export abstract class InventoryAction extends Action {
valueChange: number;
protected constructor(characterDependency: CharacterDependency, valueChange: number) {
super(characterDependency);
this.valueChange = valueChange;
}
public abstract validate(): boolean;
}

View File

@ -0,0 +1,20 @@
import {Action} from "./Action";
import {Item} from "../../inventory/Item";
import {CharacterDependency} from "../CharacterDependency";
import {InventoryAction} from "./InventoryAction";
export class InventoryItemAction extends InventoryAction {
item: Item | undefined
constructor(characterDependency: CharacterDependency, valueChange: number, item: Item | undefined) {
super(characterDependency, valueChange);
this.item = item;
}
validate(): boolean {
return this.item != undefined;
}
}

View File

@ -0,0 +1,19 @@
import {Action} from "./Action";
import {ItemGroup} from "../../inventory/ItemGroup";
import {CharacterDependency} from "../CharacterDependency";
import {InventoryAction} from "./InventoryAction";
export class InventoryItemGroupAction extends InventoryAction{
itemgroup: ItemGroup | undefined
constructor(characterDependency: CharacterDependency, valueChange: number, itemgroup: ItemGroup | undefined) {
super(characterDependency, valueChange);
this.itemgroup = itemgroup;
}
validate(): boolean {
return this.itemgroup != undefined;
}
}

View File

@ -0,0 +1,10 @@
import {CharacterDependency} from "../CharacterDependency";
export abstract class Condition {
characterDependency: CharacterDependency
constructor(characterDependency: CharacterDependency) {
this.characterDependency = characterDependency;
}
}

View File

@ -0,0 +1,20 @@
import {Gamesystem} from "../../gamesystems/Gamesystem";
import {State} from "../../gamesystems/states/State";
import {Condition} from "./Condition";
import {CharacterDependency} from "../CharacterDependency";
export class GamesystemCondition extends Condition {
targetGamesystem: Gamesystem<any, any> | undefined
requieredState: State<any> | undefined;
constructor(characterDependency: CharacterDependency, targetGamesystem: Gamesystem<any, any> | undefined, requieredState: State<any> | undefined) {
super(characterDependency);
this.targetGamesystem = targetGamesystem;
this.requieredState = requieredState;
}
validate(): boolean {
return this.targetGamesystem != undefined && this.requieredState != undefined;
}
}

View File

@ -0,0 +1,17 @@
import {Condition} from "./Condition";
import {Character} from "../../characters/Character";
import {CharacterDependency} from "../CharacterDependency";
export abstract class InventoryCondition extends Condition {
minValue: number
maxValue: number
protected constructor(characterDependency: CharacterDependency, minValue: number, maxValue: number) {
super(characterDependency);
this.minValue = minValue;
this.maxValue = maxValue;
}
abstract validate(): boolean;
}

View File

@ -0,0 +1,17 @@
import {InventoryCondition} from "./InventoryCondition";
import {Item} from "../../inventory/Item";
import {CharacterDependency} from "../CharacterDependency";
export class InventoryItemCondition extends InventoryCondition {
item: Item | undefined
constructor(characterDependency: CharacterDependency, minValue: number, maxValue: number, item: Item | undefined) {
super(characterDependency, minValue, maxValue);
this.item = item;
}
validate() {
return this.item != undefined;
}
}

View File

@ -0,0 +1,19 @@
import {InventoryCondition} from "./InventoryCondition";
import {ItemGroup} from "../../inventory/ItemGroup";
import {CharacterDependency} from "../CharacterDependency";
export class InventoryItemgroupCondition extends InventoryCondition {
itemgroup: ItemGroup | undefined
constructor(characterDependency: CharacterDependency, minValue: number, maxValue: number, itemgroup: ItemGroup | undefined) {
super(characterDependency, minValue, maxValue);
this.itemgroup = itemgroup;
}
validate(): boolean {
return this.itemgroup != undefined;
}
}

View File

@ -1,6 +1,7 @@
import {ItemGroup} from "./ItemGroup"; import {ItemGroup} from "./ItemGroup";
import {ItemGroupCharacteristic} from "./ItemgroupCharacteristic"; import {ItemGroupCharacteristic} from "./ItemgroupCharacteristic";
import {ItemgroupType} from "./ItemgroupType"; import {ItemgroupType} from "./ItemgroupType";
import {Item} from "./Item";
export class AbstractItemGroup extends ItemGroup { export class AbstractItemGroup extends ItemGroup {
@ -16,5 +17,11 @@ export class AbstractItemGroup extends ItemGroup {
//Do Nothing //Do Nothing
} }
getInheritedItems(): Item[] {
let items: Item[] = [];
this.children.forEach(child => items = items.concat(child.getInheritedItems()))
return items;
}
} }

View File

@ -27,5 +27,9 @@ export class ConcreteItemGroup extends ItemGroup {
}) })
} }
getInheritedItems(): Item[] {
return this.items;
}
} }

View File

@ -30,6 +30,5 @@ export abstract class ItemGroup extends ModelComponent {
} }
} }
abstract getInheritedItems(): Item[]
} }

View File

@ -21,3 +21,7 @@ body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
.icon-btn-primary { .icon-btn-primary {
color: #3c95f8; color: #3c95f8;
} }
.long-form {
width: 100%;
}

View File

@ -7,8 +7,27 @@
"conditions": [], "conditions": [],
"stateLabel": "A", "stateLabel": "A",
"stateDescription": "" "stateDescription": ""
},
{
"initial": false,
"conditions": [],
"stateLabel": "B",
"stateDescription": ""
}
],
"transitions": [
{
"scriptAccountActions": [
{
"characterDependency": 2,
"changingValue": 6,
"scriptAccount": "Test"
}
],
"scriptAccountConditions": [],
"startingState": "A",
"endingState": "B"
} }
], ],
"transitions": [],
"generateIsolatedStates": false "generateIsolatedStates": false
} }

View File

@ -7,8 +7,27 @@
"conditions": [], "conditions": [],
"stateLabel": "1", "stateLabel": "1",
"stateDescription": "" "stateDescription": ""
},
{
"initial": false,
"conditions": [],
"stateLabel": "2",
"stateDescription": ""
}
],
"transitions": [
{
"scriptAccountActions": [
{
"characterDependency": 2,
"changingValue": 5,
"scriptAccount": "Test"
}
],
"scriptAccountConditions": [],
"startingState": "1",
"endingState": "2"
} }
], ],
"transitions": [],
"generateIsolatedStates": false "generateIsolatedStates": false
} }