SES-146 Added spells dialog #75

Merged
s426128 merged 2 commits from SES-146 into dev 2021-01-16 19:33:22 +01:00
7 changed files with 254 additions and 2 deletions

View File

@ -46,6 +46,7 @@ import { ArmorService } from '../services/armor.service';
import { OtherEquipmentService } from '../services/other-equipment.service'; import { OtherEquipmentService } from '../services/other-equipment.service';
import { GameMasterMonstersTableComponent } from './components/game-master-monsters-table/game-master-monsters-table.component'; import { GameMasterMonstersTableComponent } from './components/game-master-monsters-table/game-master-monsters-table.component';
import { MonsterService } from '../services/monster.service'; import { MonsterService } from '../services/monster.service';
import { SpellDetailsDialogComponent } from './components/spell-details-dialog/spell-details-dialog.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -63,6 +64,7 @@ import { MonsterService } from '../services/monster.service';
GameMasterWeaponsTableComponent, GameMasterWeaponsTableComponent,
GameMasterCharacterActionsDialogComponent, GameMasterCharacterActionsDialogComponent,
GameMasterMonstersTableComponent, GameMasterMonstersTableComponent,
SpellDetailsDialogComponent,
], ],
imports: [ imports: [
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }), BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
@ -108,6 +110,7 @@ import { MonsterService } from '../services/monster.service';
GameMasterMonstersTableComponent, GameMasterMonstersTableComponent,
AbilitiesComponent, AbilitiesComponent,
GameMasterCharacterActionsDialogComponent, GameMasterCharacterActionsDialogComponent,
SpellDetailsDialogComponent,
], ],
}) })
export class AppModule {} export class AppModule {}

View File

@ -27,7 +27,7 @@
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="ShowSpellDetailsDialog(row.id)"></tr>
<tr class="mat-row" *matNoDataRow> <tr class="mat-row" *matNoDataRow>
<td class="mat-cell" colspan="4">No data matching the filter "{{input.value}}"</td> <td class="mat-cell" colspan="4">No data matching the filter "{{input.value}}"</td>

View File

