Commit d7570dc3 authored by Romain DELEAU's avatar Romain DELEAU

add tutorial

parent 6cbcc4d3
<div class="container"> <div class="container">
<div class="container-appDragScroll" appDragScroll appMouseWheelZoom> <div class="container-appDragScroll" appDragScroll appMouseWheelZoom [scenario]="scenario">
<div class="container-appMouseWheelZoom"> <div class="container-appMouseWheelZoom">
<div class="container-scenario-main"> <div class="container-scenario-main">
...@@ -172,4 +172,7 @@ ...@@ -172,4 +172,7 @@
[matTooltip]="translate.instant('zoomOut_tooltip')" [matTooltip]="translate.instant('zoomOut_tooltip')"
matTooltipPosition="before" [matTooltipDisabled]="!tooltipService.activatedTooltips"><mat-icon fontIcon="zoom_out"></mat-icon></button> matTooltipPosition="before" [matTooltipDisabled]="!tooltipService.activatedTooltips"><mat-icon fontIcon="zoom_out"></mat-icon></button>
</div> </div>
<button class="container-tutorial-resume" mat-raised-button (click)="tutorialService.isActive = !tutorialService.isActive; resumeTutorialTrace()" *ngIf="!tutorialService.isActive"><mat-icon fontIcon="keyboard_return"></mat-icon> {{'tutorial_resume' | translate}}</button>
<app-tutorial style="z-index: 10; position: absolute;" *ngIf="tutorialService.isActive" cdkDrag cdkDragBoundary=".container"></app-tutorial>
</div> </div>
\ No newline at end of file
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
flex-direction: row; flex-direction: row;
z-index: 1; z-index: 1;
width: 100%;
height: 100%;
&-appDragScroll { &-appDragScroll {
position: absolute; position: absolute;
height: 100%; height: 100%;
...@@ -140,7 +143,7 @@ ...@@ -140,7 +143,7 @@
position: fixed; position: fixed;
right: 0; right: 0;
top: 0; top: 0;
z-index: 2; z-index: 5;
box-shadow: -6px 0px 16px rgba(0, 0, 0, 0.3); box-shadow: -6px 0px 16px rgba(0, 0, 0, 0.3);
&-title { &-title {
...@@ -150,7 +153,7 @@ ...@@ -150,7 +153,7 @@
width: 340px; width: 340px;
background-color: #e3e3e3; background-color: #e3e3e3;
position: relative; position: relative;
z-index: 2; z-index: 5;
box-shadow: 0px 4px 15px rgba(0, 0, 0, 0.3); box-shadow: 0px 4px 15px rgba(0, 0, 0, 0.3);
font-family: 'Glacial Indifference Bold', sans-serif; font-family: 'Glacial Indifference Bold', sans-serif;
display: flex; display: flex;
...@@ -180,7 +183,7 @@ ...@@ -180,7 +183,7 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
z-index: 2; z-index: 5;
&-mode { &-mode {
width: 120px; width: 120px;
...@@ -211,7 +214,7 @@ ...@@ -211,7 +214,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
z-index: 2; z-index: 5;
button { button {
width: 50px; width: 50px;
...@@ -227,7 +230,7 @@ ...@@ -227,7 +230,7 @@
position: fixed; position: fixed;
height: 200px; height: 200px;
width: 200px; width: 200px;
z-index: 2; z-index: 5;
background-color: white; background-color: white;
box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12); box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12);
padding: 5px; padding: 5px;
...@@ -236,6 +239,16 @@ ...@@ -236,6 +239,16 @@
right: 360px; right: 360px;
overflow: scroll; overflow: scroll;
} }
&-tutorial {
&-resume {
border-radius: 10px;
position: fixed;
top: 10px;
left: 10px;
z-index: 5;
}
}
} }
::ng-deep .mat-tooltip { ::ng-deep .mat-tooltip {
...@@ -248,6 +261,11 @@ ...@@ -248,6 +261,11 @@
font-size: 16px; font-size: 16px;
} }
::ng-deep .mat-raised-button {
font-family: 'Glacial Indifference', sans-serif;
font-size: 16px;
}
::ng-deep .mat-menu-content { ::ng-deep .mat-menu-content {
padding: 5px; padding: 5px;
......
...@@ -36,6 +36,7 @@ import { Trace } from './class/trace/trace'; ...@@ -36,6 +36,7 @@ import { Trace } from './class/trace/trace';
import Minimap from 'js-minimap'; import Minimap from 'js-minimap';
import { MinimapService } from './services/minimap/minimap.service'; import { MinimapService } from './services/minimap/minimap.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { TutorialService } from './services/tutorial/tutorial.service';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
...@@ -52,7 +53,7 @@ export class AppComponent { ...@@ -52,7 +53,7 @@ export class AppComponent {
constructor(private cdr: ChangeDetectorRef, private http: HttpClient, protected pieceDetailsService: PieceDetailsService, protected tooltipService: TooltipService, constructor(private cdr: ChangeDetectorRef, private http: HttpClient, protected pieceDetailsService: PieceDetailsService, protected tooltipService: TooltipService,
private elementRef: ElementRef, private zoomService: ZoomService, private dialog: MatDialog, private titleService: Title, private elementRef: ElementRef, private zoomService: ZoomService, private dialog: MatDialog, private titleService: Title,
private _snackBar: MatSnackBar, protected minimapService: MinimapService, protected translate: TranslateService) { private _snackBar: MatSnackBar, protected minimapService: MinimapService, protected translate: TranslateService, protected tutorialService: TutorialService) {
translate.setTranslation('en', require('../assets/lang/en.json')); translate.setTranslation('en', require('../assets/lang/en.json'));
translate.setTranslation('fr', require('../assets/lang/fr.json')); translate.setTranslation('fr', require('../assets/lang/fr.json'));
...@@ -79,21 +80,21 @@ export class AppComponent { ...@@ -79,21 +80,21 @@ export class AppComponent {
ngOnInit(): void { ngOnInit(): void {
const container = this.elementRef.nativeElement.querySelector('.container-appDragScroll'); const container = this.elementRef.nativeElement.querySelector('.container-appDragScroll');
const target = this.elementRef.nativeElement.querySelector('.container-minimap'); const target = this.elementRef.nativeElement.querySelector('.container-minimap');
container.scrollTo(0,800); container.scrollTo(0,500);
this.minimapService.minimap = new Minimap({ this.minimapService.minimap = new Minimap({
container, container,
target, target,
observe: false observe: false
}) })
} }
/*
@HostListener('window:beforeunload', ['$event']) @HostListener('window:beforeunload', ['$event'])
beforeUnloadHandler(event: any) { beforeUnloadHandler(event: any) {
const message = "Êtes vous sûr de vouloir quitter RLG Maker ?\nVous risquez de perdre les données non sauvegardées."; const message = "Êtes vous sûr de vouloir quitter RLG Maker ?\nVous risquez de perdre les données non sauvegardées.";
event.returnValue = message; event.returnValue = message;
return message; return message;
} }
*/
@HostListener('document:keydown', ['$event']) @HostListener('document:keydown', ['$event'])
onKeyDown(event: KeyboardEvent) { onKeyDown(event: KeyboardEvent) {
if (event.ctrlKey && event.key === 's') { if (event.ctrlKey && event.key === 's') {
...@@ -162,6 +163,10 @@ export class AppComponent { ...@@ -162,6 +163,10 @@ export class AppComponent {
this.titleService.setTitle('RLG Maker - '+this.scenario.projectName); this.titleService.setTitle('RLG Maker - '+this.scenario.projectName);
} }
this.scenario.tooltips = this.tooltipService.activatedTooltips; this.scenario.tooltips = this.tooltipService.activatedTooltips;
this.scenario.tutorial_isActive = this.tutorialService.isActive;
this.scenario.tutorial_phase = this.tutorialService.phase;
this.scenario.tutorial_optionnalPhase = this.tutorialService.optionnalPhase;
this.scenario.tutorial_phaseDone = this.tutorialService.phaseDone;
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'save', undefined, undefined, 'all', 'Scenario')); this.scenario.traces.push(new Trace(this.scenario.traces.length, 'save', undefined, undefined, 'all', 'Scenario'));
const jsonString = JSON.stringify(this.scenario,undefined,2); const jsonString = JSON.stringify(this.scenario,undefined,2);
const blob = new Blob([jsonString], { type: 'application/json' }); const blob = new Blob([jsonString], { type: 'application/json' });
...@@ -192,6 +197,10 @@ export class AppComponent { ...@@ -192,6 +197,10 @@ export class AppComponent {
const jsonData: any = JSON.parse(fileContent); const jsonData: any = JSON.parse(fileContent);
const scenario: Scenario = Object.assign(new Scenario(), jsonData); const scenario: Scenario = Object.assign(new Scenario(), jsonData);
this.tooltipService.activatedTooltips = scenario.tooltips; this.tooltipService.activatedTooltips = scenario.tooltips;
this.tutorialService.isActive = scenario.tutorial_isActive;
this.tutorialService.phase = scenario.tutorial_phase;
this.tutorialService.optionnalPhase = scenario.tutorial_optionnalPhase;
this.tutorialService.phaseDone = scenario.tutorial_phaseDone;
scenario.context = Object.assign(new GameContext(), jsonData.context); scenario.context = Object.assign(new GameContext(), jsonData.context);
scenario.context.comments = jsonData.context.comments.map((commentData: any) => Object.assign(new Comment(), commentData)); scenario.context.comments = jsonData.context.comments.map((commentData: any) => Object.assign(new Comment(), commentData));
scenario.educationnalObjective = Object.assign(new GameEducationnalObjective(), jsonData.educationnalObjective); scenario.educationnalObjective = Object.assign(new GameEducationnalObjective(), jsonData.educationnalObjective);
...@@ -334,6 +343,10 @@ export class AppComponent { ...@@ -334,6 +343,10 @@ export class AppComponent {
this.zoomService.zoom += 0.1; this.zoomService.zoom += 0.1;
element.style.transform = `scale(${this.zoomService.zoom})`; element.style.transform = `scale(${this.zoomService.zoom})`;
this.minimapService.reset(); this.minimapService.reset();
if (!this.tutorialService.optionnalPhase && !this.tutorialService.phaseDone[this.tutorialService.phase-1] && this.tutorialService.isActive && this.tutorialService.phase == 2) {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'valid_phase', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
this.tutorialService.validPhase();
}
} }
} }
...@@ -343,6 +356,10 @@ export class AppComponent { ...@@ -343,6 +356,10 @@ export class AppComponent {
this.zoomService.zoom -= 0.1; this.zoomService.zoom -= 0.1;
element.style.transform = `scale(${this.zoomService.zoom})`; element.style.transform = `scale(${this.zoomService.zoom})`;
this.minimapService.reset(); this.minimapService.reset();
if (!this.tutorialService.optionnalPhase && !this.tutorialService.phaseDone[this.tutorialService.phase-1] && this.tutorialService.isActive && this.tutorialService.phase == 2) {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'valid_phase', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
this.tutorialService.validPhase();
}
} }
} }
...@@ -415,4 +432,8 @@ export class AppComponent { ...@@ -415,4 +432,8 @@ export class AppComponent {
this.scenario.traces.push(new Trace(this.scenario.traces.length,'disable_tooltips',undefined, undefined,'tooltips','Scenario')); this.scenario.traces.push(new Trace(this.scenario.traces.length,'disable_tooltips',undefined, undefined,'tooltips','Scenario'));
} }
} }
resumeTutorialTrace() {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'resume_tutorial', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
}
} }
\ No newline at end of file
...@@ -8,7 +8,8 @@ import { MatButtonModule } from '@angular/material/button'; ...@@ -8,7 +8,8 @@ import { MatButtonModule } from '@angular/material/button';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog';
import {MatSnackBarModule} from '@angular/material/snack-bar'; import { MatSnackBarModule } from '@angular/material/snack-bar';
import { DragDropModule } from '@angular/cdk/drag-drop';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { EducationalObjectiveComponent } from './pieces/educational-objective/educational-objective.component'; import { EducationalObjectiveComponent } from './pieces/educational-objective/educational-objective.component';
...@@ -45,6 +46,8 @@ import { LoadingfailSnackbarComponent } from './components/snackbars/loadingfail ...@@ -45,6 +46,8 @@ import { LoadingfailSnackbarComponent } from './components/snackbars/loadingfail
import { HttpClient, HttpClientModule } from '@angular/common/http'; import { HttpClient, HttpClientModule } from '@angular/common/http';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { TutorialComponent } from './components/tutorial/tutorial.component';
import { FinishTutorialComponent } from './components/snackbars/finish-tutorial/finish-tutorial.component';
export function HttpLoaderFactory(http: HttpClient) { export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http); return new TranslateHttpLoader(http);
...@@ -82,7 +85,9 @@ export function HttpLoaderFactory(http: HttpClient) { ...@@ -82,7 +85,9 @@ export function HttpLoaderFactory(http: HttpClient) {
SaveDialogComponent, SaveDialogComponent,
IdentifierSnackbarComponent, IdentifierSnackbarComponent,
LoadingsucessSnackbarComponent, LoadingsucessSnackbarComponent,
LoadingfailSnackbarComponent LoadingfailSnackbarComponent,
TutorialComponent,
FinishTutorialComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
...@@ -102,7 +107,8 @@ export function HttpLoaderFactory(http: HttpClient) { ...@@ -102,7 +107,8 @@ export function HttpLoaderFactory(http: HttpClient) {
useFactory: HttpLoaderFactory, useFactory: HttpLoaderFactory,
deps: [HttpClient] deps: [HttpClient]
} }
}) }),
DragDropModule
], ],
providers: [], providers: [],
bootstrap: [AppComponent] bootstrap: [AppComponent]
......
...@@ -17,5 +17,9 @@ export class Scenario { ...@@ -17,5 +17,9 @@ export class Scenario {
comments: Comment[] = []; comments: Comment[] = [];
projectName: string = ''; projectName: string = '';
tooltips: boolean = true; tooltips: boolean = true;
tutorial_isActive: boolean = true;
tutorial_phase: number = 1;
tutorial_optionnalPhase: string = '';
tutorial_phaseDone: boolean[] = [];
traces: Trace[] = [new Trace(0, 'new', undefined, undefined, 'all', 'Scenario')]; traces: Trace[] = [new Trace(0, 'new', undefined, undefined, 'all', 'Scenario')];
} }
\ No newline at end of file
<span matSnackBarLabel>{{'tutorial_finish' | translate}}</span>
\ No newline at end of file
::ng-deep .mat-snack-bar-container {
background-color: #1ba345;
color: white;
box-shadow: 0px 0px 15px 5px #1ba345;
text-align: center;
}
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FinishTutorialComponent } from './finish-tutorial.component';
describe('FinishTutorialComponent', () => {
let component: FinishTutorialComponent;
let fixture: ComponentFixture<FinishTutorialComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ FinishTutorialComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(FinishTutorialComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-finish-tutorial',
templateUrl: './finish-tutorial.component.html',
styleUrls: ['./finish-tutorial.component.scss']
})
export class FinishTutorialComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
<div class="piece">
<div class="piece-top">
<button mat-button (click)="tutorialService.isActive = !tutorialService.isActive; closeTutorialTrace();"><mat-icon fontIcon="expand_less"></mat-icon> {{'tutorial_leave' | translate}}</button>
<div class="piece-top-title">{{'tutorial_title' | translate}}</div>
<div class="piece-top-phases" *ngIf="!tutorialService.optionnalPhase">
<button mat-button (click)="onClickPrevious()" [disabled]="tutorialService.phase == 1">{{'tutorial_previous' | translate}}</button>
<div class="piece-top-phases-phase">{{'tutorial_phase' | translate}} {{tutorialService.phase}}/9</div>
<button mat-button (click)="onClickNext()" [disabled]="tutorialService.phase == 9">{{'tutorial_next' | translate}}</button>
</div>
<div class="piece-top-phases" *ngIf="tutorialService.optionnalPhase">
<button mat-button (click)="tutorialService.optionnalPhase = ''; resumeClassicTrace();">{{'tutorial_back' | translate}}</button>
</div>
</div>
<div class="piece-tutorial valid" [ngClass]="[tutorialService.validationFeedback, tutorialService.phaseDone[tutorialService.phase-1] && !tutorialService.optionnalPhase ? 'isValid' : '']">
<div *ngIf="!tutorialService.optionnalPhase">
<p class="piece-tutorial-title">{{'tutorial_phase_'+tutorialService.phase+'_title' | translate}}</p>
<p>{{'tutorial_phase_'+tutorialService.phase | translate}}</p>
</div>
<div *ngIf="tutorialService.optionnalPhase">
<p class="piece-tutorial-title">{{'tutorial_optionnal_'+tutorialService.optionnalPhase | translate}}</p>
<p>{{'tutorial_optionnalPhase_'+tutorialService.optionnalPhase | translate}}</p>
</div>
</div>
<div class="piece-optionnal">
<button mat-button (click)="onClickOptionnal()">{{'tutorial_optionnal' | translate}} <mat-icon fontIcon="expand_more" *ngIf="optionnalExpanded == 'hide'"></mat-icon><mat-icon fontIcon="expand_less" *ngIf="optionnalExpanded == 'show'"></mat-icon></button>
<div class="piece-optionnal-options" [class]="optionnalExpanded">
<div class="piece-optionnal-options-line">
<button mat-button [style.background-color]="'#f3ed97'" (click)="setOptionnalPhase('comments')">{{'tutorial_optionnal_comments' | translate}}</button>
<button mat-button [style.background-color]="'#c0d5fc'" (click)="setOptionnalPhase('step')">{{'tutorial_optionnal_step' | translate}}</button>
<button mat-button [style.background-color]="'#bfdaa3'" (click)="setOptionnalPhase('event')">{{'tutorial_optionnal_event' | translate}}</button>
</div>
<div class="piece-optionnal-options-line">
<button mat-button [style.background-color]="'#f7c9cf'" (click)="setOptionnalPhase('interrupt')">{{'tutorial_optionnal_interrupt' | translate}}</button>
<button mat-button [style.background-color]="'#e5c5ac'" (click)="setOptionnalPhase('occurence')">{{'tutorial_optionnal_occurence' | translate}}</button>
</div>
<div class="piece-optionnal-options-line">
<button mat-button [style.background-color]="'#ce7b66'" (click)="setOptionnalPhase('characters')">{{'tutorial_optionnal_characters' | translate}}</button>
<button mat-button [style.background-color]="'#c6c2bd'" (click)="setOptionnalPhase('rules')">{{'tutorial_optionnal_rules' | translate}}</button>
<button mat-button [style.background-color]="'#abbcc6'" (click)="setOptionnalPhase('repeat')">{{'tutorial_optionnal_repeat' | translate}}</button>
</div>
<div class="piece-optionnal-options-line">
<button mat-button [style.background-color]="'#f7f7f7'" (click)="setOptionnalPhase('resource')">{{'tutorial_optionnal_resource' | translate}}</button>
<button mat-button [style.background-color]="'#9ad5eb'" (click)="setOptionnalPhase('supplementaryRole')">{{'tutorial_optionnal_supplementaryRole' | translate}}</button>
</div>
<div class="piece-optionnal-options-line">
<button mat-button [style.background-color]="'#b28386'" (click)="setOptionnalPhase('finalTask')">{{'tutorial_optionnal_finalTask' | translate}}</button>
<button mat-button [style.background-color]="'#e8e3b3'" (click)="setOptionnalPhase('optionnalTask')">{{'tutorial_optionnal_optionnalTask' | translate}}</button>
<button mat-button [style.background-color]="'#a6c9ec'" (click)="setOptionnalPhase('repeatTask')">{{'tutorial_optionnal_repeatTask' | translate}}</button>
</div>
</div>
</div>
</div>
\ No newline at end of file
.piece {
width: 500px;
height: auto;
background-color: #d6d6d6;
border-radius: 10px;
box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12);
transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1);
padding: 5px;
cursor: grab;
button {
background-color: white;
border-radius: 10px;
}
&-top {
display: flex;
flex-direction: row;
width: 100%;
justify-content: space-between;
&-title {
font-size: 25px;
font-family: "Glacial Indifference Bold";
}
&-phases {
display: flex;
flex-direction: row;
justify-content: space-between;
position: relative;
&-phase {
font-size: 16px;
margin-top: 8px;
margin-left: 5px;
margin-right: 5px;
}
}
}
&-tutorial {
width: 98%;
margin-top: 20px;
border-radius: 10px;
padding: 5px;
overflow-y: auto;
&-title {
font-family: "Glacial Indifference Bold";
}
}
&-optionnal {
margin-top: 10px;
display: flex;
flex-direction: column;
justify-content: center;
button {
width: 100%;
}
&-options {
transition: max-height 0.5s linear;
overflow: hidden;
button {
width: auto;
}
&-line {
display: flex;
flex-direction: row;
margin-top: 5px;
justify-content: space-evenly;
}
}
&-options.show {
max-height: 500px;
}
&-options.hide {
max-height: 0;
}
}
}
.piece:active {
box-shadow: 0px 3px 6px 5px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12);
cursor: grabbing;
}
.valid {
transition: background-color 1s linear;
background-color: white;
}
.valid.validation {
background-color: lightgreen;
}
.isValid {
background: no-repeat center url(../../../assets/background-images/tuto.png) white;
background-size: 20%;
}
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TutorialComponent } from './tutorial.component';
describe('TutorialComponent', () => {
let component: TutorialComponent;
let fixture: ComponentFixture<TutorialComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ TutorialComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(TutorialComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, Input } from '@angular/core';
import { Scenario } from 'src/app/class/scenario/scenario';
import { Trace } from 'src/app/class/trace/trace';
import { TutorialService } from 'src/app/services/tutorial/tutorial.service';
@Component({
selector: 'app-tutorial',
templateUrl: './tutorial.component.html',
styleUrls: ['./tutorial.component.scss']
})
export class TutorialComponent implements OnInit {
@Input() scenario: Scenario = new Scenario();
optionnalExpanded: string = 'hide';
constructor(protected tutorialService: TutorialService) { }
ngOnInit(): void {
}
onClickOptionnal(): void {
if (this.optionnalExpanded == 'hide') {
this.optionnalExpanded = 'show';
} else {
this.optionnalExpanded = 'hide';
}
}
onClickNext(): void {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'next', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
if (this.tutorialService.phase < 9) {
this.tutorialService.phase++;
}
this.tutorialService.optionnalPhase = '';
}
onClickPrevious(): void {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'previous', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
if (this.tutorialService.phase > 1) {
this.tutorialService.phase--;
}
this.tutorialService.optionnalPhase = '';
}
setOptionnalPhase(phase: string): void {
this.tutorialService.optionnalPhase = phase;
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'view_optionnal', undefined, undefined, 'phase_'+phase, 'Tutorial'));
}
closeTutorialTrace(): void {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'close', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
}
resumeClassicTrace(): void {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'resume_classic', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
}
}
import { Directive, HostListener, ElementRef } from '@angular/core'; import { Directive, HostListener, ElementRef, Input } from '@angular/core';
import { TutorialService } from '../services/tutorial/tutorial.service';
import { Scenario } from '../class/scenario/scenario';
import { Trace } from '../class/trace/trace';
@Directive({ @Directive({
selector: '[appDragScroll]' selector: '[appDragScroll]'
...@@ -8,8 +11,9 @@ export class DragScrollDirective { ...@@ -8,8 +11,9 @@ export class DragScrollDirective {
private startX: number = 0; private startX: number = 0;
private startY: number = 0; private startY: number = 0;
private element: HTMLElement; private element: HTMLElement;
@Input() scenario: Scenario = new Scenario();
constructor(private elementRef: ElementRef) { constructor(private elementRef: ElementRef, private tutorialService: TutorialService) {
this.element = elementRef.nativeElement; this.element = elementRef.nativeElement;
} }
...@@ -37,6 +41,10 @@ export class DragScrollDirective { ...@@ -37,6 +41,10 @@ export class DragScrollDirective {
this.element.scrollTop -= y; this.element.scrollTop -= y;
this.startX = event.clientX; this.startX = event.clientX;
this.startY = event.clientY; this.startY = event.clientY;
if (!this.tutorialService.optionnalPhase && !this.tutorialService.phaseDone[this.tutorialService.phase-1] && this.tutorialService.isActive && this.tutorialService.phase == 1) {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'valid_phase', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
this.tutorialService.validPhase();
}
} }
} }
...@@ -56,6 +64,10 @@ export class DragScrollDirective { ...@@ -56,6 +64,10 @@ export class DragScrollDirective {
} else if (event.key === 'ArrowRight') { } else if (event.key === 'ArrowRight') {
this.element.scrollBy(distance, 0); this.element.scrollBy(distance, 0);
} }
if (!this.tutorialService.optionnalPhase && !this.tutorialService.phaseDone[this.tutorialService.phase-1] && this.tutorialService.isActive && this.tutorialService.phase == 1) {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'valid_phase', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
this.tutorialService.validPhase();
}
} }
} }
} }
......
import { Directive, ElementRef, HostListener } from '@angular/core'; import { Directive, ElementRef, HostListener, Input } from '@angular/core';
import { ZoomService } from '../services/zoom/zoom.service'; import { ZoomService } from '../services/zoom/zoom.service';
import { MinimapService } from '../services/minimap/minimap.service'; import { MinimapService } from '../services/minimap/minimap.service';
import { TutorialService } from '../services/tutorial/tutorial.service';
import { Scenario } from '../class/scenario/scenario';
import { Trace } from '../class/trace/trace';
@Directive({ @Directive({
selector: '[appMouseWheelZoom]' selector: '[appMouseWheelZoom]'
}) })
export class MouseWheelZoomDirective { export class MouseWheelZoomDirective {
constructor(private elementRef: ElementRef, private zoomService: ZoomService, private minimapService: MinimapService) { } @Input() scenario: Scenario = new Scenario();
constructor(private elementRef: ElementRef, private zoomService: ZoomService, private minimapService: MinimapService, private tutorialService: TutorialService) { }
@HostListener('wheel', ['$event']) @HostListener('wheel', ['$event'])
onMouseWheel(event: WheelEvent) { onMouseWheel(event: WheelEvent) {
...@@ -23,5 +28,9 @@ export class MouseWheelZoomDirective { ...@@ -23,5 +28,9 @@ export class MouseWheelZoomDirective {
this.zoomService.zoom += zoomLevel; this.zoomService.zoom += zoomLevel;
this.elementRef.nativeElement.querySelector('.container-appMouseWheelZoom').style.transform = `scale(${this.zoomService.zoom})`; this.elementRef.nativeElement.querySelector('.container-appMouseWheelZoom').style.transform = `scale(${this.zoomService.zoom})`;
this.minimapService.reset(); this.minimapService.reset();
if (!this.tutorialService.optionnalPhase && !this.tutorialService.phaseDone[this.tutorialService.phase-1] && this.tutorialService.isActive && this.tutorialService.phase == 2) {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'valid_phase', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
this.tutorialService.validPhase();
}
} }
} }
\ No newline at end of file
...@@ -7,6 +7,7 @@ import { CleanDialogComponent } from 'src/app/components/dialogs/clean-dialog/cl ...@@ -7,6 +7,7 @@ import { CleanDialogComponent } from 'src/app/components/dialogs/clean-dialog/cl
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { Trace } from 'src/app/class/trace/trace'; import { Trace } from 'src/app/class/trace/trace';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { TutorialService } from 'src/app/services/tutorial/tutorial.service';
@Component({ @Component({
selector: 'app-game-context', selector: 'app-game-context',
...@@ -18,7 +19,7 @@ export class GameContextComponent implements OnInit { ...@@ -18,7 +19,7 @@ export class GameContextComponent implements OnInit {
@Input() scenario: Scenario = new Scenario(); @Input() scenario: Scenario = new Scenario();
@Input() gameContext: GameContext = new GameContext(); @Input() gameContext: GameContext = new GameContext();
constructor(protected pieceDetailsService: PieceDetailsService, protected tooltipService: TooltipService, public dialog: MatDialog, protected translate: TranslateService) { } constructor(protected pieceDetailsService: PieceDetailsService, protected tooltipService: TooltipService, public dialog: MatDialog, protected translate: TranslateService, private tutorialService: TutorialService) { }
ngOnInit(): void { ngOnInit(): void {
} }
...@@ -54,5 +55,9 @@ export class GameContextComponent implements OnInit { ...@@ -54,5 +55,9 @@ export class GameContextComponent implements OnInit {
} else { } else {
this.scenario.traces.push(new Trace(this.scenario.traces.length,'erase',undefined,undefined,source,'Context_g', '#B6CC87')); this.scenario.traces.push(new Trace(this.scenario.traces.length,'erase',undefined,undefined,source,'Context_g', '#B6CC87'));
} }
if (!this.tutorialService.optionnalPhase && !this.tutorialService.phaseDone[this.tutorialService.phase-1] && this.tutorialService.isActive && this.tutorialService.phase == 3) {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'valid_phase', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
this.tutorialService.validPhase();
}
} }
} }
...@@ -7,6 +7,7 @@ import { CleanDialogComponent } from 'src/app/components/dialogs/clean-dialog/cl ...@@ -7,6 +7,7 @@ import { CleanDialogComponent } from 'src/app/components/dialogs/clean-dialog/cl
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { Trace } from 'src/app/class/trace/trace'; import { Trace } from 'src/app/class/trace/trace';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { TutorialService } from 'src/app/services/tutorial/tutorial.service';
@Component({ @Component({
selector: 'app-game-educationnal-objective', selector: 'app-game-educationnal-objective',
...@@ -18,7 +19,7 @@ export class GameEducationnalObjectiveComponent implements OnInit { ...@@ -18,7 +19,7 @@ export class GameEducationnalObjectiveComponent implements OnInit {
@Input() scenario: Scenario = new Scenario(); @Input() scenario: Scenario = new Scenario();
@Input() gameEducationnalObjective: GameEducationnalObjective = new GameEducationnalObjective(); @Input() gameEducationnalObjective: GameEducationnalObjective = new GameEducationnalObjective();
constructor(protected pieceDetailsService: PieceDetailsService, protected tooltipService: TooltipService, public dialog: MatDialog, protected translate: TranslateService) { } constructor(protected pieceDetailsService: PieceDetailsService, protected tooltipService: TooltipService, public dialog: MatDialog, protected translate: TranslateService, private tutorialService: TutorialService) { }
ngOnInit(): void { ngOnInit(): void {
} }
...@@ -50,5 +51,9 @@ export class GameEducationnalObjectiveComponent implements OnInit { ...@@ -50,5 +51,9 @@ export class GameEducationnalObjectiveComponent implements OnInit {
} else { } else {
this.scenario.traces.push(new Trace(this.scenario.traces.length,'erase',undefined,undefined,source,'Obj_g', '#BAC5D8')); this.scenario.traces.push(new Trace(this.scenario.traces.length,'erase',undefined,undefined,source,'Obj_g', '#BAC5D8'));
} }
if (!this.tutorialService.optionnalPhase && !this.tutorialService.phaseDone[this.tutorialService.phase-1] && this.tutorialService.isActive && this.tutorialService.phase == 3) {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'valid_phase', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
this.tutorialService.validPhase();
}
} }
} }
...@@ -31,7 +31,7 @@ matTooltipPosition="left" [matTooltipDisabled]="!tooltipService.activatedTooltip ...@@ -31,7 +31,7 @@ matTooltipPosition="left" [matTooltipDisabled]="!tooltipService.activatedTooltip
</div> </div>
<div class="piece-form-name"> <div class="piece-form-name">
<label for="name">{{'role_name_label' | translate}}</label> <label for="name">{{'role_name_label' | translate}}</label>
<input name="name" type="text" [(ngModel)]="role.questName" (change)="editTrace($event,'quest_name')" [placeholder]="translate.instant('role_name_placeholder')" <input name="name" type="text" [(ngModel)]="role.questName" (change)="editTrace($event,'quest_name'); validTutorialPhase5();" [placeholder]="translate.instant('role_name_placeholder')"
[matTooltip]="translate.instant('role_name_tooltip')" [matTooltip]="translate.instant('role_name_tooltip')"
matTooltipPosition="above" [matTooltipDisabled]="!tooltipService.activatedTooltips"/> matTooltipPosition="above" [matTooltipDisabled]="!tooltipService.activatedTooltips"/>
</div> </div>
......
...@@ -20,6 +20,7 @@ import { CreateDialogComponent } from 'src/app/components/dialogs/create-dialog/ ...@@ -20,6 +20,7 @@ import { CreateDialogComponent } from 'src/app/components/dialogs/create-dialog/
import { Trace } from 'src/app/class/trace/trace'; import { Trace } from 'src/app/class/trace/trace';
import { MinimapService } from 'src/app/services/minimap/minimap.service'; import { MinimapService } from 'src/app/services/minimap/minimap.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { TutorialService } from 'src/app/services/tutorial/tutorial.service';
@Component({ @Component({
selector: 'app-role', selector: 'app-role',
...@@ -34,7 +35,7 @@ export class RoleComponent implements OnInit { ...@@ -34,7 +35,7 @@ export class RoleComponent implements OnInit {
@Input() i: number = 0; @Input() i: number = 0;
@Input() missionIndex: number = 0; @Input() missionIndex: number = 0;
constructor(protected pieceDetailsService: PieceDetailsService, protected tooltipService: TooltipService, public dialog: MatDialog, private minimapService: MinimapService, protected translate: TranslateService) { } constructor(protected pieceDetailsService: PieceDetailsService, protected tooltipService: TooltipService, public dialog: MatDialog, private minimapService: MinimapService, protected translate: TranslateService, private tutorialService: TutorialService) { }
ngOnInit(): void { ngOnInit(): void {
this.mission.equalizeLengths(); this.mission.equalizeLengths();
...@@ -48,6 +49,10 @@ export class RoleComponent implements OnInit { ...@@ -48,6 +49,10 @@ export class RoleComponent implements OnInit {
this.pieceDetailsService.missionIndex = this.missionIndex; this.pieceDetailsService.missionIndex = this.missionIndex;
this.pieceDetailsService.roleIndex = this.i; this.pieceDetailsService.roleIndex = this.i;
this.pieceDetailsService.pieceIndex = this.i; this.pieceDetailsService.pieceIndex = this.i;
if (!this.tutorialService.optionnalPhase && !this.tutorialService.phaseDone[this.tutorialService.phase-1] && this.tutorialService.isActive && this.tutorialService.phase == 4) {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'valid_phase', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
this.tutorialService.validPhase();
}
} }
onClickAdd(): void { onClickAdd(): void {
...@@ -183,6 +188,7 @@ export class RoleComponent implements OnInit { ...@@ -183,6 +188,7 @@ export class RoleComponent implements OnInit {
this.role.rewards.push(new QuestReward()); this.role.rewards.push(new QuestReward());
this.scenario.traces.push(new Trace(this.scenario.traces.length,'new',this.missionIndex,this.i,'Reward_['+this.role.rewards.length+']', 'Role_['+this.i+']', '#9AD5EC', '*')); this.scenario.traces.push(new Trace(this.scenario.traces.length,'new',this.missionIndex,this.i,'Reward_['+this.role.rewards.length+']', 'Role_['+this.i+']', '#9AD5EC', '*'));
this.minimapService.reset(); this.minimapService.reset();
this.validTutorialPhase5();
} }
changeRewardType(index: number, type: string): void { changeRewardType(index: number, type: string): void {
...@@ -274,4 +280,11 @@ export class RoleComponent implements OnInit { ...@@ -274,4 +280,11 @@ export class RoleComponent implements OnInit {
this.scenario.traces.push(new Trace(this.scenario.traces.length,'erase',this.missionIndex,this.i,source,'Role_['+(this.i)+']', '#9AD5EC','*')); this.scenario.traces.push(new Trace(this.scenario.traces.length,'erase',this.missionIndex,this.i,source,'Role_['+(this.i)+']', '#9AD5EC','*'));
} }
} }
validTutorialPhase5(): void {
if (!this.tutorialService.optionnalPhase && !this.tutorialService.phaseDone[this.tutorialService.phase-1] && this.tutorialService.isActive && this.tutorialService.phase == 5 && this.role.questName && this.role.rewards.length > 0) {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'valid_phase', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
this.tutorialService.validPhase();
}
}
} }
...@@ -16,6 +16,8 @@ import { IdentifierSnackbarComponent } from 'src/app/components/snackbars/identi ...@@ -16,6 +16,8 @@ import { IdentifierSnackbarComponent } from 'src/app/components/snackbars/identi
import { Trace } from 'src/app/class/trace/trace'; import { Trace } from 'src/app/class/trace/trace';
import { MinimapService } from 'src/app/services/minimap/minimap.service'; import { MinimapService } from 'src/app/services/minimap/minimap.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { TutorialService } from 'src/app/services/tutorial/tutorial.service';
import { FinishTutorialComponent } from 'src/app/components/snackbars/finish-tutorial/finish-tutorial.component';
@Component({ @Component({
selector: 'app-annexe-task', selector: 'app-annexe-task',
...@@ -43,12 +45,17 @@ export class AnnexeTaskComponent implements OnInit { ...@@ -43,12 +45,17 @@ export class AnnexeTaskComponent implements OnInit {
antecedent: boolean = false; antecedent: boolean = false;
constructor(protected pieceDetailsService: PieceDetailsService, protected tooltipService: TooltipService, public dialog: MatDialog, constructor(protected pieceDetailsService: PieceDetailsService, protected tooltipService: TooltipService, public dialog: MatDialog,
private _snackBar: MatSnackBar, private minimapService: MinimapService, protected translate: TranslateService) { } private _snackBar: MatSnackBar, private minimapService: MinimapService, protected translate: TranslateService, private tutorialService: TutorialService) { }
ngOnInit(): void { ngOnInit(): void {
this.setPieceWidth(); this.setPieceWidth();
this.mission.equalizeLengths(); this.mission.equalizeLengths();
this.minimapService.reset(); this.minimapService.reset();
if (!this.tutorialService.optionnalPhase && !this.tutorialService.phaseDone[this.tutorialService.phase-1] && this.tutorialService.isActive && this.tutorialService.phase == 9) {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'valid_phase', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
this._snackBar.openFromComponent(FinishTutorialComponent, { duration: 5000 });
this.tutorialService.validPhase();
}
} }
durationChange(): void { durationChange(): void {
......
...@@ -153,7 +153,7 @@ ...@@ -153,7 +153,7 @@
<mat-icon *ngIf="task.symbol.symbol" [style.color]="task.symbol.color" [fontIcon]="task.symbol.symbol"></mat-icon> <mat-icon *ngIf="task.symbol.symbol" [style.color]="task.symbol.color" [fontIcon]="task.symbol.symbol"></mat-icon>
</button> </button>
</div> </div>
<textarea class="piece-form-content" [(ngModel)]="task.objective" (change)="editTrace($event,'Task_action')" [style.background-image]="(i == findFirstIndexOfTaskType('normal')[0] && j == findFirstIndexOfTaskType('normal')[1]) ? urlIcon : ''" <textarea class="piece-form-content" [(ngModel)]="task.objective" (change)="editTrace($event,'Task_action'); validTutorialPhase6();" [style.background-image]="(i == findFirstIndexOfTaskType('normal')[0] && j == findFirstIndexOfTaskType('normal')[1]) ? urlIcon : ''"
[placeholder]="translate.instant('normalTask_action_placeholder')" [placeholder]="translate.instant('normalTask_action_placeholder')"
[matTooltip]="translate.instant('task_action_tooltip')" [matTooltip]="translate.instant('task_action_tooltip')"
matTooltipPosition="above" [matTooltipDisabled]="!tooltipService.activatedTooltips"></textarea> matTooltipPosition="above" [matTooltipDisabled]="!tooltipService.activatedTooltips"></textarea>
......
...@@ -16,6 +16,7 @@ import { IdentifierSnackbarComponent } from 'src/app/components/snackbars/identi ...@@ -16,6 +16,7 @@ import { IdentifierSnackbarComponent } from 'src/app/components/snackbars/identi
import { Trace } from 'src/app/class/trace/trace'; import { Trace } from 'src/app/class/trace/trace';
import { MinimapService } from 'src/app/services/minimap/minimap.service'; import { MinimapService } from 'src/app/services/minimap/minimap.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { TutorialService } from 'src/app/services/tutorial/tutorial.service';
@Component({ @Component({
selector: 'app-task', selector: 'app-task',
...@@ -43,7 +44,7 @@ export class TaskComponent implements OnInit { ...@@ -43,7 +44,7 @@ export class TaskComponent implements OnInit {
antecedent: boolean = false; antecedent: boolean = false;
constructor(protected pieceDetailsService: PieceDetailsService, protected tooltipService: TooltipService, public dialog: MatDialog, constructor(protected pieceDetailsService: PieceDetailsService, protected tooltipService: TooltipService, public dialog: MatDialog,
private _snackBar: MatSnackBar, private minimapService: MinimapService, protected translate: TranslateService) { } private _snackBar: MatSnackBar, private minimapService: MinimapService, protected translate: TranslateService, private tutorialService: TutorialService) { }
ngOnInit(): void { ngOnInit(): void {
this.setPieceWidth(); this.setPieceWidth();
...@@ -174,6 +175,7 @@ export class TaskComponent implements OnInit { ...@@ -174,6 +175,7 @@ export class TaskComponent implements OnInit {
} else { } else {
this.scenario.traces.push(new Trace(this.scenario.traces.length,'delete_common',this.missionIndex,this.roleIndex,'symbol','Task_['+this.i+';'+this.j+']', '#B9DFE3')); this.scenario.traces.push(new Trace(this.scenario.traces.length,'delete_common',this.missionIndex,this.roleIndex,'symbol','Task_['+this.i+';'+this.j+']', '#B9DFE3'));
} }
this.validTutorialPhase7();
} }
changeDisplayPrerequires(): void { changeDisplayPrerequires(): void {
...@@ -227,6 +229,10 @@ export class TaskComponent implements OnInit { ...@@ -227,6 +229,10 @@ export class TaskComponent implements OnInit {
this.displaySymbolChoice = 'hide'; this.displaySymbolChoice = 'hide';
this.mission.equalizeLengths(); this.mission.equalizeLengths();
} }
if (!this.tutorialService.optionnalPhase && !this.tutorialService.phaseDone[this.tutorialService.phase-1] && this.tutorialService.isActive && this.tutorialService.phase == 8) {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'valid_phase', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
this.tutorialService.validPhase();
}
} }
canMoveTo(direction: string): boolean { canMoveTo(direction: string): boolean {
...@@ -305,7 +311,7 @@ export class TaskComponent implements OnInit { ...@@ -305,7 +311,7 @@ export class TaskComponent implements OnInit {
this.onCheckTask(task); this.onCheckTask(task);
} else { } else {
this.onUncheckTask(task); this.onUncheckTask(task);
} }
} }
} }
...@@ -319,6 +325,7 @@ export class TaskComponent implements OnInit { ...@@ -319,6 +325,7 @@ export class TaskComponent implements OnInit {
onCheckTask(task: Task): void { onCheckTask(task: Task): void {
this.task.prerequireTasks.push(new PrerequireTask(task.identifier)); this.task.prerequireTasks.push(new PrerequireTask(task.identifier));
this.scenario.traces.push(new Trace(this.scenario.traces.length,'new',this.missionIndex,this.roleIndex,'prerequire_task','Task_['+this.i+';'+this.j+']', '#B9DFE3')); this.scenario.traces.push(new Trace(this.scenario.traces.length,'new',this.missionIndex,this.roleIndex,'prerequire_task','Task_['+this.i+';'+this.j+']', '#B9DFE3'));
this.validTutorialPhase7();
} }
onUncheckTask(task: Task): void { onUncheckTask(task: Task): void {
...@@ -342,6 +349,7 @@ export class TaskComponent implements OnInit { ...@@ -342,6 +349,7 @@ export class TaskComponent implements OnInit {
onCheckRessource(ressource: Ressource): void { onCheckRessource(ressource: Ressource): void {
this.task.prerequireRessources.push(new PrerequireRessource(ressource)); this.task.prerequireRessources.push(new PrerequireRessource(ressource));
this.scenario.traces.push(new Trace(this.scenario.traces.length,'new',this.missionIndex,this.roleIndex,'prerequire_ressource','Task_['+this.i+';'+this.j+']', '#B9DFE3')); this.scenario.traces.push(new Trace(this.scenario.traces.length,'new',this.missionIndex,this.roleIndex,'prerequire_ressource','Task_['+this.i+';'+this.j+']', '#B9DFE3'));
this.validTutorialPhase7();
} }
onUncheckRessource(ressource: Ressource): void { onUncheckRessource(ressource: Ressource): void {
...@@ -378,4 +386,19 @@ export class TaskComponent implements OnInit { ...@@ -378,4 +386,19 @@ export class TaskComponent implements OnInit {
editMoveTrace(event: any, source: string): void { editMoveTrace(event: any, source: string): void {
this.scenario.traces.push(new Trace(this.scenario.traces.length,'move',this.missionIndex,this.roleIndex,source,'Task_['+this.i+';'+this.j+']', '#B9DFE3')); this.scenario.traces.push(new Trace(this.scenario.traces.length,'move',this.missionIndex,this.roleIndex,source,'Task_['+this.i+';'+this.j+']', '#B9DFE3'));
} }
validTutorialPhase6(): void {
if (!this.tutorialService.optionnalPhase && !this.tutorialService.phaseDone[this.tutorialService.phase-1] && this.tutorialService.isActive && this.tutorialService.phase == 6) {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'valid_phase', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
this.tutorialService.validPhase();
}
}
validTutorialPhase7(): void {
if (!this.tutorialService.optionnalPhase && !this.tutorialService.phaseDone[this.tutorialService.phase-1] && this.tutorialService.isActive && this.tutorialService.phase == 7
&& this.task.symbol.symbol && (this.task.prerequireTasks.length > 0 || this.task.prerequireRessources.length > 0)) {
this.scenario.traces.push(new Trace(this.scenario.traces.length, 'valid_phase', undefined, undefined, 'phase_'+this.tutorialService.phase, 'Tutorial'));
this.tutorialService.validPhase();
}
}
} }
\ No newline at end of file
import { TestBed } from '@angular/core/testing';
import { TutorialService } from './tutorial.service';
describe('TutorialService', () => {
let service: TutorialService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(TutorialService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class TutorialService {
phase: number = 1;
isActive: boolean = true;
optionnalPhase: string = '';
phaseDone: boolean[] = [];
validationFeedback: string = '';
constructor() {
for(let i = 0; i < 9; i++) {
this.phaseDone[i] = false;
}
}
validPhase(): void {
this.phaseDone[this.phase-1] = true;
if (this.phase < 9) {
this.phase++;
}
this.validationFeedback = 'validation';
setTimeout(() => {
this.validationFeedback = '';
}, 500);
}
isDone(): boolean {
return this.phaseDone.some(phase => false);
}
}
...@@ -265,6 +265,59 @@ ...@@ -265,6 +265,59 @@
"comments_button_answer": "Answer", "comments_button_answer": "Answer",
"comments_button_newComment": "Add a comment", "comments_button_newComment": "Add a comment",
"comments_answer_delete": "this Answer", "comments_answer_delete": "this Answer",
"comments_comment_delete": "this Comment" "comments_comment_delete": "this Comment",
"tutorial_resume": "Resume tutorial",
"tutorial_leave": "Reduce",
"tutorial_title": "Tutorial",
"tutorial_finish" : "Congratulations, you have completed the tutorial. Feel free to explore the optional features to fully enjoy the RLG Maker.",
"tutorial_previous": "Previous",
"tutorial_next": "Next",
"tutorial_phase": "Phase",
"tutorial_back": "Back to the classic tutorial",
"tutorial_optionnal": "Optional features to enhance your scenario",
"tutorial_phase_1_title": "1. Navigate in the RLG Maker: ",
"tutorial_phase_1": "- Move up and down: Hold the right mouse button and drag on the page, or swipe two fingers on the touchpad (up to scroll down and vice versa to scroll up). Alternatively, click on the mini-map area where you want to move.",
"tutorial_phase_2_title": "2. Navigate in the RLG Maker:",
"tutorial_phase_2": "- Zoom in or out: use the mouse wheel or spread 2 fingers on a touchpad to zoom in and pinch them together to zoom out. Another option: use the zoom buttons at the bottom right of the page.",
"tutorial_phase_3_title": "3. Define pedagogical objectives and context:",
"tutorial_phase_3": "Fill the tiles from left to right. There is no priority order for tiles in the same column.",
"tutorial_phase_4_title": "4. Define roles:",
"tutorial_phase_4": "Choose the different roles present in your scenario. They are arranged in the same column, so the 2nd one is placed below the 1st. IMPORTANT: roles should perform tasks together (discussion, action, exchange of elements) at 1 or more moments.",
"tutorial_phase_5_title": "5. In the order of your choice: choose and name quests & define quest rewards:",
"tutorial_phase_5": "Give a name to the quest for each role. What will they do in a few words? What will the role obtain at the end of their quest? Unlock a new quest, obtain an item, improve skills in a domain, etc.",
"tutorial_phase_6_title": "6. Define tasks for each role:",
"tutorial_phase_6": "Define all actions to be taken to achieve the pedagogical objectives defined earlier for each role.",
"tutorial_phase_7_title": "7. In the order of your choice: highlight common tasks, identify prerequisites & estimate task duration:",
"tutorial_phase_7": "For each task done with another role, click on the top right of that task and choose the same symbol for tasks performed simultaneously. Identify the order of execution of tasks for the same role. Click on the 'prerequisites' button for the task and select the task(s) necessary to execute it. If a task is to be done after another, it should be placed in the column to its right. If an action is quick to perform, it will take 1 Unit of Time (UT), setting a larger UT will increase the size of the task. This ensures that common tasks can be performed simultaneously and that scenarios have the same duration.",
"tutorial_phase_8_title": "8. Order the tasks:",
"tutorial_phase_8": "If not done in the previous phase, move tasks based on prerequisites using the arrows that appear when hovering over a task. Tasks in the same column have no execution order. Those in the first column have no prerequisites and can be executed at the beginning of the game.",
"tutorial_phase_9_title": "9. Add side tasks.",
"tutorial_phase_9": "Quick and unnecessary tasks to achieve pedagogical objectives, they serve to occupy a role while another finishes their tasks to reach a common task together or even to achieve equivalent playtime between roles. A player can do all side tasks quickly or none at all; it should not be a blocking factor for the scenario.",
"tutorial_optionnal_event": "Random Event",
"tutorial_optionnalPhase_event": "These are useful if you want to add an element of unpredictability to your scenario, making the game more replayable. To add one, click on the '+' as if adding a Task and select 'Random Event'.",
"tutorial_optionnal_repeatTask": "Repeated Task",
"tutorial_optionnalPhase_repeatTask": "If a task needs to be repeated a certain number of times or until a certain condition is met, click on that task, and enter the conditions in the dedicated tile in the sidebar. An icon will appear on the Task to easily identify this indication.",
"tutorial_optionnal_interrupt": "Interrupt Another Role's Task",
"tutorial_optionnalPhase_interrupt": "If a task can be interrupted by another role, click on that task and fill in the conditions in the corresponding tile in the sidebar. An icon will appear on the Task to easily identify this indication.",
"tutorial_optionnal_rules": "Game Rules",
"tutorial_optionnalPhase_rules": "If you want to precisely define the rules of your game.",
"tutorial_optionnal_finalTask": "Final Task",
"tutorial_optionnalPhase_finalTask": "This task is used to conclude the quest. There are no more tasks after this one. If there are multiple ways to finish a quest, you can add multiple Final Tasks, each representing a different ending.",
"tutorial_optionnal_repeat": "Repeat the Turn",
"tutorial_optionnalPhase_repeat": "This tile allows you to repeat a sequence of actions and avoids having to copy tasks indefinitely. The tasks to be repeated can either be from the very beginning of the quest or from the beginning of a Step. To add a tile of this type, click on a '+' as if adding a Task.",
"tutorial_optionnal_step": "Step",
"tutorial_optionnalPhase_step": "This tile allows you to 'break down' the quest into multiple parts if necessary.",
"tutorial_optionnal_supplementaryRole": "Supplementary Role",
"tutorial_optionnalPhase_supplementaryRole": "This allows a role to have additional options related to this new function. For example, they can perform tasks specific to this supplementary role. To do this, provide the information for this role, then click on the relevant Task and select this role in the 'Task linked to the supplementary role' tile in the sidebar. If you have defined a specific color for this role, the linked Task will have the same color.",
"tutorial_optionnal_optionnalTask": "Optional Task",
"tutorial_optionnalPhase_optionnalTask": "This Task is necessary to meet pedagogical objectives, unlike additional Tasks. However, the role will have the choice between 2 optional Tasks to accomplish what is asked. For example, if they have to create a website, they could either (optional Task 1) use a platform that allows creating websites without coding or (optional Task 2) create their site from scratch, with code.",
"tutorial_optionnal_characters": "Non-player Characters",
"tutorial_optionnalPhase_characters": "It is possible to add non-player characters to your scenario who can interact with the Roles. To do this, create a character (1st column on the left) and then select the task in which they will intervene and choose this character in the associated tile in the sidebar. An icon in the color of your character will appear on the Task to easily identify this indication.",
"tutorial_optionnal_resource": "Resources (& skills)",
"tutorial_optionnalPhase_resource": "Distinguish between resources common to all players, found at the bottom of the Game Rules tile, and resources specific to each Role (defined at the bottom of their tile). Resources can be prerequisites for completing a Task.",
"tutorial_optionnal_occurence": "Role Occurrence",
"tutorial_optionnalPhase_occurence": "This allows you to define, when the number of players is greater than the number of roles, the distribution of players among the different roles. To define this, click on a Role tile.",
"tutorial_optionnal_comments": "Comments",
"tutorial_optionnalPhase_comments": "This allows you to add any information you deem important to specify on any tile. To add a comment, click on a tile, and the comments will be present in the sidebar."
} }
\ No newline at end of file
...@@ -265,5 +265,59 @@ ...@@ -265,5 +265,59 @@
"comments_button_answer": "Répondre", "comments_button_answer": "Répondre",
"comments_button_newComment": "Ajouter un commentaire", "comments_button_newComment": "Ajouter un commentaire",
"comments_answer_delete": "cette Réponse", "comments_answer_delete": "cette Réponse",
"comments_comment_delete": "ce Commentaire" "comments_comment_delete": "ce Commentaire",
"tutorial_resume": "Reprendre le tutoriel",
"tutorial_leave": "Réduire",
"tutorial_title": "Tutoriel",
"tutorial_finish" : "Félicitation, vous avez terminé le tutoriel, n'hésitez pas à consulter les options facultatives pour profiter pleinement du RLG Maker",
"tutorial_previous": "Précédent",
"tutorial_next": "Suivant",
"tutorial_phase": "Phase",
"tutorial_back": "Retour au tutoriel classique",
"tutorial_optionnal": "Options facultatives pour enrichir son scénario",
"tutorial_phase_1_title": "1. Se déplacer dans le RLG Maker :",
"tutorial_phase_1": "- Bouger de haut en bas : maintenez le clic droit de la souris et déplacez-vous dans la page ou glissez 2 doigts sur le pad (de bas en haut pour descendre et inversement pour monter). Autre solution : cliquez sur la zone de la mini map où vous souhaitez vous déplacer.",
"tutorial_phase_2_title": "2. Se déplacer dans le RLG Maker :",
"tutorial_phase_2": "- Zoomer ou dézoomer : utilisez la mollette de la souris ou sur un pad écartez 2 doigts pour zoomer et rapprochez-les pour dézoomer. Autre solution : utilisez les boutons de zoom en bas à droite de la page.",
"tutorial_phase_3_title": "3. Définir les objectifs pédagogiques et le contexte :",
"tutorial_phase_3": "Remplir les tuiles de gauche à droite. Il n'y a pas d'ordre de priorité pour des tuiles dans une même colonne.",
"tutorial_phase_4_title": "4. Définir les rôles :",
"tutorial_phase_4": "Choisir les différents rôles présents dans votre scénario. Ils sont disposés dans une même colonne, le 2e est donc placé plus bas en dessous du 1er. IMPORTANT : les rôles devront faire des tâches ensemble (discussion, action, échange d'éléments) à 1 ou plusieurs moments.",
"tutorial_phase_5_title": "5. Dans l'ordre de votre choix : choisir et nommer les quêtes & définir les récompenses de la quête :",
"tutorial_phase_5": "Donner un nom à la quête de chaque rôle. Que va-t-il faire en quelques mots ? Que va obtenir le rôle à la fin de sa quête ? Débloquer une nouvelle quête, obtenir un objet, monter en compétence dans un domaine, etc.",
"tutorial_phase_6_title": "6. Définir les tâches de chaque rôle :",
"tutorial_phase_6": "Définir toutes les actions à effectuer pour atteindre les objectifs pédagogiques définis plus tôt pour chaque rôle.",
"tutorial_phase_7_title": "7. Dans l'ordre de votre choix : mettre en évidence les tâches communes, identifier les prérequis & estimer la durée des tâches :",
"tutorial_phase_7": "Pour chaque tâche faite avec un autre rôle, cliquez sur la case en haut à droite de cette tâche et choisir le même symbole pour les tâches réalisées simultanément. Identifier l'ordre d'exécution des tâches d'un même rôle. Cliquer sur le bouton «prérequis» de la tâche et sélectionner la ou les tâches nécessaires pour exécuter celle-ci. Si une tâche est à faire après une autre, elle doit être placée dans la colonne à sa droite. Si une action est rapide à effectuer elle prendra 1 Unité de Temps (UT), mettre un UT plus grande allongera la taille de la tâche. Ceci permet de vérifier que les tâches communes peuvent être effectuées simultanément et que les scénarios soient de durée identique.",
"tutorial_phase_8_title": "8. Ordonner les tâches :",
"tutorial_phase_8": "Si cela n'a pas été effectué à la phase précédente, déplacer les tâches en fonction des prérequis grâce aux flèches qui apparaissent au survol d'une tâche. Les tâches d'une même colonne n'ont pas d'ordre d'exécution. Celles de la première colonne n'ont aucun prérequis et peuvent être exécutées dès le début du jeu.",
"tutorial_phase_9_title": "9. Ajouter des tâches annexes.",
"tutorial_phase_9": "Tâches rapides et non nécessaires pour atteindre les objectifs pédagogiques, elles servent à occuper un rôle pendant qu'un autre finit ses tâches pour atteindre une tâche commune ensemble ou même pour obtenir un temps de jeu équivalent entre les rôles. Un joueur peut faire toutes les tâches annexes s'il est rapide comme n'en faire aucune, cela ne doit pas être bloquant pour le scénario.",
"tutorial_optionnal_event": "Événement aléatoire",
"tutorial_optionnalPhase_event": "Ils sont utiles si vous souhaitez mettre un peu d'imprévu dans votre scénario, le jeu devient alors plus facilement rejouable. Pour en ajouter, cliquez sur le «+» comme pour ajouter une Tâche et sélectionnez «Événement aléatoire».",
"tutorial_optionnal_repeatTask": "Tâche répétée",
"tutorial_optionnalPhase_repeatTask": "Si une tâche doit être répétée un certain nombre de fois ou jusqu'à une certaine condition, alors cliquez sur cette tâche, et entrez ces conditions dans la tuile dédiée dans la barre latérale. Une icône apparaîtra sur la Tâche pour repérer cette indication plus facilement.",
"tutorial_optionnal_interrupt": "Interrompre la Tâche d'un autre Rôle",
"tutorial_optionnalPhase_interrupt": "Si une tâche peut être interrompue par un autre rôle alors cliquez sur cette tâche et remplissez les conditions dans la tuile en question dans la barre latérale. Une icône apparaîtra sur la Tâche pour repérer cette indication plus facilement.",
"tutorial_optionnal_rules": "Règles du jeu",
"tutorial_optionnalPhase_rules": "Si vous souhaitez définir précisément les règles de votre jeu.",
"tutorial_optionnal_finalTask": "Tâche finale",
"tutorial_optionnalPhase_finalTask": "Cette tâche permet de clôturer la quête. Il n'y a plus de tâche après celle-ci. S'il y a plusieurs manières de finir une quête, il est possible de mettre plusieurs Tâches finales, chacune représentant une fin différente.",
"tutorial_optionnal_repeat": "Répéter le tour",
"tutorial_optionnalPhase_repeat": "Cette tuile permet de répéter une séquence d'actions et évite de devoir recopier indéfiniment des Tâches. Les tâches à répéter peuvent être soit depuis le tout début de la quête soit depuis le début d'une Étape. Pour ajouter une tuile de ce type, cliquez sur un «+» comme pour ajouter une Tâche.",
"tutorial_optionnal_step": "Étape",
"tutorial_optionnalPhase_step": "Cette tuile permet de «découper» la quête en plusieurs parties s'il y en a.",
"tutorial_optionnal_supplementaryRole": "Rôle supplémentaire",
"tutorial_optionnalPhase_supplementaryRole": "Ceci permet à un rôle d'obtenir des options supplémentaires liées à cette nouvelle fonction. Par exemple il pourra effectuer des Tâches qui seront propres à ce rôle supplémentaire. Pour cela, renseignez les informations de ce rôle, puis cliquez sur la Tâche en question et sélectionnez ce rôle dans la tuile «Tâche liée au rôle supplémentaire» dans la barre latérale. Si vous avez défini une couleur particulière pour ce rôle, la Tâche liée aura la même couleur.",
"tutorial_optionnal_optionnalTask": "Tâche optionnelle",
"tutorial_optionnalPhase_optionnalTask": "Cette Tâche est nécessaire pour répondre aux objectifs pédagogiques contrairement aux Tâches annexes, cependant le rôle aura le choix entre 2 Tâches optionnelles pour effectuer ce qui lui est demandé. Par exemple s'il doit faire un site web, il pourrait soit (tâche optionnelle 1) partir d'une plateforme permettant de créer des sites web sans codage soit (tâche optionnelle 2) créer son site à partir de rien, avec du code.",
"tutorial_optionnal_characters": "Personnages non joueurs",
"tutorial_optionnalPhase_characters": "Il est possible d'ajouter des personnages non joueurs à votre scénario qui pourront interagir avec les Rôles. Pour cela, créez un personnage (1ere colonne à gauche) puis sélectionnez la tâche dans laquelle il interviendra et choisissez ce personnage dans la tuile associée dans la barre latérale. Une icône de la couleur de votre personnage apparaîtra sur la Tâche pour repérer cette indication plus facilement.",
"tutorial_optionnal_resource": "Ressources (& compétences)",
"tutorial_optionnalPhase_resource": "Distinguez les ressources communes à tous les joueurs, présentes en bas de la tuile Règles du jeu des ressources propres à chaque Rôle (définies en bas de leur tuile). Les ressources peuvent faire partie des pré-requis pour réaliser une Tâche.",
"tutorial_optionnal_occurence": "Occurence du Rôle",
"tutorial_optionnalPhase_occurence": "Ceci permet de définir lorsque le nombre de joueur est supérieur au nombre de rôles, la répartition des joueurs au sein des différents rôles. Pour définir cela, cliquez sur une tuile Rôle.",
"tutorial_optionnal_comments": "Commentaires",
"tutorial_optionnalPhase_comments": "Ceci permet d'ajouter tout élément qui vous semble important de préciser sur n'importe quelle tuile. Pour ajouter un commentaire, cliquez sur une tuile et les commentaires seront présents dans la barre latérale."
} }
\ No newline at end of file
...@@ -24,7 +24,11 @@ ...@@ -24,7 +24,11 @@
font-style: italic; font-style: italic;
} }
body { body, html {
width: 99%;
height: 99%;
background-color: var(--background-color); background-color: var(--background-color);
font-family: 'Glacial Indifference', sans-serif; font-family: 'Glacial Indifference', sans-serif;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment