Browse Source

implementd modal to manage custom spells. Deletion logic still errorneous

Warafear 1 year ago
parent
commit
a5523ae520
34 changed files with 1327 additions and 520 deletions
  1. 42 43
      src/app/journal/journal-spellcards/add-card/add-card.component.html
  2. 52 5
      src/app/journal/journal-spellcards/add-card/add-card.component.scss
  3. 7 1
      src/app/journal/journal-spellcards/add-card/add-card.component.ts
  4. 26 0
      src/app/journal/journal-spellcards/custom-spells-modal/custom-spells-modal.component.html
  5. 60 0
      src/app/journal/journal-spellcards/custom-spells-modal/custom-spells-modal.component.scss
  6. 23 0
      src/app/journal/journal-spellcards/custom-spells-modal/custom-spells-modal.component.spec.ts
  7. 39 0
      src/app/journal/journal-spellcards/custom-spells-modal/custom-spells-modal.component.ts
  8. 6 2
      src/app/journal/journal-spellcards/journal-spellcards.component.html
  9. 19 0
      src/app/journal/journal-spellcards/journal-spellcards.component.scss
  10. 73 15
      src/app/journal/journal-spellcards/journal-spellcards.component.ts
  11. 1 1
      src/app/journal/journal-spellcards/spellcard/spellcard.component.html
  12. 4 21
      src/app/journal/journal-spellcards/spellcard/spellcard.component.ts
  13. 14 14
      src/app/journal/journal-stats/ability-panel/proficiencies-table/proficiencies-table.component.html
  14. 2 2
      src/app/journal/journal-stats/weapons-container/spell-table/favorite-spells-modal/favorite-spells-modal.component.html
  15. 1 1
      src/app/journal/journal-stats/weapons-container/spell-table/spell-details/spell-details.component.html
  16. 2 2
      src/app/journal/journal-stats/weapons-container/weapon-table/weapon-details/weapon-details.component.html
  17. 28 5
      src/app/journal/journal-stats/weapons-container/weapon-table/weapon-modal/weapon-modal.component.html
  18. 1 3
      src/app/journal/journal-stats/weapons-container/weapon-table/weapon-modal/weapon-modal.component.scss
  19. 14 0
      src/app/journal/journal-stats/weapons-container/weapon-table/weapon-modal/weapon-modal.component.ts
  20. 12 7
      src/app/journal/journal-stats/weapons-container/weapon-table/weapon-table.component.ts
  21. 2 0
      src/app/journal/journal.module.ts
  22. 345 111
      src/app/journal/spell-modal/spell-modal.component.html
  23. 84 71
      src/app/journal/spell-modal/spell-modal.component.scss
  24. 64 24
      src/app/journal/spell-modal/spell-modal.component.ts
  25. 5 3
      src/app/shared-components/full-spellcard/full-spellcard.component.html
  26. BIN
      src/assets/images/spells/aid.jpg
  27. BIN
      src/assets/images/spells/spiritualWeapon.jpg
  28. 5 3
      src/interfaces/spell.ts
  29. 3 0
      src/interfaces/weapon.ts
  30. 1 1
      src/services/class/class.service.ts
  31. 5 3
      src/services/data/data.service.ts
  32. 11 2
      src/services/modal/modal.service.ts
  33. 356 180
      src/services/spells/spells.service.ts
  34. 20 0
      src/styles.scss

+ 42 - 43
src/app/journal/journal-spellcards/add-card/add-card.component.html

@@ -1,54 +1,53 @@
 <div class="add-card">
   @if(state === 1) {
-  <div class="clickable-background" (click)="state = 2">
+  <div class="clickable-card" (click)="state = 2; closeOthers()">
     <img class="add-icon" src="assets/icons/UIIcons/add.svg" />
   </div>
   } @else if(state === 2) {
-  <button
-    class="add-button"
-    (click)="continueToSpellSelection(false); state = 3"
-  >
-    Aus bekannten Zaubern wählen
-  </button>
-  <button
-    class="add-button"
-    (click)="continueToSpellSelection(true); state = 3"
-  >
-    Einen bekannten Zauber bearbeiten
-  </button>
-  <button class="add-button" (click)="emitNewSpell()">
-    Einen neuen Zauber erstellen
-  </button>
+  <div class="button-card">
+    <button (click)="continueToSpellSelection(false); state = 3">
+      Offizieller Zauber
+    </button>
 
-  <button class="cancel-button" (click)="resetThis()">Abbrechen</button>
+    <hr />
+    <button (click)="continueToSpellSelection(true); state = 3">
+      Offiziellen Zauber bearbeiten
+    </button>
+
+    <hr />
+
+    <button (click)="emitNewSpell()">Eigener Zauber</button>
+  </div>
   } @else if (state === 3) {
+  <div class="spell-selection">
+    <input
+      type="text"
+      class="spell-name"
+      [(ngModel)]="newSpellName"
+      placeholder="Zauber durchsuchen"
+      (keyup)="
+        isModification ? filterSpellArrayForModification() : filterSpellArray()
+      "
+    />
+    <div class="available-spells">
+      <ul>
+        @for(spell of availableSpells; track spell) {
+        <li>
+          <button
+            (click)="
+              isModification
+                ? emitNewSpellFromOfficial(spell)
+                : spellSelected(spell)
+            "
+          >
+            {{ spell.german }}
+          </button>
+        </li>
+        } @empty { Keine Zauber gefunden }
+      </ul>
+    </div>
 
-  <input
-    type="text"
-    class="spell-name"
-    [(ngModel)]="newSpellName"
-    placeholder="Zauber durchsuchen"
-    (keyup)="
-      isModification ? filterSpellArrayForModification() : filterSpellArray()
-    "
-  />
-  <div class="available-spells">
-    <ul>
-      @for(spell of availableSpells; track spell) {
-      <li>
-        <button
-          (click)="
-            isModification
-              ? emitNewSpellFromOfficial(spell)
-              : spellSelected(spell)
-          "
-        >
-          {{ spell.german }}
-        </button>
-      </li>
-      } @empty { Keine Zauber gefunden }
-    </ul>
+    <button class="cancel-button" (click)="resetThis()">Abbrechen</button>
   </div>
-  <button class="cancel-button" (click)="resetThis()">Abbrechen</button>
   }
 </div>

+ 52 - 5
src/app/journal/journal-spellcards/add-card/add-card.component.scss

@@ -8,7 +8,38 @@
     overflow: hidden;
     transition: all 0.3s ease-in-out;
 
-    .clickable-background {
+    .button-card {
+        height: 100%;
+        width: 100%;
+        display: flex;
+        flex-direction: column;
+
+        button {
+            height: 33.3%;
+            width: 100%;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            background-color: var(--items);
+            &:hover {
+                background-color: var(--items-hover);
+            }
+            transition: all 0.2s ease-in-out;
+            // cursor: pointer;
+        }
+
+        hr {
+            margin: 0;
+        }
+
+        button {
+            border: none;
+            font-size: 1.125rem;
+            font-weight: 600;
+        }
+    }
+
+    .clickable-card {
         width: 100%;
         height: 100%;
         position: relative;
@@ -29,6 +60,13 @@
     }
 }
 
