From a4391c84d047b3e498c84ffcf0229a9cdc079ef2 Mon Sep 17 00:00:00 2001 From: Chris Chen Date: Fri, 14 Nov 2025 07:37:48 -0800 Subject: [PATCH] Init Boss Fight --- src/app/games/games.module.ts | 4 + .../md2-boss-fight-editor.component.html | 144 +++++++++++++ .../md2-boss-fight-editor.component.scss | 1 + .../md2-boss-fight-editor.component.ts | 202 ++++++++++++++++++ .../md2-mob-info-detail.component.html | 1 + .../md2-mob-info-detail.component.ts | 53 ++++- .../md2-mob-info-editor.component.ts | 40 +++- .../md2-phase-buff-editor.component.html | 180 ++++++++++++++++ .../md2-phase-buff-editor.component.scss | 1 + .../md2-phase-buff-editor.component.ts | 114 ++++++++++ .../service/massive-darkness2.service.ts | 24 ++- 11 files changed, 758 insertions(+), 6 deletions(-) create mode 100644 src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-boss-fight-editor/md2-boss-fight-editor.component.html create mode 100644 src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-boss-fight-editor/md2-boss-fight-editor.component.scss create mode 100644 src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-boss-fight-editor/md2-boss-fight-editor.component.ts create mode 100644 src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-phase-buff-editor/md2-phase-buff-editor.component.html create mode 100644 src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-phase-buff-editor/md2-phase-buff-editor.component.scss create mode 100644 src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-phase-buff-editor/md2-phase-buff-editor.component.ts diff --git a/src/app/games/games.module.ts b/src/app/games/games.module.ts index 2440fb3..215ed5f 100644 --- a/src/app/games/games.module.ts +++ b/src/app/games/games.module.ts @@ -50,6 +50,8 @@ import { MD2MobInfoEditorComponent } from './massive-darkness2/md2-mob-info-main import { MD2MobInfoDetailComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-mob-info-detail/md2-mob-info-detail.component'; import { MD2MobSkillEditorComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-mob-skill-editor/md2-mob-skill-editor.component'; import { MD2MobLevelEditorComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-mob-level-editor/md2-mob-level-editor.component'; +import { MD2BossFightEditorComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-boss-fight-editor/md2-boss-fight-editor.component'; +import { MD2PhaseBuffEditorComponent } from './massive-darkness2/md2-mob-info-maintenance/md2-phase-buff-editor/md2-phase-buff-editor.component'; import { MD2HeroProfileMaintenanceComponent } from './massive-darkness2/md2-hero-profile-maintenance/md2-hero-profile-maintenance.component'; import { MD2HeroProfileEditorComponent } from './massive-darkness2/md2-hero-profile-maintenance/md2-hero-profile-editor/md2-hero-profile-editor.component'; @@ -87,6 +89,8 @@ import { MD2HeroProfileEditorComponent } from './massive-darkness2/md2-hero-prof MD2MobInfoDetailComponent, MD2MobSkillEditorComponent, MD2MobLevelEditorComponent, + MD2BossFightEditorComponent, + MD2PhaseBuffEditorComponent, MD2HeroProfileMaintenanceComponent, MD2HeroProfileEditorComponent ], diff --git a/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-boss-fight-editor/md2-boss-fight-editor.component.html b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-boss-fight-editor/md2-boss-fight-editor.component.html new file mode 100644 index 0000000..38963c5 --- /dev/null +++ b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-boss-fight-editor/md2-boss-fight-editor.component.html @@ -0,0 +1,144 @@ +
+ + + +
+
+
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+ + + +
+
+ +
+ + + + + + {{ dataItem.phase }} + + + + + + {{ dataItem.extraAction }} + + + + + + {{ dataItem.extraHp }} + + + + + + {{ dataItem.extraTokenCount }} + + + + + + {{ dataItem.extraTokenCount2 }} + + + + + +
+
+ - +
+
+ + + + + + + +
+
+
+
+
+
+ + + + + diff --git a/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-boss-fight-editor/md2-boss-fight-editor.component.scss b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-boss-fight-editor/md2-boss-fight-editor.component.scss new file mode 100644 index 0000000..f67848d --- /dev/null +++ b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-boss-fight-editor/md2-boss-fight-editor.component.scss @@ -0,0 +1 @@ +// Boss Fight Editor styles diff --git a/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-boss-fight-editor/md2-boss-fight-editor.component.ts b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-boss-fight-editor/md2-boss-fight-editor.component.ts new file mode 100644 index 0000000..e596fe1 --- /dev/null +++ b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-boss-fight-editor/md2-boss-fight-editor.component.ts @@ -0,0 +1,202 @@ +import { Component, Input, OnInit, ViewChild } from '@angular/core'; +import { DialogRef, DialogContentBase, DialogService } from '@progress/kendo-angular-dialog'; +import { GridComponent, GridDataResult } from '@progress/kendo-angular-grid'; +import { State } from '@progress/kendo-data-query'; +import { first } from 'rxjs/operators'; +import { BossFightProfile, BossFightPhaseBuff } from '../../massive-darkness2.db.model'; +import { MobSkillType } from '../../massive-darkness2.model.boss'; +import { MD2BossFightProfileService, MD2PhaseBuffService } from '../../service/massive-darkness2.service'; +import { MsgBoxService } from '../../../../services/msg-box.service'; +import { MD2PhaseBuffEditorComponent } from '../md2-phase-buff-editor/md2-phase-buff-editor.component'; + +@Component({ + selector: 'ngx-md2-boss-fight-editor', + templateUrl: './md2-boss-fight-editor.component.html', + styleUrls: ['./md2-boss-fight-editor.component.scss'] +}) +export class MD2BossFightEditorComponent extends DialogContentBase implements OnInit { + @Input() public data: BossFightProfile; + @Input() public mobInfoId: string; + @ViewChild('phaseBuffsGrid') phaseBuffsGrid: GridComponent; + + public model: BossFightProfile; + public phaseBuffs: BossFightPhaseBuff[] = []; + public phaseBuffsData: GridDataResult = { data: [], total: 0 }; + public phaseBuffsState: State = { + skip: 0, + take: 10, + sort: [], + filter: { + logic: 'and', + filters: [] + } + }; + public isLoading: boolean = false; + public processing: boolean = false; + + constructor( + public dialog: DialogRef, + private dialogService: DialogService, + private bossFightProfileService: MD2BossFightProfileService, + private phaseBuffService: MD2PhaseBuffService, + private msgBoxService: MsgBoxService + ) { + super(dialog); + } + + ngOnInit(): void { + this.initializeModel(); + } + + public initializeModel(): void { + this.model = { + id: this.data?.id || '', + mobInfoId: this.mobInfoId || this.data?.mobInfoId || '', + prerequisite: this.data?.prerequisite || '', + objective: this.data?.objective || '', + specialRules: this.data?.specialRules || '', + extraTokenName: this.data?.extraTokenName || '', + extraTokenHtml: this.data?.extraTokenHtml || '', + extraTokenName2: this.data?.extraTokenName2 || '', + extraTokenHtml2: this.data?.extraTokenHtml2 || '', + phaseBuffs: this.data?.phaseBuffs || [] + }; + + this.phaseBuffs = this.model.phaseBuffs || []; + this.loadPhaseBuffs(); + } + + public loadPhaseBuffs(): void { + this.phaseBuffsData = { + data: this.phaseBuffs.sort((a, b) => a.phase - b.phase), + total: this.phaseBuffs.length + }; + } + + public addPhaseBuffHandler(): void { + if (!this.model) return; + + const lastPhaseBuff = this.phaseBuffs.length > 0 + ? this.phaseBuffs.reduce((prev, current) => (prev.phase > current.phase) ? prev : current) + : null; + + const nextPhase = lastPhaseBuff ? lastPhaseBuff.phase + 1 : 1; + + const newPhaseBuff: BossFightPhaseBuff = { + id: this.generatePhaseBuffId(), + bossFightProfileId: this.model.id, + phase: nextPhase, + extraAction: 0, + extraAttackDice: { type: MobSkillType.Attack, yellow: null, orange: null, red: null, blue: null, green: null, black: null }, + extraDefenceDice: { type: MobSkillType.Defense, yellow: null, orange: null, red: null, blue: null, green: null, black: null }, + extraHp: 0, + extraTokenCount: 0, + extraTokenCount2: 0, + enableExtraBuffDescription: false, + extraBuffDescription: '' + }; + + this.openPhaseBuffEditor(newPhaseBuff, true); + } + + public editPhaseBuffHandler(dataItem: BossFightPhaseBuff): void { + if (!this.model) return; + + const phaseBuffCopy: BossFightPhaseBuff = JSON.parse(JSON.stringify(dataItem)); + this.openPhaseBuffEditor(phaseBuffCopy, false); + } + + private openPhaseBuffEditor(phaseBuff: BossFightPhaseBuff, isNew: boolean): void { + if (!this.model) return; + + const dialogRef = this.dialogService.open({ + title: isNew ? 'Add New Phase Buff' : 'Edit Phase Buff', + content: MD2PhaseBuffEditorComponent, + width: '80vw', + height: 700 + }); + + const editor = dialogRef.content.instance as MD2PhaseBuffEditorComponent; + editor.isAdding = isNew; + editor.data = phaseBuff; + editor.bossFightProfileId = this.model.id; + + setTimeout(() => { + editor.initializeModel(); + }, 0); + + dialogRef.result.subscribe(result => { + if (result && typeof result === 'object' && 'id' in result) { + this.handlePhaseBuffSaved(result as BossFightPhaseBuff, isNew); + } + }); + } + + private handlePhaseBuffSaved(result: BossFightPhaseBuff, isNew: boolean): void { + if (!this.model) return; + + if (isNew) { + if (!this.phaseBuffs) { + this.phaseBuffs = []; + } + this.phaseBuffs.push(result); + } else { + const index = this.phaseBuffs.findIndex(p => p.id === result.id); + if (index !== -1) { + this.phaseBuffs[index] = result; + } + } + + this.model.phaseBuffs = this.phaseBuffs; + this.loadPhaseBuffs(); + } + + public removePhaseBuffHandler({ dataItem }: { dataItem: BossFightPhaseBuff }): void { + this.msgBoxService.showConfirmDeleteBox().pipe(first()).subscribe(answer => { + if (answer === true) { + this.isLoading = true; + this.phaseBuffService.delete(dataItem.id).pipe(first()).subscribe(result => { + const index = this.phaseBuffs.findIndex(p => p.id === dataItem.id); + if (index !== -1) { + this.phaseBuffs.splice(index, 1); + this.model.phaseBuffs = this.phaseBuffs; + this.loadPhaseBuffs(); + } + this.isLoading = false; + }); + } + }); + } + + private generatePhaseBuffId(): string { + return 'phasebuff_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); + } + + public close(): void { + this.dialog.close(); + } + + public save(): void { + if (!this.processing && this.model) { + this.processing = true; + + this.model.phaseBuffs = this.phaseBuffs; + this.model.mobInfoId = this.mobInfoId || this.model.mobInfoId; + + this.bossFightProfileService.createOrUpdate(this.model).pipe(first()).subscribe(result => { + this.processing = false; + this.dialog.close(result); + }, error => { + this.processing = false; + console.error('Error saving boss fight profile:', error); + }); + } + } + + public get isValid(): boolean { + if (!this.model) { + return false; + } + return this.model.mobInfoId !== ''; + } +} diff --git a/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-mob-info-detail/md2-mob-info-detail.component.html b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-mob-info-detail/md2-mob-info-detail.component.html index dea2269..6721714 100644 --- a/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-mob-info-detail/md2-mob-info-detail.component.html +++ b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-mob-info-detail/md2-mob-info-detail.component.html @@ -183,5 +183,6 @@ + \ No newline at end of file diff --git a/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-mob-info-detail/md2-mob-info-detail.component.ts b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-mob-info-detail/md2-mob-info-detail.component.ts index 038d914..1f30029 100644 --- a/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-mob-info-detail/md2-mob-info-detail.component.ts +++ b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-mob-info-detail/md2-mob-info-detail.component.ts @@ -3,13 +3,15 @@ import { DialogRef, DialogContentBase, DialogService } from '@progress/kendo-ang import { GridComponent, GridDataResult } from '@progress/kendo-angular-grid'; import { State } from '@progress/kendo-data-query'; import { first } from 'rxjs/operators'; -import { MD2MobInfo, MD2MobLevelInfo, MD2MobSkill, MobSkillTarget, GameBundle } from '../../massive-darkness2.db.model'; +import { MD2MobInfo, MD2MobLevelInfo, MD2MobSkill, MobSkillTarget, GameBundle, BossFightProfile } from '../../massive-darkness2.db.model'; import { MobType } from '../../massive-darkness2.model'; import { MobSkillType } from '../../massive-darkness2.model.boss'; import { MD2MobLevelInfoService, MD2MobSkillService } from '../../service/massive-darkness2.service'; import { MsgBoxService } from '../../../../services/msg-box.service'; import { MD2MobSkillEditorComponent } from '../md2-mob-skill-editor/md2-mob-skill-editor.component'; import { MD2MobLevelEditorComponent } from '../md2-mob-level-editor/md2-mob-level-editor.component'; +import { MD2BossFightEditorComponent } from '../md2-boss-fight-editor/md2-boss-fight-editor.component'; +import { MD2MobInfoService } from '../../service/massive-darkness2.service'; @Component({ selector: 'ngx-md2-mob-info-detail', @@ -18,6 +20,7 @@ import { MD2MobLevelEditorComponent } from '../md2-mob-level-editor/md2-mob-leve }) export class MD2MobInfoDetailComponent extends DialogContentBase implements OnInit { MobSkillType = MobSkillType; + MobType = MobType; @Input() public mobInfo: MD2MobInfo; @ViewChild('levelsGrid') levelsGrid: GridComponent; @ViewChild('skillsGrid') skillsGrid: GridComponent; @@ -54,6 +57,7 @@ export class MD2MobInfoDetailComponent extends DialogContentBase implements OnIn private dialogService: DialogService, private mobLevelInfoService: MD2MobLevelInfoService, private mobSkillService: MD2MobSkillService, + private mobInfoService: MD2MobInfoService, private msgBoxService: MsgBoxService ) { super(dialog); @@ -434,6 +438,53 @@ export class MD2MobInfoDetailComponent extends DialogContentBase implements OnIn return GameBundle[bundle] || ''; } + public editBossFightHandler(): void { + if (!this.mobInfo || this.mobInfo.type !== MobType.Boss) return; + + // Ensure bossFightProfile is initialized + if (!this.mobInfo.bossFightProfile) { + this.mobInfo.bossFightProfile = { + id: 'bossfight_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9), + mobInfoId: this.mobInfo.id, + prerequisite: '', + objective: '', + specialRules: '', + extraTokenName: '', + extraTokenHtml: '', + extraTokenName2: '', + extraTokenHtml2: '', + phaseBuffs: [] + }; + } + + // Create a copy of the boss fight profile for editing + const bossFightProfileCopy: BossFightProfile = JSON.parse(JSON.stringify(this.mobInfo.bossFightProfile)); + + const dialogRef = this.dialogService.open({ + title: `Edit Boss Fight: ${this.mobInfo.name}`, + content: MD2BossFightEditorComponent, + width: '90vw', + height: '90vh' + }); + + const editor = dialogRef.content.instance as MD2BossFightEditorComponent; + editor.data = bossFightProfileCopy; + editor.mobInfoId = this.mobInfo.id; + + setTimeout(() => { + editor.initializeModel(); + }, 0); + + dialogRef.result.subscribe(result => { + if (result && typeof result === 'object' && 'id' in result) { + // Reload mob info to get updated boss fight profile + this.mobInfoService.getById(this.mobInfo.id).pipe(first()).subscribe(mobInfo => { + this.mobInfo = mobInfo; + }); + } + }); + } + public close(): void { this.dialog.close(true); } diff --git a/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-mob-info-editor/md2-mob-info-editor.component.ts b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-mob-info-editor/md2-mob-info-editor.component.ts index d0ad810..50cd28f 100644 --- a/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-mob-info-editor/md2-mob-info-editor.component.ts +++ b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-mob-info-editor/md2-mob-info-editor.component.ts @@ -2,7 +2,7 @@ import { Component, Input, OnInit, ViewChild, ChangeDetectorRef } from '@angular import { DialogRef, DialogContentBase } from '@progress/kendo-angular-dialog'; import { NgForm } from '@angular/forms'; import { first } from 'rxjs/operators'; -import { MD2MobInfo, GameBundle } from '../../massive-darkness2.db.model'; +import { MD2MobInfo, GameBundle, BossFightProfile } from '../../massive-darkness2.db.model'; import { MobType } from '../../massive-darkness2.model'; import { MD2MobInfoService } from '../../service/massive-darkness2.service'; @@ -48,9 +48,15 @@ export class MD2MobInfoEditorComponent extends DialogContentBase implements OnIn leaderImgUrl: this.data?.leaderImgUrl || '', minionImgUrl: this.data?.minionImgUrl || '', mobLevelInfos: this.data?.mobLevelInfos || [], - skills: this.data?.skills || [] + skills: this.data?.skills || [], + bossFightProfile: this.data?.bossFightProfile }; + // Initialize bossFightProfile for Boss type mobs if not already set + if (typeValue === MobType.Boss && !this.model.bossFightProfile) { + this.model.bossFightProfile = this.createDefaultBossFightProfile(this.model.id); + } + // Set selected objects for dropdowns this.selectedMobType = this.mobTypes.find(t => t.value === typeValue) || this.mobTypes[0] || null; this.selectedGameBundle = this.gameBundles.find(b => b.value === fromValue) || this.gameBundles[0] || null; @@ -58,6 +64,21 @@ export class MD2MobInfoEditorComponent extends DialogContentBase implements OnIn this.cdr.detectChanges(); } + private createDefaultBossFightProfile(mobInfoId: string): BossFightProfile { + return { + id: 'bossfight_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9), + mobInfoId: mobInfoId || '', + prerequisite: '', + objective: '', + specialRules: '', + extraTokenName: '', + extraTokenHtml: '', + extraTokenName2: '', + extraTokenHtml2: '', + phaseBuffs: [] + }; + } + private initializeEnums(): void { // Initialize MobType options @@ -85,13 +106,24 @@ export class MD2MobInfoEditorComponent extends DialogContentBase implements OnIn if (this.model.name && !this.processing) { this.processing = true; + const mobType = this.selectedMobType?.value ?? MobType.Mob; + + // Ensure bossFightProfile is initialized for Boss type + let bossFightProfile = this.model.bossFightProfile; + if (mobType === MobType.Boss && !bossFightProfile) { + bossFightProfile = this.createDefaultBossFightProfile(this.model.id); + } else if (mobType !== MobType.Boss) { + bossFightProfile = undefined; + } + // Extract enum values from selected objects const mobInfo: MD2MobInfo = { ...this.model, - type: this.selectedMobType?.value ?? MobType.Mob, + type: mobType, from: this.selectedGameBundle?.value ?? GameBundle.CoreGame, mobLevelInfos: this.data?.mobLevelInfos || [], - skills: this.data?.skills || [] + skills: this.data?.skills || [], + bossFightProfile: bossFightProfile }; this.mobInfoService.createOrUpdate(mobInfo).pipe(first()).subscribe(result => { diff --git a/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-phase-buff-editor/md2-phase-buff-editor.component.html b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-phase-buff-editor/md2-phase-buff-editor.component.html new file mode 100644 index 0000000..1f8eeba --- /dev/null +++ b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-phase-buff-editor/md2-phase-buff-editor.component.html @@ -0,0 +1,180 @@ +
+
+ +
+
+
+ + + +
+
+ +
+
+ + + +
+
+ +
+
+ + + +
+
+ +
+
+ + + +
+
+ +
+
+ + + +
+
+ +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ +
+
+
Extra Attack Dice
+
+
+
+ + + + + + + + + +
+
+
+
+ + + +
+
+
+
+ + + +
+
+
+
+ + + +
+
+
+
+ + + +
+
+
+ +
+
+
Extra Defence Dice
+
+
+
+ + + + + + + + + +
+
+
+
+ + + +
+
+
+
+ + + +
+
+
+
+ + + +
+
+
+
+
+ + + + + diff --git a/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-phase-buff-editor/md2-phase-buff-editor.component.scss b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-phase-buff-editor/md2-phase-buff-editor.component.scss new file mode 100644 index 0000000..4a987b3 --- /dev/null +++ b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-phase-buff-editor/md2-phase-buff-editor.component.scss @@ -0,0 +1 @@ +// Phase Buff Editor styles diff --git a/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-phase-buff-editor/md2-phase-buff-editor.component.ts b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-phase-buff-editor/md2-phase-buff-editor.component.ts new file mode 100644 index 0000000..090e6ec --- /dev/null +++ b/src/app/games/massive-darkness2/md2-mob-info-maintenance/md2-phase-buff-editor/md2-phase-buff-editor.component.ts @@ -0,0 +1,114 @@ +import { Component, Input, OnInit, ViewChild, ChangeDetectorRef } from '@angular/core'; +import { DialogRef, DialogContentBase } from '@progress/kendo-angular-dialog'; +import { NgForm } from '@angular/forms'; +import { first } from 'rxjs/operators'; +import { BossFightPhaseBuff, MD2DiceSet } from '../../massive-darkness2.db.model'; +import { MobSkillType } from '../../massive-darkness2.model.boss'; +import { MD2PhaseBuffService } from '../../service/massive-darkness2.service'; +import { MD2Icon } from '../../massive-darkness2.model'; +import { MD2Service } from '../../../../services/MD2/md2.service'; + +@Component({ + selector: 'ngx-md2-phase-buff-editor', + templateUrl: './md2-phase-buff-editor.component.html', + styleUrls: ['./md2-phase-buff-editor.component.scss'] +}) +export class MD2PhaseBuffEditorComponent extends DialogContentBase implements OnInit { + MD2Icon = MD2Icon; + MobSkillType = MobSkillType; + @Input() public data: BossFightPhaseBuff; + @Input() public bossFightProfileId: string; + @Input() public isAdding: boolean = false; + @ViewChild('form') form: NgForm; + + public model: BossFightPhaseBuff; + public processing: boolean = false; + public attackTypes: Array<{ value: MobSkillType; text: string }> = []; + public selectedAttackType: { value: MobSkillType; text: string } | null = null; + public selectedDefenceType: { value: MobSkillType; text: string } | null = null; + + constructor( + public dialog: DialogRef, + private phaseBuffService: MD2PhaseBuffService, + private cdr: ChangeDetectorRef, + private md2Service: MD2Service + ) { + super(dialog); + } + + ngOnInit(): void { + this.initializeModel(); + this.initializeEnums(); + } + + public initializeEnums(): void { + this.attackTypes = [ + { value: MobSkillType.Attack, text: 'None' }, + { value: MobSkillType.MeleeAttack, text: this.md2Service.iconHtml(MD2Icon.Melee) + ' Melee Attack' }, + { value: MobSkillType.RangeAttack, text: this.md2Service.iconHtml(MD2Icon.Range) + ' Range Attack' }, + { value: MobSkillType.MagicAttack, text: this.md2Service.iconHtml(MD2Icon.Magic) + ' Magic Attack' }, + { value: MobSkillType.Defense, text: this.md2Service.iconHtml(MD2Icon.Defense) + ' Defense' } + ]; + this.selectedAttackType = this.attackTypes.find(t => t.value === this.model.extraAttackDice?.type) || this.attackTypes[0] || null; + this.selectedDefenceType = this.attackTypes.find(t => t.value === this.model.extraDefenceDice?.type) || this.attackTypes[4] || null; + } + + public initializeModel(): void { + this.model = { + id: this.data?.id || '', + bossFightProfileId: this.bossFightProfileId || this.data?.bossFightProfileId || '', + phase: this.data?.phase ?? 1, + extraAction: this.data?.extraAction ?? 0, + extraAttackDice: this.data?.extraAttackDice + ? { ...this.data.extraAttackDice } + : { type: MobSkillType.Attack, yellow: null, orange: null, red: null, blue: null, green: null, black: null }, + extraDefenceDice: this.data?.extraDefenceDice + ? { ...this.data.extraDefenceDice } + : { type: MobSkillType.Defense, yellow: null, orange: null, red: null, blue: null, green: null, black: null }, + extraHp: this.data?.extraHp ?? 0, + extraTokenCount: this.data?.extraTokenCount ?? 0, + extraTokenCount2: this.data?.extraTokenCount2 ?? 0, + enableExtraBuffDescription: this.data?.enableExtraBuffDescription ?? false, + extraBuffDescription: this.data?.extraBuffDescription || '' + }; + + this.cdr.detectChanges(); + } + + public close(): void { + this.dialog.close(); + } + + public save(): void { + if (!this.processing) { + this.processing = true; + + // Ensure required objects exist + if (!this.model.extraAttackDice) { + this.model.extraAttackDice = { type: MobSkillType.Attack, yellow: null, orange: null, red: null, blue: null, green: null, black: null }; + } + if (!this.model.extraDefenceDice) { + this.model.extraDefenceDice = { type: MobSkillType.Defense, yellow: null, orange: null, red: null, blue: null, green: null, black: null }; + } + + this.model.extraAttackDice.type = this.selectedAttackType?.value ?? MobSkillType.Attack; + this.model.extraDefenceDice.type = this.selectedDefenceType?.value ?? MobSkillType.Defense; + this.model.bossFightProfileId = this.bossFightProfileId || this.model.bossFightProfileId; + + this.phaseBuffService.createOrUpdate(this.model).pipe(first()).subscribe(result => { + this.processing = false; + this.dialog.close(result); + }, error => { + this.processing = false; + console.error('Error saving phase buff:', error); + }); + } + } + + public get isValid(): boolean { + if (!this.model) { + return false; + } + return this.model.phase > 0 && this.model.bossFightProfileId !== ''; + } +} diff --git a/src/app/games/massive-darkness2/service/massive-darkness2.service.ts b/src/app/games/massive-darkness2/service/massive-darkness2.service.ts index efc3519..279be6c 100644 --- a/src/app/games/massive-darkness2/service/massive-darkness2.service.ts +++ b/src/app/games/massive-darkness2/service/massive-darkness2.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { CrudService } from '../../../services/crudServices/crud.service'; -import { MD2MobInfo, MD2MobLevelInfo, MD2MobSkill } from '../massive-darkness2.db.model'; +import { MD2MobInfo, MD2MobLevelInfo, MD2MobSkill, BossFightProfile, BossFightPhaseBuff } from '../massive-darkness2.db.model'; import { HttpClient } from '@angular/common/http'; import { MD2HeroProfile } from '../massive-darkness2.model'; @@ -47,4 +47,26 @@ export class MD2HeroProfileService extends CrudService { super(http, (action: string = null) => { return `MD2HeroProfile${(action ? `/${action}` : '')}` }); } +} + +@Injectable({ + providedIn: 'root' +}) +export class MD2BossFightProfileService extends CrudService { + + constructor(http: HttpClient) { + super(http, (action: string = null) => { return `MD2BossFightProfile${(action ? `/${action}` : '')}` }); + } + +} + +@Injectable({ + providedIn: 'root' +}) +export class MD2PhaseBuffService extends CrudService { + + constructor(http: HttpClient) { + super(http, (action: string = null) => { return `MD2BossFightPhaseBuff${(action ? `/${action}` : '')}` }); + } + } \ No newline at end of file