@ -7,6 +7,8 @@ import { SpellService } from '../../../services/spell.service';
import { first } from 'rxjs/operators'; import { first } from 'rxjs/operators';
import { ErrorResponse } from '../../../types/ErrorResponse'; import { ErrorResponse } from '../../../types/ErrorResponse';
import { HttpErrorResponse } from '@angular/common/http'; import { HttpErrorResponse } from '@angular/common/http';
import { MatDialog } from '@angular/material';
import { SpellDetailsDialogComponent } from '../spell-details-dialog/spell-details-dialog.component';
@Component({ @Component({
selector: 'app-game-master-spells-table', selector: 'app-game-master-spells-table',
@ -20,7 +22,7 @@ export class GameMasterSpellsTableComponent implements OnInit {
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
@ViewChild(MatSort, { static: true }) sort: MatSort; @ViewChild(MatSort, { static: true }) sort: MatSort;
constructor(private spellService: SpellService) { constructor(private spellService: SpellService, public dialog: MatDialog) {
spellService spellService
.GetAllSpells() .GetAllSpells()
.pipe(first()) .pipe(first())
@ -49,4 +51,9 @@ export class GameMasterSpellsTableComponent implements OnInit {
this.dataSource.paginator.firstPage(); this.dataSource.paginator.firstPage();
} }
} }
ShowSpellDetailsDialog(spellId: number) {
let spell = this.dataSource.data.find((e) => e.id == spellId);
this.dialog.open(SpellDetailsDialogComponent, { data: { spell: spell } });
}
} }

View File

@ -0,0 +1,38 @@
.spell-card-flag {
clip-path: polygon(100% 0, 100% 80%, 50% 100%, 0 80%, 0 0);
width: 70px;
height: 150px;
}
.shadow-for-flag {
right: 5%;
position: absolute;
filter: drop-shadow(0px 0px 3px white);
}
.names-on-flag {
height: 18px;
font-size: 15px !important;
text-align: center;
}
::ng-deep mat-dialog-container {
padding: 0 !important;
}
::ng-deep mat-card {
padding: 0 !important;
}
mat-icon {
vertical-align: middle;
}
.spell-details-card {
color: whitesmoke;
background-color: lightgray;
max-width: 600px;
}
.spell-details-card > mat-card-header {
background-color: #3d4751;
}
.spell-details-card > mat-card-content {
background-color: #4f5c69;
}
::-webkit-scrollbar {
display: none;
}

View File

@ -0,0 +1,49 @@
<mat-card class="spell-details-card">
<mat-card-header>
<div style="margin-top: 10px">
<mat-card-title>{{spell.name}}</mat-card-title>
</div>
<div class="shadow-for-flag">
<div class="spell-card-flag" [ngStyle]="{backgroundColor: spellFlagBackgroundColor}">
<div *ngFor="let elem of spell.classes" class="names-on-flag">{{elem}}</div>
<div *ngFor="let elem of spell.subclasses" class="names-on-flag">{{elem}}</div>
</div>
</div>
</mat-card-header>
<mat-card-content style="padding-left: 10px; padding-right: 10px;overflow-y: auto; max-height: 600px;-ms-overflow-style: none; scrollbar-width: none;">
<br/>
<div style="font-style: italic;">{{GetSpellLevelAndSchool()}}</div>
<hr />
<div>
<mat-icon inline style="font-size: 16px;" color="white">history_toggle_off</mat-icon> Casting Time: {{spellDictionary.GetCastingTime(spell.castingTime)}}
</div>
<div>
<mat-icon inline style="font-size: 16px;" color="white"><i class="ra ra-archery-target"></i></mat-icon> Range: {{spellDictionary.GetRange(spell.range)}}
</div>
<div>
<mat-icon inline style="font-size: 16px;" color="white">hourglass_bottom</mat-icon> Duration: {{spellDictionary.GetDuration(spell.duration)}}
</div>
<hr />
<b>Description:</b>
<p style="text-align: justify;">{{GetSpellDescription()}}</p>
<div *ngIf="spell.higherLevel.length > 0">
<b>At Higher Levels:</b>
<p style="text-align: justify;">{{GetSpellAtHigherLevels()}}</p>
</div>
<hr />
<div *ngIf="spell.components.length > 0">
<table style="border-spacing: 10px 0; border-collapse: unset">
<tr>
<td *ngIf="spell.components.includes('V')" style="text-align: center;"><mat-icon style="font-size: 34px; width: 34px; height: 34px">volume_up</mat-icon></td>
<td *ngIf="spell.components.includes('S')" style="text-align: center;"><mat-icon style="font-size: 34px; width: 34px; height: 34px">gesture</mat-icon></td>
<td *ngIf="spell.components.includes('M')" style="text-align: center;" [matTooltip]="spell.material"><mat-icon style="font-size: 34px; width: 34px; height: 34px">extension</mat-icon></td>
</tr>
<tr>
<td *ngIf="spell.components.includes('V')">Verbal</td>
<td *ngIf="spell.components.includes('S')">Somatic</td>
<td *ngIf="spell.components.includes('M')">Material</td>
</tr>
</table>
</div>
</mat-card-content>
</mat-card>

View File

@ -0,0 +1,96 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
import { SpellViewModel } from '../../../types/viewmodels/spell-viewmodels/SpellViewModel';
import SpellDictionary from '../../shared/dictionaries/SpellDictionary';
@Component({
selector: 'app-spell-details-dialog',
templateUrl: './spell-details-dialog.component.html',
styleUrls: ['./spell-details-dialog.component.css'],
})
export class SpellDetailsDialogComponent implements OnInit {
spell: SpellViewModel;
spellFlagBackgroundColor: string;
spellDictionary = SpellDictionary;
constructor(
public dialogRef: MatDialogRef<SpellDetailsDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any
) {}
ngOnInit() {
this.spell = this.data.spell;
this.ChangeFlagColor();
}
GetSpellDescription() {
let result = '';
for (let elem of this.spell.descriptions) {
result += elem;
}
return result;
}
GetSpellAtHigherLevels() {
let result = '';
for (let elem of this.spell.higherLevel) {
result += elem;
}
return result;
}
GetSpellLevelAndSchool() {
let result = '';
if (this.spell.level == -1) {
result += this.spell.school + ' Cantrip';
} else {
result += this.spell.level + ' st level ' + this.spell.school;
}
if (this.spell.ritual) {
result += ' (ritual)';
}
return result;
}
ChangeFlagColor() {
switch (this.spell.school) {
case 'Evocation': {
this.spellFlagBackgroundColor = '#7c081e';
break;
}
case 'Conjuration': {
this.spellFlagBackgroundColor = '#335f96';
break;
}
case 'Abjuration': {
this.spellFlagBackgroundColor = '#bab725';
break;
}
case 'Transmutation': {
this.spellFlagBackgroundColor = '#875a2d';
break;
}
case 'Enchantment': {
this.spellFlagBackgroundColor = '#479636';
break;
}
case 'Necromancy': {
this.spellFlagBackgroundColor = '#444444';
break;
}
case 'Divination': {
this.spellFlagBackgroundColor = '#a1a3a3';
break;
}
case 'Illusion': {
this.spellFlagBackgroundColor = '#652d84';
break;
}
default: {
this.spellFlagBackgroundColor = '#212b87';
break;
}
}
}
}

View File

@ -0,0 +1,59 @@
const Ranges: { [key: string]: string } = {
Self: 'Self',
Touch: 'Touch',
Special: 'Special',
Sight: 'Slight',
Unlimited: 'Unlimited',
Feet_5: '5 Feet',
Feet_10: '10 Feet',
Feet_30: '30 Feet',
Feet_60: '60 Feet',
Feet_90: '90 Feet',
Feet_100: '100 Feet',
Feet_120: '120 Feet',
Feet_150: '150 Feet',
Feet_300: '300 Feet',
Feet_500: '500 Feet',
Miles_1: '1 Mile',
Miles_500: '500 Miles',
};
const Durations: { [key: string]: string } = {
Instantaneous: 'Instantaneous',
Until_dispelled: 'Until dispeled',
Special: 'Special',
Minutes_1: '1 Minute',
Minutes_10: '10 Minutes',
Hours_1: '1 Hour',
Hours_2: '2 Hours',
Hours_8: '8 Hours',
Hours_24: '24 Hours',
Days_7: '7 Days',
Days_10: '10 Days',
Days_30: '30 Days',
Round_1: '1 Round',
};
const CastingTimes: { [key: string]: string } = {
Minutes_1: '1 Minute',
Minutes_10: '10 Minutes',
Hours_1: '1 Hour',
Hours_8: '8 Hours',
Hours_12: '12 Hours',
Hours_24: '24 Hours',
Action_1: '1 Action',
Reaction_1: '1 Reaction',
Bonus_Action_1: '1 Bonus Action',
};
export default new (class SpellDictionary {
GetRange(name: string) {
return Ranges[name];
}
GetDuration(name: string) {
return Durations[name];
}
GetCastingTime(name: string) {
return CastingTimes[name];
}
})();