+.spell-selection {
+    width: 100%;
+    height: 100%;
+    background-color: var(--background-color);
+    padding-top: 0.875rem;
+}
+
 .add-button {
     width: 90%;
     margin: 0 0.5rem;
@@ -47,10 +85,11 @@
 
 .spell-name {
     width: 94%;
-    margin: 1rem auto;
+    margin: auto;
     display: block;
     border-radius: 5px;
     border: var(--border);
+    // box-shadow: var(--shadow);
 }
 
 .available-spells {
@@ -58,9 +97,10 @@
     padding: 0.5rem;
     border: var(--border);
     border-radius: 5px;
-    // box-shadow: var(--shadow);
     overflow: auto;
-    height: 12rem;
+    height: 13rem;
+    background-color: white;
+    box-shadow: var(--shadow);
 
     button {
         all: unset;
@@ -70,6 +110,13 @@
         margin: 0 auto 0.375rem auto;
         display: block;
         width: 90%;
+        box-shadow: var(--shadow);
+        cursor: pointer;
+        transition: all 0.2s ease-in-out;
+        background-color: var(--items);
+        &:hover {
+            background-color: var(--items-hover);
+        }
     }
 }
 
@@ -83,7 +130,7 @@ ul {
     width: 10rem;
     height: 2.5rem;
     display: block;
-    margin: 1rem auto 0 auto;
+    margin: 0.75rem auto 0 auto;
     background-color: var(--delete);
     border: var(--border);
     border-radius: 10px;

+ 7 - 1
src/app/journal/journal-spellcards/add-card/add-card.component.ts

@@ -34,6 +34,8 @@ export class AddCardComponent {
     );
     this.filterSpellArray();
     this.spellsAccessor.closeSubject$.subscribe((level) => {
+      console.log('Close subject received: ', level);
+
       if (level !== this.level) {
         this.resetThis();
       }
@@ -65,7 +67,7 @@ export class AddCardComponent {
     } else {
       this.filterSpellArray();
     }
-    this.spellsAccessor.closeAllOthers(this.level);
+    this.closeOthers();
   }
 
   public spellSelected(spell: any): void {
@@ -73,6 +75,10 @@ export class AddCardComponent {
     this.resetThis();
   }
 
+  public closeOthers(): void {
+    this.spellsAccessor.closeAllOthers(this.level);
+  }
+
   public resetThis(): void {
     this.newSpellName = '';
     this.state = 1;

+ 26 - 0
src/app/journal/journal-spellcards/custom-spells-modal/custom-spells-modal.component.html

@@ -0,0 +1,26 @@
+<div class="dimensions">
+  <div class="content">
+    <div class="title">Eigene Zauber verwalten</div>
+    <div class="spell-list">
+      @for(spell of spells; let index = $index; track spell){
+
+      <div
+        matRipple
+        class="spell"
+        [ngClass]="{ selected: indexList.includes(index) }"
+        (click)="toggleSpellSelection(index)"
+      >
+        {{ spell.german }}
+      </div>
+      }
+    </div>
+  </div>
+  <div class="horizontal-buttons" style="padding: 2rem 1.5rem">
+    <ui-button [color]="'red'" [width]="'w15'" (click)="delete()"
+      >Ausgewählte Löschen</ui-button
+    >
+    <ui-button [color]="'red'" [width]="'w15'" (click)="cancel()"
+      >Abbrechen</ui-button
+    >
+  </div>
+</div>

+ 60 - 0
src/app/journal/journal-spellcards/custom-spells-modal/custom-spells-modal.component.scss

@@ -0,0 +1,60 @@
+.dimensions {
+    width: 35rem;
+    height: 45rem;
+    padding: 0;
+}
+
+.title {
+    box-shadow: var(--shadow-bottom);
+    padding: 1.5rem 0 1rem 0;
+    margin-top: 0;
+}
+
+.content {
+    margin: 0;
+}
+
+.spell-list {
+    height: 32rem;
+    padding: 1rem 1rem 0 1rem;
+    overflow-y: auto;
+    gap: 0.5rem;
+}
+
+.spell {
+    height: 3rem;
+    padding: 15px 10px;
+    margin-bottom: 0.5rem;
+    color: rgba(0, 0, 0, 0.87);
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    background: var(--items);
+    border-radius: 10px;
+    border: var(--border);
+    font-size: 1rem;
+    font-weight: 600;
+    // text-align: center;
+    cursor: pointer;
+    box-shadow:
+        0 5px 5px -3px rgba(0, 0, 0, 0.2),
+        0 8px 8px 1px rgba(0, 0, 0, 0.14),
+        0 3px 10px 2px rgba(0, 0, 0, 0.12);
+    transition: background-color 0.2s ease-in-out;
+    &:hover {
+        background-color: #f8d8c6;
+    }
+}
+
+.selected {
+    background-color: #f8d8c6;
+    box-sizing: border-box;
+    border: 3px solid var(--primary);
+    &:hover {
+        background-color: var(--items);
+    }
+}
+
+.horizontal-buttons {
+    box-shadow: var(--shadow-top);
+}

+ 23 - 0
src/app/journal/journal-spellcards/custom-spells-modal/custom-spells-modal.component.spec.ts

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

+ 39 - 0
src/app/journal/journal-spellcards/custom-spells-modal/custom-spells-modal.component.ts

@@ -0,0 +1,39 @@
+import { Component, Input } from '@angular/core';
+import { Spell } from 'src/interfaces/spell';
+import { ModalService } from 'src/services/modal/modal.service';
+
+@Component({
+  selector: 'custom-spells-modal',
+  templateUrl: './custom-spells-modal.component.html',
+  styleUrl: './custom-spells-modal.component.scss',
+})
+export class CustomSpellsModalComponent {
+  @Input() public spells: Spell[] = [];
+  public indexList: number[] = [];
+
+  public constructor(private modalAccessor: ModalService) {}
+
+  public toggleSpellSelection(index: number): void {
+    if (this.indexList.includes(index)) {
+      this.indexList = this.indexList.filter((i) => i !== index);
+    } else {
+      this.indexList.push(index);
+    }
+  }
+
+  public delete(): void {
+    const deletionList: Spell[] = [];
+    this.indexList.forEach((index) => deletionList.push(this.spells[index]));
+    console.log('CustomSpellsModalComponent: delete', deletionList);
+
+    this.modalAccessor.handleModalClosing('delete', deletionList);
+    this.indexList = [];
+  }
+
+  public cancel(): void {
+    console.log('CustomSpellsModalComponent: cancel');
+
+    this.modalAccessor.handleModalClosing('cancel', undefined);
+    this.indexList = [];
+  }
+}

+ 6 - 2
src/app/journal/journal-spellcards/journal-spellcards.component.html

@@ -1,7 +1,11 @@
 <div class="spellcards-container">
+  <button class="manage-spells" (click)="openManageCustomSpellsModal()">
+    <img src="assets/icons/UIIcons/settings.svg" /> Eigene Zauber verwalten
+  </button>
+
   <div cdkDropListGroup>
     <!-- TODO: revert array to 0-9 -->
-    @for(level of [0,1]; track level; let index = $index) {
+    @for(level of [0,1,2,3]; track level; let index = $index) {
 
     <div class="example-container">
       <div
@@ -50,7 +54,7 @@
           (createNewSpellFromOfficial)="
             openSpellCreationModal(index, true, $event)
           "
-          (onSpellSelected)="handleSpellSelection($event, index)"
+          (onSpellSelected)="addSpellToSpelllist($event, index)"
         ></add-card>
         }
       </div>

+ 19 - 0
src/app/journal/journal-spellcards/journal-spellcards.component.scss

@@ -5,6 +5,25 @@
   overflow-y: auto;
   display: flex;
   flex-direction: column;
+  position: relative;
+}
+
+.manage-spells {
+  position: absolute;
+  right: 2rem;
+  top: 1rem;
+  font-size: 1.25rem;
+  font-weight: 600;
+  display: flex;
+  align-items: center;
+  gap: 0.5rem;
+  border-radius: 10px;
+  padding: 0.5rem 1rem;
+  box-shadow: var(--shadow);
+  background: var(--edit);
+  &:hover {
+    background: var(--edit-hover);
+  }
 }
 
 .example-container {

+ 73 - 15
src/app/journal/journal-spellcards/journal-spellcards.component.ts

@@ -1,9 +1,6 @@
 import { Component } from '@angular/core';
 import {
   CdkDragDrop,
-  CdkDrag,
-  CdkDropList,
-  CdkDropListGroup,
   moveItemInArray,
   transferArrayItem,
 } from '@angular/cdk/drag-drop';
@@ -12,13 +9,8 @@ import { DataService } from 'src/services/data/data.service';
 import { ModalService } from 'src/services/modal/modal.service';
 import { SpellsService } from 'src/services/spells/spells.service';
 import { SpellModalComponent } from 'src/app/journal/spell-modal/spell-modal.component';
-import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
-import { Observable, OperatorFunction } from 'rxjs';
-import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators';
-import { FormsModule } from '@angular/forms';
-import { JsonPipe } from '@angular/common';
-import { FormControl } from '@angular/forms';
 import { FullSpellcardComponent } from 'src/app/shared-components/full-spellcard/full-spellcard.component';
+import { CustomSpellsModalComponent } from './custom-spells-modal/custom-spells-modal.component';
 
 @Component({
   selector: 'app-journal-spellcards',
@@ -53,9 +45,9 @@ export class JournalSpellcardsComponent {
   public draggingIndex: number | undefined;
 
   public constructor(
-    public dataAccessor: DataService,
+    private dataAccessor: DataService,
     private modalAccessor: ModalService,
-    private spellsService: SpellsService
+    public spellsService: SpellsService
   ) {
     this.loadSpells();
     this.hideEmptySpelllists();
@@ -160,33 +152,94 @@ export class JournalSpellcardsComponent {
     const resultSubscription = this.modalAccessor.result$.subscribe(
       (result) => {
         if (result.state === 'add') {
-          console.warn('Add spell: ', result.data);
-
           this.addSpell(result.data, level);
           // this.spellsService.addCustomSpell(result.data);
           this.dataAccessor.addCustomSpell(result.data);
         } else {
-          console.warn('Result state from modal: ' + result.state);
         }
         resultSubscription.unsubscribe();
       }
     );
   }
 
-  public handleSpellSelection(spell: Spell, level: number): void {
+  public openManageCustomSpellsModal(): void {
+    this.modalAccessor.openModal(CustomSpellsModalComponent, {
+      spells: this.dataAccessor.customSpells,
+    });
+    const resultSubscription = this.modalAccessor.result$.subscribe(
+      (result) => {
+        console.warn('CustomSpellsModalComponent: result', result);
+
+        if (result.state === 'delete') {
+          result.data.forEach((spell: Spell) => {
+            this.deleteCustomSpell(spell);
+          });
+        }
+        resultSubscription.unsubscribe();
+      }
+    );
+  }
+
+  /**
+   * Deletes a custom spell from the custom spells list.
+   * It is deleted in the spells and data service.
+   * It is also removed from the prepared and favorite spells list if present.
+   * @param spell
+   */
+  public deleteCustomSpell(spell: Spell): void {
+    console.log(spell);
+    this.dataAccessor.deleteCustomSpell(spell);
+    this.spellsService.deleteCustomSpell(spell);
+
+    let list = this.getSpellList(spell.level);
+    const index = list.findIndex((spellList) => spellList.id === spell.id);
+    // const index = list.indexOf(spell);
+    console.log('liste: ', list);
+    console.log('Index of the spell to delete: ', index);
+
+    if (index > -1) {
+      list.splice(index, 1);
+      this.dataAccessor.removeFavoriteSpell(spell);
+      this.updateSpellsInDatabase(spell.level);
+    }
+  }
+
+  /**
+   * Adds a given spell to the spelllist of a specific level.
+   * @param spell The spell to add.
+   * @param level The level the spell needs to be added to.
+   */
+  public addSpellToSpelllist(spell: Spell, level: number): void {
     this.addSpell(spell, level);
   }
 
+  /**
+   * Adds a given spell to the spell list specified by level.
+   * Also updates the spell list in the database.
+   * @param spell The spell to add.
+   * @param level The level to add the spell to.
+   */
   public addSpell(spell: Spell, level: number) {
     this.getSpellList(level).push(spell);
     this.updateSpellsInDatabase(level);
   }
 
+  /**
+   * Overrides a spell in a given spell list, specified by the index.
+   * @param spell The new spell.
+   * @param level The level to add the spell to.
+   * @param index The index at which the spell should be overridden.
+   */
   public updateSpell(spell: Spell, level: number, index: number): void {
     this.getSpellList(level)![index] = spell;
     this.updateSpellsInDatabase(level);
   }
 
+  /**
+   * Returns the reference to the spell list specified by the level.
+   * @param level Specifies the level of the spell list.
+   * @returns Returns the spell list specified by the level.
+   */
   public getSpellList(level: number): Spell[] {
     switch (level) {
       case 0:
@@ -215,6 +268,11 @@ export class JournalSpellcardsComponent {
     }
   }
 
+  /**
+   * A lookup function to match spell levels from 0-9 to strings eg: 2 => "Level 2".
+   * @param level The level that is looked up.
+   * @returns Returns a string with the name to display in the view.
+   */
   public getSpellLevel(level: number): string {
     switch (level) {
       case 0:

+ 1 - 1
src/app/journal/journal-spellcards/spellcard/spellcard.component.html

@@ -13,7 +13,7 @@
   </div>
   <img
     class="spell-image"
-    [src]="'assets/images/spells/' + spell.english + '.jpg'"
+    [src]="'assets/images/spells/' + spell.image + '.jpg'"
     (error)="setBackupImage($event)"
     [style.box-shadow]="'var(--' + spell.school.toLowerCase() + '-border)'"
     alt="spell image"

+ 4 - 21
src/app/journal/journal-spellcards/spellcard/spellcard.component.ts

@@ -10,32 +10,15 @@ import { TranslatorService } from 'src/services/translator/translator.service';
 export class SpellcardComponent {
   @Input() public spell!: Spell;
   public schools: any;
-  public backgroundColors: any = {
-    Abjuration: '#F0E3CE', //final
-    Conjuration: '#26714a', //final
-    Divination: '#F0E3CE', //final
-    Enchantment: '#629179', //final
-    Evocation: '#a46a7d',
-    Illusion: '#69a', //final
-    Necromancy: '#000000', //final
-    Transmutation: '#69a', //final
-  };
-
-  public borders: any = {
-    Abjuration: '0 0 0 2px #171314, 0 0 0 5px #FFFFFF',
-    Conjuration: '0 0 0 2px #171314, 0 0 0 5px #26714a', //final
-    Divination: '0 0 0 2px #171314, 0 0 0 5px #FFFFFF',
-    Enchantment: '0 0 0 2px #171314, 0 0 0 5px #26714a', //final
-    Evocation: '0 0 0 2px #171314, 0 0 0 5px #863f57',
-    Illusion: '0 0 0 2px #171314, 0 0 0 5px #2d6475', //final
-    Necromancy: '0 0 0 2px #171314, 0 0 0 5px #36353a', //final
-    Transmutation: '0 0 0 2px #171314, 0 0 0 5px #2d6475', //final
-  };
 
   public constructor(public translator: TranslatorService) {
     this.schools = translator.schools;
   }
 
+  public ngOnInit(): void {
+    console.log(this.spell);
+  }
+
   public setBackupImage(event: any): void {
     event.target.src = 'assets/images/spells/backup.jpg';
   }

+ 14 - 14
src/app/journal/journal-stats/ability-panel/proficiencies-table/proficiencies-table.component.html

@@ -7,33 +7,33 @@
         </mat-expansion-panel-header>
         <form [formGroup]="proficiencies" (change)="updateDatabase()">
           <div class="centered-line">
-            <input type="checkbox" formControlName="light" />
-            <label>Leichte Rüstungen</label>
+            <input id="light" type="checkbox" formControlName="light" />
+            <label for="light">Leichte Rüstungen</label>
           </div>
           <div class="centered-line">
-            <input type="checkbox" formControlName="medium" />
-            <label>Mittlere Rüstungen</label>
+            <input id="medium" type="checkbox" formControlName="medium" />
+            <label for="medium">Mittlere Rüstungen</label>
           </div>
           <div class="centered-line">
-            <input type="checkbox" formControlName="heavy" />
-            <label>Schwere Rüstungen</label>
+            <input id="heavy" type="checkbox" formControlName="heavy" />
+            <label for="heavy">Schwere Rüstungen</label>
           </div>
           <div class="centered-line">
-            <input type="checkbox" formControlName="shields" />
-            <label>Schilde</label>
+            <input id="shields" type="checkbox" formControlName="shields" />
+            <label for="shields">Schilde</label>
           </div>
           <div class="centered-line">
-            <input type="checkbox" formControlName="simple" />
-            <label>Einfache Waffen</label>
+            <input id="simple" type="checkbox" formControlName="simple" />
+            <label for="simple">Einfache Waffen</label>
           </div>
 
           <div class="centered-line">
-            <input type="checkbox" formControlName="martial" />
-            <label>Schwere Waffen</label>
+            <input id="martial" type="checkbox" formControlName="martial" />
+            <label for="martial">Schwere Waffen</label>
           </div>
           <div class="centered-line">
-            <label>Weitere Waffen</label>
-            <input formControlName="other" />
+            <label for="other">Weitere Waffen</label>
+            <input id="other" formControlName="other" />
           </div>
         </form>
       </mat-expansion-panel>

+ 2 - 2
src/app/journal/journal-stats/weapons-container/spell-table/favorite-spells-modal/favorite-spells-modal.component.html

@@ -91,7 +91,7 @@
 <ng-template #costTemplate let-spell="spell">
   <div class="bold">
     <span *ngIf="spell.cost === 'action'">A</span>
-    <span *ngIf="spell.cost === 'bonus'">B</span>
+    <span *ngIf="spell.cost === 'bonus action'">B</span>
     <span *ngIf="spell.cost === 'reaction'">R</span>
   </div>
 </ng-template>
@@ -163,7 +163,7 @@
     <div *ngIf="!spell.isRanged">Berührung</div>
 
     <div *ngIf="spell.hasAreaOfEffect">
-      <span>{{ spell.radius }} ft. {{ areas[spell.areaOfEffectType] }} </span>
+      <span>{{ spell.diameter }} ft. {{ areas[spell.areaOfEffectType] }} </span>
     </div>
   </div>
 </ng-template>

+ 1 - 1
src/app/journal/journal-stats/weapons-container/spell-table/spell-details/spell-details.component.html

@@ -22,7 +22,7 @@
   <div class="details-content-small">
     <div>Reichweite: {{ spell.range }} ft.</div>
     <div *ngIf="spell.hasAreaOfEffect">
-      Flächeneffekt: {{ spell.radius }} ft. {{ spell.areaOfEffectType }}
+      Flächeneffekt: {{ spell.diameter }} ft. {{ spell.areaOfEffectType }}
     </div>
   </div>
 

+ 2 - 2
src/app/journal/journal-stats/weapons-container/weapon-table/weapon-details/weapon-details.component.html

@@ -12,9 +12,9 @@
 <div class="flex-row">
   <div class="label">Schaden:</div>
   @for(damage of weapon?.damage; let index = $index; track damage){
-  {{ damage.diceNumber }} {{ damage.diceType }}
+  {{ damage.diceNumber }} {{ damage.diceType }} &nbsp;
   <span *ngIf="index === 0 && damageModifier !== '0'">
-    {{ damageModifier }}
+    {{ damageModifier }} &nbsp;
   </span>
   {{ damageTranslator[damage.damageType] }}
 

+ 28 - 5
src/app/journal/journal-stats/weapons-container/weapon-table/weapon-modal/weapon-modal.component.html

@@ -3,7 +3,7 @@
     @if(isUpdate){Waffe bearbeiten} @else{Waffe hinzufügen}
   </div>
 
-  <div class="content">
+  <div class="content b-0">
     <div class="input-label">Name</div>
     <mat-form-field appearance="outline" class="w-100">
       <input matInput [(ngModel)]="name" />
@@ -16,6 +16,14 @@
           <input id="proficient" type="checkbox" [(ngModel)]="proficient" />
           <label for="proficient">Geübt</label>
         </div>
+        <div class="checkbox-row">
+          <input
+            id="useAttributeModifier"
+            type="checkbox"
+            [(ngModel)]="useAttributeModifier"
+          />
+          <label for="useAttributeModifier">Attributmodifikator anwenden</label>
+        </div>
         <div class="checkbox-row">
           <input id="finesse" type="checkbox" [(ngModel)]="isFinesse" />
           <label for="finesse">Finesse</label>
@@ -47,6 +55,14 @@
           <input id="isMagical" type="checkbox" [(ngModel)]="isMagical" />
           <label for="isMagical">Magisch</label>
         </div>
+        <div class="checkbox-row">
+          <input
+            id="additionalDamage"
+            type="checkbox"
+            [(ngModel)]="hasAdditionalDamage"
+          />
+          <label for="additionalDamage">Zusatzschaden</label>
+        </div>
       </div>
     </div>
 
@@ -97,7 +113,7 @@
 
               <div class="damage-box">
                 <div class="subheading left t-025">
-                  @if(index == 0){Schaden} @else {Zusatzschaden}
+                  @if(index == 0){Schaden} @else {Weiterer Schaden}
                 </div>
                 <div class="input-label">Anzahl Würfel</div>
                 <mat-form-field appearance="outline">
@@ -141,6 +157,15 @@
                   [icon]="'delete'"
                   (click)="removeDamage(index)"
                 ></icon-button>
+                } @else if (hasAdditionalDamage){
+                <div class="input-label t-05">Zusatzschaden</div>
+                <mat-form-field appearance="outline">
+                  <mat-select [(ngModel)]="additionalDamage">
+                    @for (damage of additonalDamages; track damage) {
+                    <mat-option [value]="damage"> +{{ damage }} </mat-option>
+                    }
+                  </mat-select>
+                </mat-form-field>
                 }
               </div>
               @if(damage.length < 2){
@@ -248,7 +273,7 @@
       ></div>
     </div>
 
-    <hr />
+    <hr style="margin-bottom: 0" />
   </div>
 
   <div class="horizontal-buttons">
@@ -266,5 +291,3 @@
     </ui-button>
   </div>
 </div>
-
-Hallo, was geht ab ? Ich möchte gerne

+ 1 - 3
src/app/journal/journal-stats/weapons-container/weapon-table/weapon-modal/weapon-modal.component.scss

@@ -41,6 +41,7 @@
 
     input {
         -moz-appearance: textfield;
+        appearance: textfield;
     }
 
     .input-value {
@@ -73,16 +74,13 @@ div.nav-pills.flex-column.nav {
     border-right: 1px solid rgba(0, 0, 0, 0.125);
     padding-right: 4px;
     width: 11rem;
-    // box-shadow: var(--shadow);
 
     .nav-link {
         width: 100%;
         border-radius: 10px;
         transition: all 0.25s ease-in-out;
-        // font-size: 1.25rem;
         font-weight: 600;
         color: var(--text);
-        // padding: 0;
 
         &.active,
         &:hover {

+ 14 - 0
src/app/journal/journal-stats/weapons-container/weapon-table/weapon-modal/weapon-modal.component.ts

@@ -20,6 +20,9 @@ export class WeaponModalComponent {
   public throwRange: number[] = [5, 5];
   public attackBonus: string = '+0';
   public damage: Damage[] = [{ diceNumber: '', diceType: '', damageType: '' }];
+  public hasAdditionalDamage: boolean = false;
+  public additionalDamage: number = 0;
+  public useAttributeModifier: boolean = false;
   public proficient: boolean = false;
   public isVersatile: boolean = false;
   public isTwoHanded: boolean = false;
@@ -102,6 +105,8 @@ export class WeaponModalComponent {
     '+20',
   ];
 
+  additonalDamages: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+
   public magicBonuses: any[] = [
     { display: '+1', value: 1 },
     { display: '+2', value: 2 },
@@ -123,6 +128,9 @@ export class WeaponModalComponent {
     this.throwRange = this.item.throwRange;
     this.attackBonus = this.item.attackBonus;
     this.damage = this.item.damage;
+    this.hasAdditionalDamage = this.item.hasAdditionalDamage;
+    this.additionalDamage = this.item.additionalDamage;
+    this.useAttributeModifier = this.item.useAttributeModifier;
     this.proficient = this.item.proficient;
     this.isVersatile = this.item.isVersatile;
     this.isTwoHanded = this.item.isTwoHanded;
@@ -162,6 +170,9 @@ export class WeaponModalComponent {
       throwRange: this.throwRange,
       attackBonus: this.attackBonus,
       damage: this.damage,
+      hasAdditionalDamage: this.hasAdditionalDamage,
+      additionalDamage: this.additionalDamage,
+      useAttributeModifier: this.useAttributeModifier,
       proficient: this.proficient,
       isVersatile: this.isVersatile,
       isTwoHanded: this.isTwoHanded,
@@ -186,6 +197,9 @@ export class WeaponModalComponent {
     this.throwRange = [5, 5];
     this.attackBonus = '+0';
     this.damage = [{ diceNumber: '', diceType: '', damageType: '' }];
+    this.hasAdditionalDamage = false;
+    this.additionalDamage = 0;
+    this.useAttributeModifier = false;
     this.proficient = false;
     this.isVersatile = false;
     this.isTwoHanded = false;

+ 12 - 7
src/app/journal/journal-stats/weapons-container/weapon-table/weapon-table.component.ts

@@ -71,19 +71,24 @@ export class WeaponTableComponent {
   }
 
   private calculateSingleDamageModifier(index: number): void {
-    let value: number;
+    let value: number = 0;
     let bonus: number = 0;
     const weapon: Weapon = this.weapons[index];
-    if (this.isMonk || weapon.isFinesse) {
-      value = Math.max(this.strengthValue, this.dexterityValue);
-    } else if (weapon.isRanged) {
-      value = this.dexterityValue;
-    } else {
-      value = this.strengthValue;
+    if (weapon.useAttributeModifier) {
+      if (this.isMonk || weapon.isFinesse) {
+        value = Math.max(this.strengthValue, this.dexterityValue);
+      } else if (weapon.isRanged) {
+        value = this.dexterityValue;
+      } else {
+        value = this.strengthValue;
+      }
     }
     if (weapon.isMagical) {
       bonus = weapon.magicBonus!;
     }
+    if (weapon.hasAdditionalDamage) {
+      bonus += weapon.additionalDamage!;
+    }
     this.damageModifiers[index] = this.calculateModifier(value, bonus);
   }
 

+ 2 - 0
src/app/journal/journal.module.ts

@@ -94,6 +94,7 @@ import { BackgroundComponent } from './journal-character/background/background.c
 import { StoryComponent } from './journal-character/story/story.component';
 import { FavoriteSpellsModalComponent } from './journal-stats/weapons-container/spell-table/favorite-spells-modal/favorite-spells-modal.component';
 import { MatRippleModule } from '@angular/material/core';
+import { CustomSpellsModalComponent } from './journal-spellcards/custom-spells-modal/custom-spells-modal.component';
 
 @NgModule({
   declarations: [
@@ -171,6 +172,7 @@ import { MatRippleModule } from '@angular/material/core';
     BackgroundComponent,
     StoryComponent,
     FavoriteSpellsModalComponent,
+    CustomSpellsModalComponent,
   ],
   imports: [
     CommonModule,

+ 345 - 111
src/app/journal/spell-modal/spell-modal.component.html

@@ -1,78 +1,334 @@
-<div class="modal-dimensions">
-  <h2 style="text-align: center">
+<div class="dimensions">
+  <div class="title">
     @if(isModification){Zauber bearbeiten}@else{Zauber erstellen}
-  </h2>
-
-  <div class="add-form-group">
-    <div class="input-element">
-      <label for="weaponName">Name</label>
-      <input
-        type="text"
-        class="add-input"
-        id="weaponName"
-        [(ngModel)]="german"
-      />
-    </div>
-    <div class="form-element-row">
-      <!-- general -->
-      <div class="checkbox-element">
-        <input type="checkbox" [(ngModel)]="needsVerbal" />
-        <label>Verbal</label>
-      </div>
-      <div class="checkbox-element">
-        <input type="checkbox" [(ngModel)]="needsSomatic" />
-        <label>Geste</label>
-      </div>
-      <div class="checkbox-element">
-        <input type="checkbox" [(ngModel)]="needsMaterial" />
-        <label>Material</label>
-      </div>
+  </div>
 
-      <div class="checkbox-element">
-        <input type="checkbox" [(ngModel)]="needsConcentration" />
-        <label>Konzentration</label>
-      </div>
+  <div class="content b-0">
+    <div class="input-label">Name</div>
+    <mat-form-field appearance="outline" class="w-100">
+      <input matInput [(ngModel)]="name" />
+    </mat-form-field>
+    <hr />
 
-      <div class="checkbox-element">
-        <select [(ngModel)]="cost">
-          <option *ngFor="let cost of costs" [value]="cost.value">
-            {{ cost.display }}
-          </option>
-        </select>
-        <label>Kosten</label>
+    <div class="flex-row t-1">
+      <div class="checkbox-column">
+        <div class="checkbox-row">
+          <input id="verbal" type="checkbox" [(ngModel)]="needsVerbal" />
+          <label for="verbal">Verbal</label>
+        </div>
+        <div class="checkbox-row">
+          <input id="somatic" type="checkbox" [(ngModel)]="needsSomatic" />
+          <label for="somatic">Geste</label>
+        </div>
+        <div class="checkbox-row">
+          <input id="material" type="checkbox" [(ngModel)]="needsMaterial" />
+          <label for="material">Material</label>
+        </div>
+        <div class="checkbox-row">
+          <input
+            id="concentration"
+            type="checkbox"
+            [(ngModel)]="needsConcentration"
+          />
+          <label for="concentration">Konzentration</label>
+        </div>
+
+        <div class="checkbox-row">
+          <input
+            id="canRitual"
+            type="checkbox"
+            (change)="unsetRitual($event)"
+            [(ngModel)]="canRitual"
+          />
+          <label for="canRitual">Ritual möglich</label>
+        </div>
+        <div class="checkbox-row">
+          <input
+            id="isRitual"
+            type="checkbox"
+            (change)="setRitual($event)"
+            [(ngModel)]="isRitual"
+          />
+          <label for="isRitual">Ist Ritual</label>
+        </div>
       </div>
+      <div class="checkbox-column">
+        <div class="checkbox-row">
+          <input id="doesDamage" type="checkbox" [(ngModel)]="doesDamage" />
+          <label for="doesDamage">Schaden</label>
+        </div>
+        <div class="checkbox-row">
+          <input id="doesHeal" type="checkbox" [(ngModel)]="doesHeal" />
+          <label for="doesHeal">Heilung</label>
+        </div>
+        <div class="checkbox-row">
+          <input id="isRanged" type="checkbox" [(ngModel)]="isRanged" />
+          <label for="isRanged">Hat Reichweite</label>
+        </div>
+        <div class="checkbox-row">
+          <input
+            id="hasAreaOfEffect"
+            type="checkbox"
+            [(ngModel)]="hasAreaOfEffect"
+          />
+          <label for="hasAreaOfEffect">Hat Flächeneffekt</label>
+        </div>
 
-      <div class="checkbox-element">
-        <input type="checkbox" [(ngModel)]="canRitual" />
-        <label>Ritual möglich?</label>
+        <div class="checkbox-row">
+          <input
+            id="needsSavingThrow"
+            type="checkbox"
+            [(ngModel)]="needsSavingThrow"
+          />
+          <label for="needsSavingThrow">Erfordert Rettungswurf</label>
+        </div>
+        <div class="checkbox-row">
+          @if(!needsSavingThrow){
+          <input
+            id="needsAttackRoll"
+            type="checkbox"
+            [(ngModel)]="needsAttackRoll"
+          />
+          <label for="needsAttackRoll">Erfordert Angriffswurf</label>
+          }@else {
+          <label>Attribut</label>
+          <select [(ngModel)]="savingThrowAttribute" *ngIf="needsSavingThrow">
+            <option
+              *ngFor="let attribute of savingThrowAttributes"
+              [value]="attribute.value"
+            >
+              {{ attribute.display }}
+            </option>
+          </select>
+          }
+        </div>
       </div>
 
-      <div class="checkbox-element">
-        <select [(ngModel)]="level">
-          <option
-            *ngFor="let level of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]"
-            [value]="level"
-          >
-            @if(level == 0){Zaubertrick}@else{ {{ level }}}
-          </option>
-        </select>
-        <label>Stufe</label>
+      <div class="checkbox-column">
+        <div class="input-row">
+          <label>Kosten</label>
+          <mat-form-field appearance="outline">
+            <mat-select [(ngModel)]="cost">
+              @for (cost of costs; track cost) {
+              <mat-option [value]="cost.value">{{ cost.display }}</mat-option
+              >}
+            </mat-select>
+          </mat-form-field>
+        </div>
+
+        <div class="input-row duration">
+          <label>Dauer</label>
+          <mat-form-field appearance="outline">
+            <input type="number" matInput [(ngModel)]="duration" />
+            <span matSuffix class="suffix">Runden</span>
+          </mat-form-field>
+        </div>
+
+        <div class="input-row">
+          <label>Stufe</label>
+          <mat-form-field appearance="outline">
+            <mat-select [(ngModel)]="level">
+              @for (level of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; track level) {
+              <mat-option [value]="level">{{ level }}</mat-option
+              >}
+            </mat-select>
+          </mat-form-field>
+        </div>
+        <div class="input-row">
+          <label>Schule</label>
+          <mat-form-field appearance="outline">
+            <mat-select [(ngModel)]="school">
+              @for (school of schools; track school) {
+              <mat-option [value]="school.value">{{
+                school.display
+              }}</mat-option
+              >}
+            </mat-select>
+          </mat-form-field>
+        </div>
       </div>
+    </div>
 
-      <div class="checkbox-element">
-        <select [(ngModel)]="school">
-          <option *ngFor="let school of schools" [value]="school.value">
-            {{ school.display }}
-          </option>
-        </select>
-        <label>Schule</label>
+    <hr />
+
+    <!-- TAB-PANEL -->
+    <div class="d-flex">
+      <div
+        ngbNav
+        #nav="ngbNav"
+        [(activeId)]="active"
+        class="flex-column navigation-bar"
+        orientation="vertical"
+      >
+        <!-- DAMAGE -->
+        <ng-container ngbNavItem="damage">
+          @if(doesDamage){
+          <button ngbNavLink>Schaden</button>
+          }@else {
+          <button class="disabled-button" disabled>Schaden</button>
+          }
+          <ng-template ngbNavContent>
+            <div class="tab-content flex-row t-05">
+              @for(damageEntry of damage; let index = $index; track
+              damageEntry){
+
+              <div class="damage-box">
+                <div class="subheading left t-025">
+                  @if(index == 0){Schaden} @else {Weiterer Schaden}
+                </div>
+                <div class="input-label">Anzahl Würfel</div>
+                <mat-form-field appearance="outline">
+                  <mat-select [(ngModel)]="damageEntry.diceNumber">
+                    @for (number of numbers; track number) {
+                    <mat-option [value]="number"> {{ number }} </mat-option>}
+                  </mat-select>
+                </mat-form-field>
+
+                <div class="input-label t-05">Würfelart</div>
+                <mat-form-field appearance="outline">
+                  <mat-select [(ngModel)]="damageEntry.diceType">
+                    @for (die of dice; track die) {
+                    <mat-option [value]="die"> {{ die }} </mat-option>}
+                  </mat-select>
+                </mat-form-field>
+
+                <div class="input-label t-05">Schadensart</div>
+                <mat-form-field appearance="outline">
+                  <mat-select [(ngModel)]="damageEntry.damageType">
+                    @for (type of damageTypes; track type) {
+                    <mat-option [value]="type.value">
+                      {{ type.display }}
+                    </mat-option>
+                    }
+                  </mat-select>
+                </mat-form-field>
+                @if(index !== 0){
+                <icon-button
+                  [icon]="'delete'"
+                  (click)="removeDamage(index)"
+                ></icon-button>
+                }
+              </div>
+              @if(damage.length < 2){
+              <icon-button
+                [icon]="'add'"
+                style="margin-top: 7rem; margin-left: 5rem"
+                (click)="addDamage()"
+              ></icon-button>
+              } }
+            </div>
+          </ng-template>
+        </ng-container>
+        <!-- HEAL -->
+        <ng-container ngbNavItem="heal">
+          @if(doesHeal){
+          <button ngbNavLink>Heilung</button>} @else {
+          <button class="disabled-button" disabled>Heilung</button>
+          }
+          <ng-template ngbNavContent>
+            <div class="tab-content t-05">
+              <div class="input-label">Würfelanzahl</div>
+              <mat-form-field appearance="outline">
+                <mat-select [(ngModel)]="heal.diceNumber">
+                  @for (number of numbers; track number) {
+                  <mat-option [value]="number">{{ number }}</mat-option
+                  >}
+                </mat-select>
+              </mat-form-field>
+
+              <div class="input-label">Würfelart</div>
+              <mat-form-field appearance="outline">
+                <mat-select [(ngModel)]="heal.diceType">
+                  @for (die of dice; track die) {
+                  <mat-option [value]="die">{{ die }}</mat-option
+                  >}
+                </mat-select>
+              </mat-form-field>
+            </div>
+          </ng-template>
+        </ng-container>
+        <!-- RANGE -->
+        <ng-container ngbNavItem="range">
+          @if(isRanged || hasAreaOfEffect){
+          <button ngbNavLink>Reichweite</button>
+          } @else {
+          <button class="disabled-button" disabled>Reichweite</button>
+          }
+          <ng-template ngbNavContent>
+            <div class="range-container">
+              <div class="input-label">Reichweite</div>
+              <mat-form-field appearance="outline">
+                <input type="number" matInput [(ngModel)]="range" />
+                <span class="suffix" matTextSuffix>Fuß</span>
+              </mat-form-field>
+              @if(hasAreaOfEffect) {
+              <div class="input-label">Flächenart</div>
+              <mat-form-field appearance="outline">
+                <mat-select [(ngModel)]="areaOfEffectType">
+                  @for (areaType of areaTypes; track areaType) {
+                  <mat-option [value]="areaType.value">{{
+                    areaType.display
+                  }}</mat-option
+                  >}
+                </mat-select>
+              </mat-form-field>
+              <div class="input-label">Durchmesser</div>
+              <mat-form-field appearance="outline">
+                <input type="number" matInput [(ngModel)]="diameter" />
+                <span class="suffix" matTextSuffix>Fuß</span>
+              </mat-form-field>
+              }
+            </div>
+          </ng-template>
+        </ng-container>
+        <!-- DESCRIPTION -->
+        <ng-container ngbNavItem="description">
+          <button ngbNavLink>Beschreibung</button>
+          <ng-template ngbNavContent>
+            <div class="NgxEditor__Wrapper">
+              <ngx-editor-menu [editor]="editor" [toolbar]="toolbar">
+              </ngx-editor-menu>
+              <ngx-editor
+                [editor]="editor"
+                [(ngModel)]="description_de"
+                placeholder="Beschreibung des Zaubers"
+              ></ngx-editor>
+            </div>
+          </ng-template>
+        </ng-container>
       </div>
+
+      <div
+        [ngbNavOutlet]="nav"
+        style="
+          width: 100%;
+          min-height: 20rem;
+          max-height: 26rem;
+          overflow-y: auto;
+          overflow-x: hidden;
+        "
+        class="ms-4"
+      ></div>
     </div>
 
-    <!-- ranged and area -->
-    <h3 style="text-align: center">Reichweite und Fläche</h3>
+    <hr class="b-0" />
+  </div>
+
+  <!-- <div class="horizontal-buttons">
+    @if(isModification){
+    <ui-button [color]="'green'" style="width: 40%" (click)="update()">
+      Anpassen
+    </ui-button>
+    }@else{
+    <ui-button [color]="'green'" style="width: 40%" (click)="add()">
+      Erstellen
+    </ui-button>
+    }
+    <ui-button [color]="'red'" style="width: 40%" (click)="cancel()">
+      Abbrechen
+    </ui-button> -->
 
-    <div class="range-area-container">
+  <!-- <div class="range-area-container">
       <div class="range-box">
         <div class="checkbox-element">
           <input type="checkbox" [(ngModel)]="isRanged" />
@@ -90,8 +346,8 @@
           <label>Flächeneffekt</label>
         </div>
         <div class="input-element" *ngIf="hasAreaOfEffect">
-          <input type="text" class="add-input" [(ngModel)]="radius" />
-          <label>Radius</label>
+          <input type="text" class="add-input" [(ngModel)]="diameter" />
+          <label>diameter</label>
         </div>
         <div class="checkbox-element" *ngIf="hasAreaOfEffect">
           <select [(ngModel)]="areaOfEffectType">
@@ -105,16 +361,6 @@
     </div>
 
     <div style="display: flex">
-      <div>
-        <input type="checkbox" [(ngModel)]="doesDamage" />
-        <label>Schaden?</label>
-      </div>
-
-      <div>
-        <input type="checkbox" [(ngModel)]="doesHeal" />
-        <label>Heilung?</label>
-      </div>
-
       <div>
         <input type="checkbox" [(ngModel)]="needsSavingThrow" />
         <label>Rettungswurf?</label>
@@ -124,9 +370,9 @@
         <input type="checkbox" [(ngModel)]="needsAttackRoll" />
         <label>Angriff?</label>
       </div>
-    </div>
+    </div> -->
 
-    <ng-container
+  <!-- <ng-container
       *ngIf="needsSavingThrow"
       [ngTemplateOutlet]="attackTabContent"
     ></ng-container>
@@ -141,39 +387,27 @@
       [ngTemplateOutlet]="healTabContent"
     ></ng-container>
 
-    <ng-container [ngTemplateOutlet]="descriptionTabContent"></ng-container>
-
-    <!-- Button section -->
-    <div class="button-wrapper-2-block">
-      @if(isModification){
-      <ui-button
-        [type]="'update'"
-        [size]="'xlarge'"
-        [color]="'primary'"
-        (click)="update()"
-      ></ui-button>
-      }@else{
-      <ui-button
-        *ngIf="!isModification"
-        [type]="'add'"
-        [size]="'xlarge'"
-        [color]="'primary'"
-        (click)="add()"
-      ></ui-button>
-      }
-      <ui-button
-        [type]="'cancel'"
-        [size]="'xlarge'"
-        [color]="'primary'"
-        (click)="cancel()"
-      ></ui-button>
-    </div>
+    <ng-container [ngTemplateOutlet]="descriptionTabContent"></ng-container> -->
+
+  <!-- Button section -->
+  <div class="horizontal-buttons">
+    <ui-button
+      [color]="'green'"
+      style="width: 40%"
+      (click)="isModification ? update() : add()"
+    >
+      @if(isModification){Anwenden}@else{Erstellen}
+    </ui-button>
+    <ui-button [color]="'red'" style="width: 40%" (click)="cancel()"
+      >Abbrechen
+    </ui-button>
   </div>
 </div>
+<!-- </div> -->
 
 <!-- templates -->
 
-<ng-template #attackTabContent>
+<!-- <ng-template #attackTabContent>
   <div>
     <select [(ngModel)]="savingThrowAttribute" *ngIf="needsSavingThrow">
       <option
@@ -185,9 +419,9 @@
     </select>
     <label *ngIf="needsSavingThrow">Attribut</label>
   </div>
-</ng-template>
+</ng-template> -->
 
-<ng-template #damageTabContent>
+<!-- <ng-template #damageTabContent>
   <div class="damage-container">
     <div class="damage-box" *ngFor="let damage of damage; let index = index">
       <div class="dice-row">
@@ -235,9 +469,9 @@
       [class]="'pointer'"
     ></icon>
   </div>
-</ng-template>
+</ng-template> -->
 
-<ng-template #healTabContent>
+<!-- <ng-template #healTabContent>
   <div class="heal-container">
     <div class="dice-row">
       <div class="flex-column">
@@ -260,8 +494,8 @@
     </div>
     <label>Heilung</label>
   </div>
-</ng-template>
+</ng-template> -->
 
-<ng-template #descriptionTabContent>
+<!-- <ng-template #descriptionTabContent>
   <textarea style="height: 10rem" [(ngModel)]="description_de"></textarea>
-</ng-template>
+</ng-template> -->

+ 84 - 71
src/app/journal/spell-modal/spell-modal.component.scss

@@ -1,95 +1,108 @@
-.modal-dimensions {
-    width: 40vw;
-    background-color: antiquewhite;
-    border-radius: 10px;
-    border: 1px solid var(--border-color);
-    padding: 1rem;
-}
-
-.range-area-container {
-    display: flex;
-    flex-direction: row;
-}
-
-.range-box,
-.area-box {
+.checkbox-column {
+    width: 33.3%;
     display: flex;
     flex-direction: column;
-    flex-basis: 45%;
-    gap: 1rem;
+    gap: 0.5rem;
+    padding-left: 0.5rem;
 }
 
-.add-form-group {
+.checkbox-row {
     display: flex;
-    flex-direction: column;
+    flex-direction: row;
     gap: 1rem;
-}
-.input-element {
-    display: flex;
-    flex-direction: column;
-    // gap: 0.1rem;
-    // align-items: center;
-    // justify-content: center;
+    align-items: center;
 }
 
-.checkbox-element {
+.input-row {
     display: flex;
-    flex-direction: column;
+    flex-direction: row;
+    justify-content: space-between;
     align-items: center;
-    justify-content: center;
-    flex-basis: 33.33%;
 }
 
-.form-element-row {
-    display: flex;
-    flex-direction: row;
-    flex-wrap: wrap;
-    justify-content: space-around;
-    text-align: center;
-    row-gap: 1rem;
+.damage-box {
+    width: 50%;
 }
 
-.damage-container {
-    display: flex;
-    flex-direction: row;
-    gap: 1rem;
-    // align-items: center;
-    // justify-content: center;
+.damage-box {
+    icon-button {
+        padding-left: 4rem;
+        margin-bottom: 1rem;
+        margin-top: 0.5rem;
+    }
 }
 
-.damage-row {
-    display: flex;
-    flex-direction: row;
-    justify-content: center;
-    align-items: center;
-    gap: 0.1rem;
+.flex-row,
+.range-container {
+    ::ng-deep .mat-mdc-text-field-wrapper {
+        width: 10rem !important;
+    }
+    ::ng-deep .mat-mdc-form-field-infix {
+        height: 32px !important;
+        min-height: 32px !important;
+        padding-top: 4px !important;
+    }
+    input::-webkit-outer-spin-button,
+    input::-webkit-inner-spin-button {
+        display: none;
+    }
+
+    input {
+        -moz-appearance: textfield;
+        appearance: textfield;
+    }
 }
 
-.damage-box {
-    display: flex;
-    flex-direction: column;
-    align-items: left;
-    gap: 0.5rem;
-    flex-basis: 30%;
+.tab-content {
+    ::ng-deep .mat-mdc-text-field-wrapper {
+        width: 10rem !important;
+    }
+    ::ng-deep .mat-mdc-form-field-infix {
+        height: 40px !important;
+        min-height: 40px !important;
+        padding-top: 8px !important;
+    }
 }
 
-.dice-row {
-    display: flex;
-    flex-direction: row;
-    // justify-content: center;
-    // align-items: center;
-    gap: 1rem;
-    margin-bottom: 1rem;
+div.nav-pills.flex-column.nav {
+    border-right: 1px solid darkgray;
+    padding-right: 4px;
+}
+
+::ng-deep .ProseMirror {
+    height: 17.5rem !important;
+    overflow-y: auto;
 }
 
-.button-wrapper {
-    width: 100%;
-    display: grid;
-    grid-template-rows: 1fr 1fr;
-    grid-template-columns: 1fr;
-    gap: 10px;
-    margin-top: 2rem;
-    margin-top: 2rem;
+.navigation-bar {
+    padding-top: 0.5rem;
+    gap: 1rem;
+    height: 20rem;
     align-items: center;
-    justify-content: center;
+    border-right: 1px solid rgba(0, 0, 0, 0.125);
+    padding-right: 4px;
+    width: 11rem;
+
+    .nav-link {
+        width: 100%;
+        border-radius: 10px;
+        transition: all 0.25s ease-in-out;
+        font-weight: 600;
+        color: var(--text);
+
+        &.active,
+        &:hover {
+            background-color: var(--tab-active);
+            box-shadow: var(--shadow);
+        }
+    }
+
+    .disabled-button {
+        padding: 8px 0;
+        border: none;
+        font-weight: 600;
+        color: black;
+        cursor: not-allowed;
+        opacity: 0.5;
+    }
 }

+ 64 - 24
src/app/journal/spell-modal/spell-modal.component.ts

@@ -3,6 +3,7 @@ import { ModalService } from 'src/services/modal/modal.service';
 import { Damage } from 'src/interfaces/damage';
 import { Heal } from 'src/interfaces/heal';
 import { Spell } from 'src/interfaces/spell';
+import { Editor } from 'ngx-editor';
 
 @Component({
   selector: 'spell-modal',
@@ -19,18 +20,22 @@ export class SpellModalComponent {
   @Input() public isBasedOnOfficialSpell: boolean = false;
 
   // #region Properties
+  public name: string = '';
   public german: string = '';
   public english: string = '';
+  public image: string = '';
   public cost: string = 'action';
   public duration: number = 0;
   public timeToCast: number = 0;
-  public canRitual: 'true' | 'false' | 'only' = 'false';
+  public canRitual: boolean = false;
+  public isRitual: boolean = false;
   public needsConcentration: boolean = false;
   public needsVerbal: boolean = false;
   public needsSomatic: boolean = false;
   public needsMaterial: boolean = false;
-  public school: string = '';
+  public school: string = 'Evocation';
   public description_de: string = '';
+  public description_en: string = '';
   public doesDamage: boolean = false;
   public doesHeal: boolean = false;
   public needsAttackRoll: boolean = false;
@@ -46,30 +51,39 @@ export class SpellModalComponent {
   public isRanged: boolean = false;
   public range: number = 5;
   public hasAreaOfEffect: boolean = false;
-  public radius: number = 0;
-  public areaOfEffectType: string = '';
+  public diameter: number = 5;
+  public areaOfEffectType: string = 'circle';
 
+  active = 'description';
+  editor: Editor = new Editor();
+  html = '';
+  toolbar: any = [
+    // default value
+    ['bold', 'italic'],
+    ['bullet_list'],
+    [{ heading: ['h3', 'h4', 'h5', 'h6'] }],
+  ];
   // #endregion
 
   // #region OPTIONS
   public areaTypes: any[] = [
+    { display: 'Kreis', value: 'circle' },
     { display: 'Kegel', value: 'cone' },
     { display: 'Kugel', value: 'sphere' },
-    { display: 'Kreis', value: 'circle' },
     { display: 'Linie', value: 'line' },
     { display: 'Quadrat', value: 'square' },
     { display: 'Würfel', value: 'cube' },
   ];
 
   public schools: any[] = [
-    { display: 'Verwandlung', value: 'transmutation' },
-    { display: 'Verzauberung', value: 'enchantment' },
-    { display: 'Illusion', value: 'illusion' },
-    { display: 'Nekromantie', value: 'necromancy' },
-    { display: 'Beschwörung', value: 'conjuration' },
-    { display: 'Hervorrufung', value: 'evocation' },
-    { display: 'Bannmagie', value: 'abjuration' },
-    { display: 'Wahrsagerei', value: 'divination' },
+    { display: 'Verwandlung', value: 'Transmutation' },
+    { display: 'Verzauberung', value: 'Enchantment' },
+    { display: 'Illusion', value: 'Illusion' },
+    { display: 'Nekromantie', value: 'Necromancy' },
+    { display: 'Beschwörung', value: 'Conjuration' },
+    { display: 'Hervorrufung', value: 'Evocation' },
+    { display: 'Bannmagie', value: 'Abjuration' },
+    { display: 'Wahrsagerei', value: 'Divination' },
   ];
 
   public savingThrowAttributes: any[] = [
@@ -151,15 +165,21 @@ export class SpellModalComponent {
     if (this.isModification) {
       this.id = this.spell.id;
       this.german = this.spell.german;
+      this.english = this.spell.english;
+      // TODO: Implement internationalization
+      this.name = this.spell.german;
       this.classes = this.spell.classes;
     } else {
       this.german = this.spell.german + ' (Kopie)';
+      this.english = this.spell.english + ' (copy)';
+      this.name = this.spell.german + ' (Kopie)';
     }
-
-    this.english = this.spell.english;
+    this.image = this.spell.image;
+    this.classes = this.spell.classes;
     this.level = this.spell.level;
     this.cost = this.spell.cost;
     this.canRitual = this.spell.canRitual;
+    this.isRitual = this.spell.isRitual;
     this.duration = this.spell.duration;
     this.timeToCast = this.spell.timeToCast;
     this.needsConcentration = this.spell.needsConcentration;
@@ -168,6 +188,7 @@ export class SpellModalComponent {
     this.needsMaterial = this.spell.needsMaterial;
     this.school = this.spell.school;
     this.description_de = this.spell.description_de;
+    this.description_en = this.spell.description_en;
     this.doesDamage = this.spell.doesDamage;
     this.needsSavingThrow = this.spell.needsSavingThrow;
     this.savingThrowAttribute = this.spell.savingThrowAttribute;
@@ -175,7 +196,7 @@ export class SpellModalComponent {
     this.isRanged = this.spell.isRanged;
     this.range = this.spell.range;
     this.hasAreaOfEffect = this.spell.hasAreaOfEffect;
-    this.radius = this.spell.radius;
+    this.diameter = this.spell.diameter;
     this.areaOfEffectType = this.spell.areaOfEffectType;
     this.needsAttackRoll = this.spell.needsAttackRoll;
     this.attackBonus = this.spell.attackBonus;
@@ -187,8 +208,9 @@ export class SpellModalComponent {
     const spell: Spell = {
       id: this.id,
       isCustom: true,
-      english: this.english,
-      german: this.german,
+      english: this.name,
+      german: this.name,
+      image: this.image,
       classes: this.classes,
       duration: 0, // FIXME: only mocked
       timeToCast: 0, // FIXME: only mocked
@@ -197,18 +219,20 @@ export class SpellModalComponent {
       level: parseInt(this.level.toString()),
       cost: this.cost,
       canRitual: this.canRitual,
+      isRitual: this.isRitual,
       school: this.school,
       description_de: this.description_de,
+      description_en: this.description_de,
       needsConcentration: this.needsConcentration,
       needsVerbal: this.needsVerbal,
       needsSomatic: this.needsSomatic,
       needsMaterial: this.needsMaterial,
-      needsAttackRoll: this.needsAttackRoll,
+      needsAttackRoll: this.needsSavingThrow ? false : this.needsAttackRoll,
       needsSavingThrow: this.needsSavingThrow,
       savingThrowAttribute: this.savingThrowAttribute,
       isRanged: this.isRanged,
       range: this.range,
-      radius: this.radius,
+      diameter: this.diameter,
       hasAreaOfEffect: this.hasAreaOfEffect,
       areaOfEffectType: this.areaOfEffectType,
       doesDamage: this.doesDamage,
@@ -221,18 +245,22 @@ export class SpellModalComponent {
     this.id = 0;
     this.german = '';
     this.english = '';
+    this.name = '';
+    this.image = '';
     this.classes = [];
     this.level = 0;
     this.cost = 'action';
-    this.canRitual = 'false';
+    this.canRitual = false;
+    this.isRitual = false;
     this.duration = 0;
     this.timeToCast = 0;
     this.needsConcentration = false;
     this.needsVerbal = false;
     this.needsSomatic = false;
     this.needsMaterial = false;
-    this.school = '';
+    this.school = 'Evocation';
     this.description_de = '';
+    this.description_en = '';
     this.doesDamage = true;
     this.needsSavingThrow = false;
     this.savingThrowAttribute = '';
@@ -240,8 +268,8 @@ export class SpellModalComponent {
     this.isRanged = false;
     this.range = 5;
     this.hasAreaOfEffect = false;
-    this.radius = 0;
-    this.areaOfEffectType = '';
+    this.diameter = 5;
+    this.areaOfEffectType = 'circle';
     this.needsAttackRoll = false;
     this.attackBonus = '';
     this.doesHeal = false;
@@ -256,5 +284,17 @@ export class SpellModalComponent {
     this.damage.splice(index, 1);
   }
 
+  public setRitual(event: any): void {
+    if (event.target.checked) {
+      this.canRitual = true;
+    }
+  }
+
+  public unsetRitual(event: any): void {
+    if (!event.target.checked) {
+      this.isRitual = false;
+    }
+  }
+
   // #endregion
 }

+ 5 - 3
src/app/shared-components/full-spellcard/full-spellcard.component.html

@@ -66,8 +66,8 @@
           <td>{{ spell.areaOfEffectType }}</td>
         </tr>
         <tr>
-          <td>Radius</td>
-          <td>{{ spell.radius }}</td>
+          <td>Durchmesser</td>
+          <td>{{ spell.diameter }}</td>
         </tr>
         } @if (spell.needsAttackRoll){
         <tr>
@@ -129,8 +129,10 @@
     @if(spell.isCustom){
     <button class="edit-button" (click)="update()">Anpassen</button>
     }
+
     <button class="delete-button" (click)="remove()">Entfernen</button>
+    @if(spell.isCustom){
     <button class="delete-button" (click)="delete()">Löschen</button>
-    }
+    } }
   </div>
 </div>

BIN
src/assets/images/spells/aid.jpg


BIN
src/assets/images/spells/spiritualWeapon.jpg


+ 5 - 3
src/interfaces/spell.ts

@@ -3,26 +3,28 @@ export interface Spell {
   isCustom: boolean;
   german: string;
   english: string;
+  image: string;
   classes: string[];
   level: number;
   cost: string;
   timeToCast: number;
   duration: number;
-  canRitual: 'true' | 'false' | 'only';
+  canRitual: boolean;
+  isRitual: boolean;
   needsConcentration: boolean;
   needsVerbal: boolean;
   needsSomatic: boolean;
   needsMaterial: boolean;
   school: string;
   description_de: string;
-  description_en?: string;
+  description_en: string;
   needsAttackRoll: boolean;
   needsSavingThrow: boolean;
   savingThrowAttribute?: string;
   isRanged: boolean;
   range?: number;
   hasAreaOfEffect: boolean;
-  radius?: number;
+  diameter?: number;
   areaOfEffectType?: string;
 
   doesDamage: boolean;

+ 3 - 0
src/interfaces/weapon.ts

@@ -2,6 +2,9 @@ export interface Weapon {
   name: string;
   damage: Damage[];
   attackBonus: string;
+  useAttributeModifier: boolean;
+  hasAdditionalDamage: boolean;
+  additionalDamage: number;
   range: number[];
   hasReach: boolean;
   throwRange?: number[];

+ 1 - 1
src/services/class/class.service.ts

@@ -515,7 +515,7 @@ export class ClassService {
   private notImplementedYet: any = {
     title: 'Not implemented yet',
     description: `
-    This class is not implemented yet. Please check back later.
+    This class is not implemented yet, please check back later.
     `,
     features: [],
   };

+ 5 - 3
src/services/data/data.service.ts

@@ -211,8 +211,10 @@ export class DataService {
 
   public removeFavoriteSpell(spell: Spell): void {
     const index = this._favoriteSpells.indexOf(spell);
-    this._favoriteSpells.splice(index, 1);
-    this.setData('favoriteSpells', { spells: this._favoriteSpells });
+    if (index > -1) {
+      this._favoriteSpells.splice(index, 1);
+      this.setData('favoriteSpells', { spells: this._favoriteSpells });
+    }
   }
 
   private _customSpells: Spell[] = [];
@@ -272,7 +274,7 @@ export class DataService {
     });
   }
 
-  private _customSpellId: number = 100;
+  private _customSpellId: number = 10000;
 
   public get customSpellId(): number {
     return this._customSpellId;

+ 11 - 2
src/services/modal/modal.service.ts

@@ -14,14 +14,23 @@ export class ModalService {
   private _resultSubject = new Subject<any>();
   result$ = this._resultSubject.asObservable();
 
+  /**
+   * Opens the modal with the specified component and provides the data as input for the component.
+   * @param component The component to open in the modal.
+   * @param data The data for the input of the component.
+   */
   public openModal(component: any, data: any) {
     console.log('ModalService: openModal');
     this._modalSubject.next({ component, data });
   }
 
-  // Is called from the dynamic content component to close the modal
+  /**
+   * This function closes the modal and sends a result state and an optinal data object to the host component.
+   * @param result The result state. Can be 'cancel', 'update' etc.
+   * @param data The oprional data object that eg contains a new or modifed weapon object.
+   */
   public handleModalClosing(result: any, data?: any) {
-    // Is listened to in the host component where the panel was opened, to initiate further steps
+    //The host components subscribe to the result$ Observable to get the result of the modal
     this._resultSubject.next({ state: result, data: data });
     this._closeModalSubject.next('close');
   }

+ 356 - 180
src/services/spells/spells.service.ts

@@ -34,8 +34,6 @@ export class SpellsService {
       .filter((spell) => spell.level === level)
       .filter((spell) => spell.classes.includes(characterClass));
     result.push(...this.customSpells.filter((spell) => spell.level === level));
-    console.log(result);
-
     return result;
   }
 
@@ -66,13 +64,15 @@ export class SpellsService {
       id: 0,
       isCustom: false,
       german: 'Göttliche Führung',
-      english: 'guidance',
+      english: 'Guidance',
+      image: 'guidance',
       classes: ['Test', 'Cleric', 'Druid'],
       level: 0,
       cost: 'action',
       duration: 10,
       timeToCast: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -94,19 +94,21 @@ export class SpellsService {
       range: 5,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 1,
       isCustom: false,
       german: 'Totenläuten',
-      english: 'tollTheDead',
+      english: 'Toll the Dead',
+      image: 'tollTheDead',
       classes: ['Test', 'Cleric', 'Warlock', 'Wizard'],
       level: 0,
       cost: 'action',
       duration: 0,
       timeToCast: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -133,19 +135,21 @@ export class SpellsService {
       range: 60,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 2,
       isCustom: false,
       german: 'Thaumaturgie',
-      english: 'thaumaturgy',
+      english: 'Thaumaturgy',
+      image: 'thaumaturgy',
       classes: ['Test', 'Cleric'],
       level: 0,
       cost: 'action',
       duration: 10,
       timeToCast: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: false,
       needsMaterial: false,
@@ -184,19 +188,21 @@ export class SpellsService {
       range: 30,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 3,
       german: 'Gift versprühen',
-      english: 'sprayPoison',
+      english: 'Spray Posion',
+      image: 'sprayPoison',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Druid', 'Artificer', 'Sorcerer', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -217,19 +223,21 @@ export class SpellsService {
       range: 10,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 4,
       german: 'Ausbessern',
-      english: 'mending',
+      english: 'Mending',
+      image: 'mending',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Bard', 'Cleric', 'Druid', 'Wizard', 'Sorcerer'],
       timeToCast: 10,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: true,
@@ -252,19 +260,21 @@ export class SpellsService {
       range: 5,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 5,
       german: 'Befall',
-      english: 'infestation',
+      english: 'Infestation',
+      image: 'infestation',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Druid', 'Sorcerer', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: true,
@@ -307,14 +317,16 @@ export class SpellsService {
     {
       id: 6,
       german: 'Blitzköder',
-      english: 'lightningLure',
+      english: 'Lightning Lure',
+      image: 'lightningLure',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Sorcerer', 'Wizard', 'Warlock'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: false,
       needsMaterial: false,
@@ -344,14 +356,16 @@ export class SpellsService {
     {
       id: 7,
       german: 'Botschaft',
-      english: 'message',
+      english: 'Message',
+      image: 'message',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Bard', 'Wizard', 'Sorcerer'],
       timeToCast: 0,
       cost: 'action',
       duration: 1,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: true,
@@ -374,19 +388,21 @@ export class SpellsService {
       range: 120,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 8,
       german: 'Donnerschlag',
-      english: 'thunderclap',
+      english: 'Thunderclap',
+      image: 'thunderclap',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Bard', 'Druid', 'Sorcerer', 'Warlock', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: false,
       needsSomatic: true,
       needsMaterial: false,
@@ -411,19 +427,21 @@ export class SpellsService {
       range: 5,
       hasAreaOfEffect: true,
       areaOfEffectType: 'circle',
-      radius: 5,
+      diameter: 5,
     },
     {
       id: 9,
       german: 'Dornenpeitsche',
-      english: 'thornWhip',
+      english: 'Thorn Whip',
+      image: 'thornWhip',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Druid', 'Artificer'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: true,
@@ -446,12 +464,13 @@ export class SpellsService {
       range: 30,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 10,
       german: 'Druidenkunst',
-      english: 'druidcraft',
+      english: 'Druidcraft',
+      image: 'druidcraft',
       level: 0,
       isCustom: false,
 
@@ -459,7 +478,8 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -493,19 +513,21 @@ export class SpellsService {
       range: 30,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 11,
       german: 'Dröhnende Klinge',
-      english: 'boomingBlade',
+      english: 'Booming Blade',
+      image: 'boomingBlade',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Bard', 'Sorcerer', 'Warlock', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
       duration: 1,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: false,
       needsSomatic: true,
       needsMaterial: true,
@@ -529,19 +551,21 @@ export class SpellsService {
       range: 5,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 11,
       german: 'Einfache Illusion',
-      english: 'minorIllusion',
+      english: 'Minor Illusion',
+      image: 'minorIllusion',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Bard', 'Wizard', 'Warlock', 'Sorcerer'],
       timeToCast: 0,
       cost: 'action',
       duration: 1,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: false,
       needsSomatic: true,
       needsMaterial: true,
@@ -566,19 +590,21 @@ export class SpellsService {
       range: 30,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 12,
       german: 'Erde Formen',
-      english: 'moldEarth',
+      english: 'Mold Earth',
+      image: 'moldEarth',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Druid', 'Artificer'],
       timeToCast: 0,
       cost: 'action',
       duration: 600,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: false,
       needsSomatic: true,
       needsMaterial: false,
@@ -612,19 +638,21 @@ export class SpellsService {
       range: 30,
       hasAreaOfEffect: true,
       areaOfEffectType: 'cube',
-      radius: 5,
+      diameter: 5,
     },
     {
       id: 13,
       german: 'Erfrierung',
-      english: 'frostbite',
+      english: 'Frostbite',
+      image: 'frostbite',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Druid', 'Warlock', 'Artificer', 'Sorcerer', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -649,19 +677,21 @@ export class SpellsService {
       range: 60,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 14,
       german: 'Feuerpfeil',
-      english: 'fireBolt',
+      english: 'Fire Bolt',
+      image: 'fireBolt',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Sorcerer', 'Wizard', 'Artificer'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -685,19 +715,21 @@ export class SpellsService {
       range: 120,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 15,
       german: 'Flammen Erzeugen',
-      english: 'produceFlames',
+      english: 'Produce Flame',
+      image: 'produceFlames',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Druid'],
       timeToCast: 0,
       cost: 'action',
       duration: 100,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -723,19 +755,21 @@ export class SpellsService {
       range: 30,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 16,
       german: 'Flammen kontrollieren',
-      english: 'controlFlames',
+      english: 'Control Flames',
+      image: 'controlFlames',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Druid', 'Artificer', 'Sorcerer', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
       duration: 600,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: false,
       needsSomatic: true,
       needsMaterial: false,
@@ -771,19 +805,21 @@ export class SpellsService {
       range: 60,
       hasAreaOfEffect: true,
       areaOfEffectType: 'cube',
-      radius: 5,
+      diameter: 5,
     },
     {
       id: 17,
       german: 'Freundschaft',
-      english: 'friends',
+      english: 'Friends',
+      image: 'friends',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Bard', 'Sorcerer', 'Warlock', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
       duration: 10,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: false,
       needsSomatic: true,
       needsMaterial: true,
@@ -805,19 +841,21 @@ export class SpellsService {
       range: 5,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 18,
       german: 'Gedankensplitter',
-      english: 'mindSliver',
+      english: 'Mind Sliver',
+      image: 'mindSliver',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Sorcerer', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: false,
       needsMaterial: false,
@@ -834,24 +872,30 @@ export class SpellsService {
         <p>Du treibst einen verstörenden Splitter psychischer Energie in den Geist einer Kreatur, die du in Reichweite sehen kannst. Das Ziel muss einen erfolgreichen Rettungswurf auf Intelligenz durchführen, sonst nimmt es 1W6 psychischen Schaden und muss 1W4 von seinem nächsten Rettungswurf vor Ende deines nächsten Zuges abziehen.</p>
         <p><b>Auf höheren Stufen:</b> Der Schaden des Zaubers erhöht sich um je 1W6 wenn du Stufe 5 (auf 2W6), Stufe 11 (auf 3W6) und Stufe 17 (auf 4W6) erreichst.</p>
       `,
+      description_en: `
+        <p>You drive a disorienting spike of psychic energy into the mind of one creature you can see within range. The target must succeed on an Intelligence saving throw or take 1d6 psychic damage and subtract 1d4 from the next saving throw it makes before the end of your next turn.</p>
+        <p><b>At higher levels:</b> The spell’s damage increases by 1d6 when you reach 5th level (2d6), 11th level (3d6), and 17th level (4d6).</p>
+      `,
       school: 'Enchantment',
       isRanged: true,
       range: 60,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 19,
       german: 'Gehässiger Spott',
-      english: 'viciousMockery',
+      english: 'Vicious Mockery',
+      image: 'viciousMockery',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Bard'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: false,
       needsMaterial: false,
@@ -875,19 +919,21 @@ export class SpellsService {
       range: 60,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 20,
       german: 'Gift versprühen',
-      english: 'poisonSpray',
+      english: 'Poison Spray',
+      image: 'poisonSpray',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Druid', 'Sorcerer', 'Warlock', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -912,19 +958,21 @@ export class SpellsService {
       range: 10,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 21,
       german: 'Grünfeuerklinge',
-      english: 'greenFlameBlade',
+      english: 'Green-Flame Blade',
+      image: 'greenFlameBlade',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Sorcerer', 'Warlock', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: false,
       needsMaterial: true,
@@ -948,19 +996,21 @@ export class SpellsService {
       range: 5,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 22,
       german: 'Heilige Flamme',
-      english: 'sacredFlame',
+      english: 'Sacred Flame',
+      image: 'sacredFlame',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Cleric'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -985,19 +1035,21 @@ export class SpellsService {
       range: 60,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 23,
       german: 'Kalte Hand',
-      english: 'chillTouch',
+      english: 'Chill Touch',
+      image: 'chillTouch',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Sorcerer', 'Warlock', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -1023,19 +1075,21 @@ export class SpellsService {
       range: 120,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 24,
       german: 'Klingenbann',
-      english: 'bladeWard',
+      english: 'Blade Ward',
+      image: 'bladeWard',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Bard', 'Sorcerer', 'Warlock', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
       duration: 100,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: false,
       needsMaterial: false,
@@ -1057,19 +1111,21 @@ export class SpellsService {
       range: 5,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 25,
       german: 'Kältestrahl',
-      english: 'rayOfFrost',
+      english: 'Ray of Frost',
+      image: 'rayOfFrost',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Sorcerer', 'Wizard', 'Artificer'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -1093,19 +1149,21 @@ export class SpellsService {
       range: 60,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 26,
       german: 'Lagerfeuer erschaffen',
-      english: 'createBonfire',
+      english: 'Create Bonfire',
+      image: 'createBonfire',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Druid', 'Sorcerer', 'Warlock', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
       duration: 10,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -1130,12 +1188,13 @@ export class SpellsService {
       range: 60,
       hasAreaOfEffect: true,
       areaOfEffectType: 'cube',
-      radius: 5,
+      diameter: 5,
     },
     {
       id: 27,
       german: 'Licht',
-      english: 'light',
+      english: 'Light',
+      image: 'light',
       level: 0,
       isCustom: false,
       classes: [
@@ -1150,7 +1209,8 @@ export class SpellsService {
       timeToCast: 0,
       cost: 'action',
       duration: 600,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: false,
       needsMaterial: true,
@@ -1162,7 +1222,7 @@ export class SpellsService {
       doesHeal: false,
       heal: { diceNumber: '', diceType: '', additionalHeal: 0 },
       description_de: `
-      <p>Du berührst einen Gegenstand, der nicht größer als 10 Fuß in einer beliebigen Dimension ist. Bis der Zauber endet, strahlt das Objekt helles Licht in einem Radius von 20 Fußn und schwaches Licht für weitere 20 Fuß aus. Das Licht kann nach Belieben gefärbt werden. Wenn du das Objekt vollständig mit etwas Undurchsichtigem abdeckst, wird das Licht blockiert. Der Zauber endet, wenn du ihn erneut wirkst oder ihn als Aktion abbrichst.</p>
+      <p>Du berührst einen Gegenstand, der nicht größer als 10 Fuß in einer beliebigen Dimension ist. Bis der Zauber endet, strahlt das Objekt helles Licht in einem Radius von 20 Fuß und schwaches Licht für weitere 20 Fuß aus. Das Licht kann nach Belieben gefärbt werden. Wenn du das Objekt vollständig mit etwas Undurchsichtigem abdeckst, wird das Licht blockiert. Der Zauber endet, wenn du ihn erneut wirkst oder ihn als Aktion abbrichst.</p>
       <p>Wenn du ein Objekt anvisierst, das von einer feindlichen Kreatur gehalten oder getragen wird, muss diese Kreatur einen Rettungswurf auf Geschicklichkeit bestehen, um den Zauber zu vermeiden.</p>
       `,
       description_en: `
@@ -1173,20 +1233,22 @@ export class SpellsService {
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
-      areaOfEffectType: '',
-      radius: 0,
+      areaOfEffectType: 'circle',
+      diameter: 40,
     },
     {
       id: 28,
       german: 'Magierhand',
-      english: 'mageHand',
+      english: 'Mage Hand',
+      image: 'mageHand',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Bard', 'Sorcerer', 'Warlock', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
       duration: 10,
-      canRitual: 'true',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -1212,19 +1274,21 @@ export class SpellsService {
       range: 30,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 29,
       german: 'Magiestein',
-      english: 'magicStone',
+      english: 'Magic Stone',
+      image: 'magicStone',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Druid', 'Hexenmeister'],
       timeToCast: 0,
       cost: 'bonus',
       duration: 10,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -1248,19 +1312,21 @@ export class SpellsService {
       range: 5,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 30,
       german: 'Resistenz',
-      english: 'resistance',
+      english: 'Resistance',
+      image: 'resistance',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Cleric', 'Druid', 'Artificer'],
       timeToCast: 0,
       cost: 'action',
       duration: 10,
-      canRitual: 'true',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: true,
@@ -1282,19 +1348,21 @@ export class SpellsService {
       range: 5,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 31,
       german: 'Schauriger Strahl',
-      english: 'eldritchBlast',
+      english: 'Eldritch Blast',
+      image: 'eldritchBlast',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Hexenmeister'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -1318,19 +1386,21 @@ export class SpellsService {
       range: 120,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 32,
       german: 'Schockgriff',
-      english: 'shockingGrasp',
+      english: 'Shocking Grasp',
+      image: 'shockingGrasp',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Sorcerer', 'Wizard', 'Artificer'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -1354,19 +1424,21 @@ export class SpellsService {
       range: 5,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 33,
       german: 'Schwertexplosion',
-      english: 'swordBurst',
+      english: 'Sword Burst',
+      image: 'swordBurst',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Sorcerer', 'Warlock', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: false,
       needsSomatic: true,
       needsMaterial: false,
@@ -1391,19 +1463,21 @@ export class SpellsService {
       range: 5,
       hasAreaOfEffect: true,
       areaOfEffectType: 'circle',
-      radius: 5,
+      diameter: 5,
     },
     {
       id: 34,
       german: 'Shillelagh',
-      english: 'shillelagh',
+      english: 'Shillelagh',
+      image: 'shillelagh',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Druid'],
       timeToCast: 0,
       cost: 'bonus',
       duration: 10,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: true,
@@ -1425,19 +1499,21 @@ export class SpellsService {
       range: 5,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 35,
       german: 'Säurespritzer',
-      english: 'acidSplash',
+      english: 'Acid Splash',
+      image: 'acidSplash',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Sorcerer', 'Wizard', 'Artificer'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -1462,19 +1538,21 @@ export class SpellsService {
       range: 60,
       hasAreaOfEffect: true,
       areaOfEffectType: 'circle',
-      radius: 5,
+      diameter: 5,
     },
     {
       id: 36,
       german: 'Tanzende Lichter',
-      english: 'dancingLights',
+      english: 'Dancing Lights',
+      image: 'dancingLights',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Bard', 'Sorcerer', 'Wizard', 'Artificer'],
       timeToCast: 0,
       cost: 'action',
       duration: 10,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: true,
@@ -1497,19 +1575,21 @@ export class SpellsService {
       range: 30,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 37,
       german: 'Taschenspielerei',
-      english: 'prestidigitation',
+      english: 'Prestidigitation',
+      image: 'prestidigitation',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Bard', 'Sorcerer', 'Wizard', 'Artificer'],
       timeToCast: 0,
       cost: 'action',
       duration: 600,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -1549,19 +1629,21 @@ export class SpellsService {
       range: 10,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 38,
       german: 'Verschonung der Toten',
-      english: 'spareTheDying',
+      english: 'Spare the Dying',
+      image: 'spareTheDying',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Cleric'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -1583,19 +1665,21 @@ export class SpellsService {
       range: 5,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 39,
       german: 'Urtümliche Wildheit',
-      english: 'primalSavagery',
+      english: 'Primal Savagery',
+      image: 'primalSavagery',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Druid'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: false,
       needsSomatic: true,
       needsMaterial: false,
@@ -1619,19 +1703,21 @@ export class SpellsService {
       range: 5,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 40,
       german: 'Wasser formen',
-      english: 'shapeWater',
+      english: 'Shape Water',
+      image: 'shapeWater',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Druid', 'Sorcerer', 'Wizard', 'Artificer'],
       timeToCast: 0,
       cost: 'action',
       duration: 600,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: false,
       needsSomatic: true,
       needsMaterial: false,
@@ -1667,19 +1753,21 @@ export class SpellsService {
       range: 30,
       hasAreaOfEffect: true,
       areaOfEffectType: 'cube',
-      radius: 5,
+      diameter: 5,
     },
     {
       id: 41,
       german: 'Windböe',
-      english: 'gust',
+      english: 'Gust',
+      image: 'gust',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Druid', 'Sorcerer', 'Wizard', 'Artificer'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -1712,19 +1800,21 @@ export class SpellsService {
       range: 30,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 42,
       german: 'Wort des Strahlens',
-      english: 'wordOfRadiance',
+      english: 'Word of Radiance',
+      image: 'wordOfRadiance',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Cleric'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -1749,19 +1839,21 @@ export class SpellsService {
       range: 5,
       hasAreaOfEffect: true,
       areaOfEffectType: 'circle',
-      radius: 5,
+      diameter: 5,
     },
     {
       id: 43,
       german: 'Zielsicherer Schlag',
-      english: 'trueStrike',
+      english: 'True Strike',
+      image: 'trueStrike',
       level: 0,
       isCustom: false,
       classes: ['Test', 'Sorcerer', 'Wizard'],
       timeToCast: 0,
       cost: 'action',
       duration: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: false,
       needsSomatic: true,
       needsMaterial: false,
@@ -1783,20 +1875,22 @@ export class SpellsService {
       range: 30,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     // Level 1
     {
       id: 1000,
       german: 'Wunden verursachen',
-      english: 'inflictWounds',
+      english: 'Inflict Wounds',
+      image: 'inflictWounds',
       level: 1,
       cost: 'action',
       isCustom: false,
       classes: ['Test', 'Cleric'],
       duration: 0,
       timeToCast: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -1809,24 +1903,28 @@ export class SpellsService {
       heal: { diceNumber: '', diceType: '', additionalHeal: 0 },
       description_de:
         'Mach einen Nahkampf-Zauberangriff gegen eine Kreatur deiner Wahl. Bei einem Erfolg erleidet das Ziel 3W10 Nekrotischen Schaden. Auf höheren Graden: Wirkst du diesen Zauber, indem du einen Zauberplatz des 2. Grades oder höher nutzt, steigt der Schaden für jeden Grad über dem 1. um 1W10.',
+      description_en:
+        'Make a melee spell attack against a creature you can reach. On a hit, the target takes 3d10 necrotic damage. At higher levels: When you cast this spell using a spell slot of 2nd level or higher, the damage increases by 1d10 for each slot level above 1st.',
       school: 'Necromancy',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 1001,
       german: 'Heilende Berührung',
-      english: 'cureWounds',
+      english: 'Cure Wounds',
+      image: 'cureWounds',
       level: 1,
       cost: 'action',
       duration: 0,
       isCustom: false,
       classes: ['Test', 'Bard', 'Cleric', 'Druid', 'Paladin', 'Ranger'],
       timeToCast: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -1839,17 +1937,20 @@ export class SpellsService {
       heal: { diceNumber: '1', diceType: 'd8', additionalHeal: 4 },
       description_de:
         'Eine Kreatur, die du berührst, gewinnt Trefferpunkte in Höhe von 1W8 + deinem Attributsmodifikator im Zauberwirken zurück. Dieser Zauber wirkt nicht auf Untote oder Konstrukte. Auf höheren Graden: Wirkst du diesen Zauber, indem du einen Zauberplatz des 2. Grades oder höher nutzt, steigt die Heilung für jeden Grad über dem 1. um 1W8.',
+      description_en:
+        'A creature you touch regains a number of hit points equal to 1d8 + your spellcasting ability modifier. This spell has no effect on undead or constructs. At higher levels: When you cast this spell using a spell slot of 2nd level or higher, the healing increases by 1d8 for each slot level above 1st.',
       school: 'Evocation',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 1002,
       german: 'Magie entdecken',
-      english: 'detectMagic',
+      english: 'Detact Magic',
+      image: 'detectMagic',
       level: 1,
       cost: 'action',
       duration: 100,
@@ -1865,7 +1966,8 @@ export class SpellsService {
         'Wizard',
       ],
       timeToCast: 0,
-      canRitual: 'true',
+      canRitual: true,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -1878,24 +1980,28 @@ export class SpellsService {
       heal: { diceNumber: '', diceType: '', additionalHeal: 0 },
       description_de:
         'Während der Wirkungsdauer nimmst du die Gegenwart von Magie im Abstand von bis zu 30 Fuß von dir wahr. Wenn du auf diese Weise Magie wahrnimmst, kannst du deine Aktion verwenden, um schwache Auren um sichtbare magische Kreaturen oder Objekte sowie ihre magische Schule zu erkennen, falls vorhanden. Dieser Zauber durchdringt die meisten Barrieren, wird aber von 30 Zentimeter Stein, 2,5 Zentimetern gewöhnlichem Metall, dünnem Bleiblech sowie von einem Meter Holz oder Erde blockiert.',
+      description_en:
+        'For the duration, you sense the presence of magic within 30 feet of you. If you sense magic in this way, you can use your action to see a faint aura around any visible creature or object in the area that bears magic, and you learn its school of magic, if any. The spell can penetrate most barriers, but it is blocked by 1 foot of stone, 1 inch of common metal, a thin sheet of lead, or 3 feet of wood or dirt.',
       school: 'Divination',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 1003,
       german: 'Segnen',
-      english: 'bless',
+      english: 'Bless',
+      image: 'bless',
       level: 1,
       cost: 'action',
       duration: 10,
       isCustom: false,
       classes: ['Test', 'Cleric', 'Paladin'],
       timeToCast: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: true,
@@ -1908,24 +2014,28 @@ export class SpellsService {
       heal: { diceNumber: '', diceType: '', additionalHeal: 0 },
       description_de:
         'Du segnest bis zu drei Kreaturen deiner Wahl in Reichweite. Wenn ein Ziel während der Wirkungsdauer einen Angriffs- oder Rettungswurf ausführt, darf es mit einem W4 würfeln und das Würfelergebnis zu seinem Angriffs- oder Rettungswurf addieren. Auf höheren Graden: Wirkst du diesen Zauber, indem du einen Zauberplatz des 2. Grades oder höher nutzt, kannst du für jeden Grad über dem 1. eine zusätzliche Kreatur als Ziel wählen.',
+      description_en:
+        'You bless up to three creatures of your choice within range. Whenever a target makes an attack roll or a saving throw before the spell ends, the target can roll a d4 and add the number rolled to the attack roll or saving throw. At higher levels: When you cast this spell using a spell slot of 2nd level or higher, you can target one additional creature for each slot level above 1st.',
       school: 'Enchantment',
       isRanged: true,
       range: 60,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 1004,
       german: 'Heilendes Wort',
-      english: 'healingWord',
+      english: 'Healing Word',
+      image: 'healingWord',
       isCustom: false,
       classes: ['Test', 'Bard', 'Cleric', 'Druid'],
       level: 1,
       cost: 'bonus action',
       duration: 0,
       timeToCast: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: false,
       needsMaterial: false,
@@ -1938,24 +2048,28 @@ export class SpellsService {
       heal: { diceNumber: '1', diceType: 'd4', additionalHeal: 4 },
       description_de:
         'Eine Kreatur deiner Wahl in Reichweite, die du sehen kannst, gewinnt Trefferpunkte in Höhe von 1W4 + deinem Zauberwirken-Attributsmodifikator zurück. Dieser Zauber wirkt nicht auf Untote oder Konstrukte. Auf höheren Graden: Wirkst du diesen Zauber, indem du einen Zauberplatz des 2. Grades oder höher nutzt, steigt die Heilung für jeden Grad über dem 1. um 1W4.',
+      description_en:
+        'A creature of your choice that you can see within range regains hit points equal to 1d4 + your spellcasting ability modifier. This spell has no effect on undead or constructs. At higher levels: When you cast this spell using a spell slot of 2nd level or higher, the healing increases by 1d4 for each slot level above 1st.',
       school: 'Evocation',
       isRanged: true,
       range: 60,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 1005,
       german: 'Lenkendes Geschoss',
-      english: 'guidingBolt',
+      english: 'Guiding Bolt',
+      image: 'guidingBolt',
       level: 1,
       cost: 'action',
       duration: 0,
       isCustom: false,
       classes: ['Test', 'Cleric'],
       timeToCast: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -1968,24 +2082,28 @@ export class SpellsService {
       heal: { diceNumber: '', diceType: '', additionalHeal: 0 },
       description_de:
         'Ein Lichtblitz schießt auf eine Kreatur deiner Wahl in Reichweite zu. Führe einen Fernkampf-Zauberangriff gegen das Ziel aus. Bei einem Erfolg erleidet das Ziel 4W6 gleißenden Schaden. Dank des mystischen dämmrigen Lichts, das auf dem Ziel glitzert, ist der nächste Angriffswurf gegen das Ziel vor Ende deines nächsten Zugs im Vorteil. Auf höheren Graden: Wirkst du diesen Zauber, indem du einen Zauberplatz des 2. Grades oder höher nutzt, steigt der Schaden für jeden Grad über dem 1. um 1W6.',
+      description_en:
+        'A flash of light streaks toward a creature of your choice within range. Make a ranged spell attack against the target. On a hit, the target takes 4d6 radiant damage, and the next attack roll made against this target before the end of your next turn has advantage, thanks to the mystical dim light glittering on the target until then. At higher levels: When you cast this spell using a spell slot of 2nd level or higher, the damage increases by 1d6 for each slot level above 1st.',
       school: 'Evocation',
       isRanged: true,
       range: 120,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 1006,
       german: 'Gift und Krankheiten entdecken',
-      english: 'detectPoisonAndDisease',
+      english: 'Detect Poison and Disease',
+      image: 'detectPoisonAndDisease',
       level: 1,
       cost: 'action',
       duration: 100,
       isCustom: false,
       classes: ['Test', 'Cleric', 'Druid', 'Paladin', 'Ranger'],
       timeToCast: 0,
-      canRitual: 'true',
+      canRitual: true,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: true,
@@ -1998,24 +2116,28 @@ export class SpellsService {
       heal: { diceNumber: '', diceType: '', additionalHeal: 0 },
       description_de:
         'Während der Wirkungsdauer nimmst du die Gegenwart und die Position von Giften, giftigen Kreaturen und Krankheiten im Abstand von bis zu neun Metern von dir wahr. Du kannst auch die Art des Gifts, der giftigen Kreatur oder der Krankheit bestimmen. Dieser Zauber durchdringt die meisten Barrieren, wird aber von 1 Fußn Stein, 2,5 Zentimetern gewöhnlichem Metall, dünnem Bleiblech sowie von einem Meter Holz oder Erde blockiert.',
+      description_en:
+        'For the duration, you can sense the presence and location of poisons, poisonous creatures, and diseases within 30 feet of you. You also identify the kind of poison, poisonous creature, or disease in each case. The spell can penetrate most barriers, but it is blocked by 1 foot of stone, 1 inch of common metal, a thin sheet of lead, or 3 feet of wood or dirt.',
       school: 'Divination',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 1007,
       german: 'Heldenmut',
-      english: 'heroism',
+      english: 'Heroism',
+      image: 'heroism',
       level: 1,
       cost: 'action',
       duration: 10,
       isCustom: false,
       classes: ['Test', 'Bard', 'Paladin'],
       timeToCast: 0,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: false,
@@ -2028,24 +2150,28 @@ export class SpellsService {
       heal: { diceNumber: '', diceType: '', additionalHeal: 0 },
       description_de:
         'Du berührst eine bereitwillige Kreatur und erfüllst sie mit Tapferkeit. Bis der Zauber endet, kann die Kreatur nicht verängstigt werden, und sie erhält zu Beginn jedes ihrer Züge temporäre Trefferpunkte in Höhe deines Zauberwirken-Attributsmodifikators. Wenn der Zauber endet, verliert das Ziel alle verbleibenden temporären Trefferpunkte dieses Zaubers. Auf höheren Graden: Wirkst du diesen Zauber, indem du einen Zauberplatz des 2. Grades oder höher nutzt, kannst du für jeden Grad über dem 1. eine zusätzliche Kreatur als Ziel wählen.',
+      description_en:
+        'A willing creature you touch is imbued with bravery. Until the spell ends, the creature is immune to being frightened and gains temporary hit points equal to your spellcasting ability modifier at the start of each of its turns. When the spell ends, the target loses any remaining temporary hit points from this spell. At higher levels: When you cast this spell using a spell slot of 2nd level or higher, you can target one additional creature for each slot level above 1st.',
       school: 'Enchantment',
       isRanged: false,
       range: 5,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
     {
       id: 1008,
       german: 'Heiligtum',
-      english: 'sanctuary',
+      english: 'Sanctuary',
+      image: 'sanctuary',
       level: 1,
       isCustom: false,
       classes: ['Test', 'Cleric'],
       timeToCast: 0,
       cost: 'bonus action',
       duration: 10,
-      canRitual: 'false',
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: true,
@@ -2058,44 +2184,94 @@ export class SpellsService {
       heal: { diceNumber: '', diceType: '', additionalHeal: 0 },
       description_de:
         'Du schützt eine Kreatur in Reichweite vor Angriffen. Während der Wirkungsdauer muss jede Kreatur, die bei einem Angriff oder schädlichen Zauber die geschützte Kreatur als Ziel hat, zuerst einen Weisheitsrettungswurf bestehen. Scheitert der Wurf, muss die Kreatur ein neues Ziel wählen, oder sie verliert den Angriff oder Zauber. Dieser Zauber schützt die entsprechende Kreatur nicht vor Flächeneffekten wie der Explosion eines Feuerballs. Greift die geschützte Kreatur an oder wirkt sie einen Zauber auf eine feindliche Kreatur, endet der Effekt dieses Zaubers.',
+      description_en:
+        'You ward a creature within range against attack. Until the spell ends, any creature who targets the warded creature with an attack or a harmful spell must first make a Wisdom saving throw. On a failed save, the creature must choose a new target or lose the attack or spell. This spell doesn’t protect the warded creature from area effects, such as the explosion of a fireball. If the warded creature makes an attack or casts a spell that affects an enemy creature, this spell ends.',
       school: 'Abjuration',
       isRanged: true,
       range: 30,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
+
+    // Level 2
     {
-      id: 1009,
-      german: 'Illusion',
-      english: 'illusion',
-      level: 1,
+      id: 2000,
+      german: 'Beistand',
+      english: 'Aid',
+      image: 'aid',
+      level: 2,
       isCustom: false,
-      classes: ['Test', 'Cleric'],
+      classes: ['Test', 'Cleric', 'Paladin'],
       timeToCast: 0,
-      cost: 'bonus action',
-      duration: 10,
-      canRitual: 'false',
+      cost: 'action',
+      duration: 4800,
+      canRitual: false,
+      isRitual: false,
       needsVerbal: true,
       needsSomatic: true,
       needsMaterial: true,
-      needsConcentration: true,
+      needsConcentration: false,
       needsAttackRoll: false,
       needsSavingThrow: false,
       doesDamage: false,
       damage: [{ diceNumber: '', diceType: '', damageType: '' }],
+      doesHeal: true,
+      heal: { diceNumber: '', diceType: '', additionalHeal: 5 },
+      description_de: `
+        <p>Dein Zauber unterstützt deine Verbündeten. Wähle bis zu 3 Kreaturen in Reichweite. Das Trefferpunkte-Maximum, sowie die momentanen trefferpunkte werden um 5 erhöht.</p>
+        <p><b>Auf höheren Graden:</b> Wirkst du diesen Zauber mit einem Zauberplatz des 3. Grades oder höher, erhöht sich das Trefferpunkte-Maximum um zusätzliche 5 Trefferpunkte für jeden Grad über dem 2.</p>
+      `,
+      description_en: `
+        <p>Your spell bolsters your allies with toughness and resolve. Choose up to three creatures within range. Each target’s hit point maximum and current hit points increase by 5 for the duration.</p>
+        <p><b>At higher levels:</b> When you cast this spell using a spell slot of 3rd level or higher, a target’s hit points increase by an additional 5 for each slot level above 2nd.</p>
+      `,
+      school: 'Abjuration',
+      isRanged: true,
+      range: 30,
+      hasAreaOfEffect: false,
+      areaOfEffectType: '',
+      diameter: 0,
+    },
+    {
+      id: 2001,
+      german: 'Waffe des Glaubens',
+      english: 'Spiritual Weapon',
+      image: 'spiritualWeapon',
+      level: 2,
+      isCustom: false,
+      classes: ['Test', 'Cleric'],
+      timeToCast: 0,
+      cost: 'bonus action',
+      duration: 10,
+      canRitual: false,
+      isRitual: false,
+      needsVerbal: true,
+      needsSomatic: true,
+      needsMaterial: false,
+      needsConcentration: false,
+      needsAttackRoll: true,
+      needsSavingThrow: false,
+      doesDamage: true,
+      damage: [{ diceNumber: '1', diceType: 'd8', damageType: 'force' }],
       doesHeal: false,
       heal: { diceNumber: '', diceType: '', additionalHeal: 0 },
-      description_de:
-        'Du schützt eine Kreatur in Reichweite vor Angriffen. Während der Wirkungsdauer muss jede Kreatur, die bei einem Angriff oder schädlichen Zauber die geschützte Kreatur als Ziel hat, zuerst einen Weisheitsrettungswurf bestehen. Scheitert der Wurf, muss die Kreatur ein neues Ziel wählen, oder sie verliert den Angriff oder Zauber. Dieser Zauber schützt die entsprechende Kreatur nicht vor Flächeneffekten wie der Explosion eines Feuerballs. Greift die geschützte Kreatur an oder wirkt sie einen Zauber auf eine feindliche Kreatur, endet der Effekt dieses Zaubers.',
-      school: 'Illusion',
+      description_de: `
+        <p>Du erschaffst eine Schwebende, gespenstische Waffe in Reichweite, die für die Wirkungsdauer anhält oder bis du den Zauber erneut wirkst. Wenn du den Zauber wirkst, kannst du einen Nahkampfangriff gegen eine Kreatur innerhalb von 5 Fuß zur Waffe durchführen. Bei einem Treffer erleidet das Ziel 1W8 + deinen Zaubermodifikator als Energieschaden.</p>
+        <p>Als Bonusaktion in deinem Zug kannst du die Waffe bis zu 20 Fuß weit bewegen und den Angriff gegen eine Kreatur innerhalb von 5 Fuß um sie wiederholen.</p>
+        <p>Die Waffe kann jede Form annehmen, die du wählst. Kleriker von Gottheiten, die mit einer bestimmten Waffe assoziiert sind (wie St. Cuthbert für seine Keule und Thor für seinen Hammer), lassen den Effekt dieses Zaubers wie diese Waffe aussehen.</p>
+        <p><b>Auf höheren Graden:</b> Wirkst du diesen Zauber mit einem Zauberplatz des 3. Grades oder höher, erhöht sich der Schaden um 1W8 für jeden zweiten Grad über dem 2.</p>
+      `,
+      description_en: `
+        <p>You create a floating, spectral weapon within range that lasts for the duration or until you cast this spell again. When you cast the spell, you can make a melee spell attack against a creature within 5 feet of the weapon. On a hit, the target takes force damage equal to 1d8 + your spellcasting ability modifier. As a bonus action on your turn, you can move the weapon up to 20 feet and repeat the attack against a creature within 5 feet of it. The weapon can take whatever form you choose. Clerics of deities who are associated with a particular weapon (as St. Cuthbert is known for his mace and Thor for his hammer) make this spell’s effect resemble that weapon.</p>
+        <p><b>At higher levels:</b> When you cast this spell using a spell slot of 3rd level or higher, the damage increases by 1d8 for every two slot levels above 2nd.</p>
+      `,
+      school: 'Evocation',
       isRanged: true,
-      range: 30,
+      range: 60,
       hasAreaOfEffect: false,
       areaOfEffectType: '',
-      radius: 0,
+      diameter: 0,
     },
-
-    // Level 2
   ];
 }

+ 20 - 0
src/styles.scss

@@ -212,6 +212,13 @@
     }
 }
 
+.suffix {
+    text-align: right;
+    padding-right: 0.5rem;
+    color: grey;
+    font-size: 0.75rem;
+}
+
 // Line styles
 
 .centered-line {
@@ -386,3 +393,16 @@ body {
 .mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__trailing {
     border-color: var(--primary) !important;
 }
+
+.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-text-field__input {
+    caret-color: var(--primary) !important;
+}
+
+.mat-mdc-form-field.mat-focused .mat-mdc-select-arrow {
+    color: var(--primary) !important;
+}
+
+.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-floating-label,
+.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-floating-label--float-above {
+    color: var(--primary) !important;
+}