spell-table.component.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. import { Component } from '@angular/core';
  2. import { DataService } from 'src/services/data/data.service';
  3. import { ModalService } from 'src/services/modal/modal.service';
  4. import { DetailsService } from 'src/services/details/details.service';
  5. import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
  6. import { Spell } from 'src/interfaces/spell';
  7. import { SpellDetailsComponent } from './spell-details/spell-details.component';
  8. import { SpellModalComponent } from 'src/app/journal/spell-modal/spell-modal.component';
  9. import { FullSpellcardComponent } from 'src/app/shared-components/full-spellcard/full-spellcard.component';
  10. import { Observable, OperatorFunction } from 'rxjs';
  11. import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators';
  12. @Component({
  13. selector: 'spell-table',
  14. templateUrl: './spell-table.component.html',
  15. styleUrls: ['./spell-table.component.scss'],
  16. })
  17. export class SpellTableComponent {
  18. public spellAttackBonus: string = '0';
  19. public spellSaveDC: number = 0;
  20. private spellcastingAttribute: string | undefined;
  21. private spellcastingAttributeModifier: number = 0;
  22. private proficiencyBonus: number = 2;
  23. public spells!: Spell[];
  24. private preparedSpells!: Spell[];
  25. private preparedSpellsNames: string[] = [];
  26. public newSpellName: string = '';
  27. public showInput: boolean = false;
  28. public attributes: any = {
  29. strength: 'STR',
  30. dexterity: 'DEX',
  31. constitution: 'CON',
  32. intelligence: 'INT',
  33. wisdom: 'WIS',
  34. charisma: 'CHA',
  35. };
  36. public areas: any = {
  37. cone: 'Kegel',
  38. sphere: 'Kugel',
  39. circle: 'Kreis',
  40. line: 'Linie',
  41. square: 'Quadrat',
  42. cube: 'Würfel',
  43. };
  44. public constructor(
  45. public dataAccessor: DataService,
  46. private modalAccessor: ModalService,
  47. public detailsAccessor: DetailsService
  48. ) {}
  49. public ngOnInit(): void {
  50. this.spells = this.dataAccessor.favoriteSpells;
  51. this.preparedSpells = this.dataAccessor.getAllPreparedSpells();
  52. this.preparedSpellsNames = this.preparedSpells.map((spell) => spell.german);
  53. this.subscribeToData();
  54. }
  55. public showFullSpellcard(spellIndex: number): void {
  56. this.modalAccessor.openModal(FullSpellcardComponent, {
  57. spell: this.spells[spellIndex],
  58. isFromDashboard: true,
  59. });
  60. const resultSubscription = this.modalAccessor.result$.subscribe(
  61. (result) => {
  62. resultSubscription.unsubscribe();
  63. if (result.state === 'delete') {
  64. this.spells.splice(spellIndex, 1);
  65. this.updateSpellsInDatabase();
  66. } else if (result.state !== 'cancel') {
  67. throw new Error('Unexpected result state, please send a bug report.');
  68. }
  69. }
  70. );
  71. }
  72. // LEGACY CODE
  73. public openDetailsPanel(index: number): void {
  74. this.detailsAccessor.openPanel(SpellDetailsComponent, {
  75. spell: this.spells[index],
  76. modifiers: {
  77. attackBonus: this.spellAttackBonus,
  78. saveDC: this.spellSaveDC,
  79. },
  80. });
  81. const resultSubscription = this.detailsAccessor.result$.subscribe(
  82. (result) => {
  83. if (result.state === 'delete') {
  84. this.deleteSpell(index);
  85. } else if (result.state === 'update') {
  86. this.openModal(true, index);
  87. } else {
  88. throw new Error('DND-Error: Unknown state: ' + result.state);
  89. }
  90. resultSubscription.unsubscribe();
  91. }
  92. );
  93. }
  94. //
  95. public openModal(isUpdate: boolean, index?: number): void {
  96. this.modalAccessor.openModal(SpellModalComponent, {
  97. spell:
  98. index !== undefined
  99. ? JSON.parse(JSON.stringify(this.spells[index]))
  100. : undefined,
  101. isUpdate: isUpdate,
  102. });
  103. const resultSubscription = this.modalAccessor.result$.subscribe(
  104. (result) => {
  105. if (result.state === 'update') {
  106. this.updateSpell(result.data, index!);
  107. } else if (result.state === 'add') {
  108. this.addSpell(result.data);
  109. } else {
  110. throw new Error('DND-Error: Unknown state: ' + result.state);
  111. }
  112. resultSubscription.unsubscribe();
  113. }
  114. );
  115. }
  116. public toggleInput(): void {
  117. this.showInput = !this.showInput;
  118. this.newSpellName = '';
  119. }
  120. public onSpellSelect(spellname: any): void {
  121. const newSpell = this.preparedSpells.filter(
  122. (spell) => spell.german === spellname
  123. );
  124. if (newSpell.length !== 1) {
  125. throw new Error('Spell not found.');
  126. } else {
  127. this.addSpell(newSpell[0]);
  128. }
  129. this.newSpellName = '';
  130. }
  131. public addSpell(spell: Spell) {
  132. this.spells.push(spell);
  133. this.updateSpellsInDatabase();
  134. }
  135. public updateSpell(spell: Spell, index: number): void {
  136. this.spells[index] = spell;
  137. this.updateSpellsInDatabase();
  138. }
  139. public deleteSpell(index: number): void {
  140. this.spells.splice(index, 1);
  141. this.updateSpellsInDatabase();
  142. }
  143. // utils
  144. public dropSpells(event: CdkDragDrop<string[]>): void {
  145. moveItemInArray(this.spells, event.previousIndex, event.currentIndex);
  146. this.updateSpellsInDatabase();
  147. }
  148. public updateSpellsInDatabase(): void {
  149. this.dataAccessor.favoriteSpells = this.spells;
  150. }
  151. private computeSpellAttackBonusAndSaveDC(): void {
  152. let attackBonus =
  153. this.spellcastingAttributeModifier + this.proficiencyBonus;
  154. this.spellSaveDC = 8 + attackBonus;
  155. if (attackBonus >= 0) {
  156. this.spellAttackBonus = '+' + attackBonus;
  157. } else {
  158. this.spellAttackBonus = attackBonus.toString();
  159. }
  160. }
  161. private subscribeToData(): void {
  162. // TODO: this.dataAccessor.getSpellcastingAttribute() oder so
  163. this.spellcastingAttribute = 'wisdom';
  164. this.dataAccessor.proficiency$.subscribe((value) => {
  165. this.proficiencyBonus = value;
  166. if (this.spellcastingAttribute) {
  167. this.computeSpellAttackBonusAndSaveDC();
  168. }
  169. });
  170. // TODO: Modify depending on the actual spellcasting attribute
  171. this.dataAccessor.wisdom$.subscribe((value) => {
  172. this.spellcastingAttributeModifier = Math.floor((value.value - 10) / 2);
  173. this.computeSpellAttackBonusAndSaveDC();
  174. });
  175. }
  176. public search: OperatorFunction<string, readonly string[]> = (
  177. text$: Observable<string>
  178. ) =>
  179. text$.pipe(
  180. debounceTime(200),
  181. distinctUntilChanged(),
  182. map((term) =>
  183. term.length < 2
  184. ? []
  185. : this.preparedSpellsNames
  186. .filter((v) => v.toLowerCase().indexOf(term.toLowerCase()) > -1)
  187. .filter(
  188. (v) =>
  189. !this.spells.some(
  190. (spell) => spell.german.toLowerCase() === v.toLowerCase()
  191. )
  192. )
  193. .slice(0, 5)
  194. )
  195. );
  196. }