From d486fe959433e311b5355ac46d716be6d67a9f77 Mon Sep 17 00:00:00 2001 From: Chris Chen Date: Fri, 29 Mar 2024 08:04:07 -0700 Subject: [PATCH] Update boss fight --- src/app/app.module.ts | 4 + src/app/games/games.module.ts | 4 +- src/app/games/massive-darkness2/MD2Base.ts | 71 ++- .../boss-activation.component.html | 30 +- .../boss-activation.component.ts | 30 +- .../boss-fight/boss-fight.component.html | 25 +- .../boss-fight/boss-fight.component.ts | 11 +- .../door-events/door-events.component.ts | 2 +- .../massive-darkness2/factorys/md2-clone.ts | 20 + .../factorys/mobs/CoreGame.ts | 18 +- .../factorys/roamingMonsters/CoreGame.ts | 403 ++++++++++++------ .../hero-dashboard.component.html | 72 ++-- .../hero-dashboard.component.ts | 50 ++- .../massive-darkness2.component.html | 5 +- .../massive-darkness2.component.ts | 22 +- .../massive-darkness2.logic.ts | 69 +++ .../massive-darkness2.model.boss.ts | 400 ++++++++++++----- .../massive-darkness2.model.ts | 94 ++-- .../md2-icon/md2-icon.component.html | 2 +- .../md2-icon/md2-icon.component.ts | 20 +- .../mob-attack-info.component.html | 52 +-- .../mob-attack-info.component.ts | 18 +- .../mob-combat-info.component.html | 27 +- .../mob-combat-info.component.scss | 9 + .../mob-combat-info.component.ts | 16 +- .../mob-def-info/mob-def-info.component.html | 32 +- .../mob-def-info/mob-def-info.component.ts | 15 + .../mob-detail-info.component.html | 62 ++- .../mob-detail-info.component.ts | 22 +- .../mob-stand-info.component.html | 7 + .../mob-stand-info.component.scss | 23 + .../mob-stand-info.component.spec.ts | 25 ++ .../mob-stand-info.component.ts | 69 +++ .../mobs/mobs.component.html | 11 +- .../mobs/mobs.component.scss | 77 ---- .../massive-darkness2/mobs/mobs.component.ts | 82 ++-- .../spawn-mob-dlg.component.html | 16 +- .../spawn-mob-dlg/spawn-mob-dlg.component.ts | 40 +- .../treasure-bag/treasure-bag.component.ts | 4 +- src/app/services/MD2/md2-broadcast.service.ts | 12 +- src/app/services/MD2/md2-init.service.ts | 51 +++ src/app/services/MD2/md2-mob.service.spec.ts | 16 + src/app/services/MD2/md2-mob.service.ts | 140 ++++++ src/app/services/MD2/md2-state.service.ts | 8 +- src/app/services/MD2/md2.service.ts | 251 +++++------ .../adjacent-number-input.component.html | 2 +- .../adjacent-number-input.component.scss | 3 + .../adjacent-number-input.component.ts | 13 +- src/app/utilities/array-utils.ts | 18 + src/assets/styles/globals.scss | 36 +- src/assets/styles/globals/_heights.scss | 57 +++ .../styles/globals/_typo-text-styles.scss | 4 + src/assets/styles/md2.scss | 34 +- src/assets/styles/site.scss | 2 +- src/environments/environment.ts | 2 +- 55 files changed, 1809 insertions(+), 799 deletions(-) create mode 100644 src/app/games/massive-darkness2/factorys/md2-clone.ts create mode 100644 src/app/games/massive-darkness2/massive-darkness2.logic.ts create mode 100644 src/app/games/massive-darkness2/mobs/mob-stand-info/mob-stand-info.component.html create mode 100644 src/app/games/massive-darkness2/mobs/mob-stand-info/mob-stand-info.component.scss create mode 100644 src/app/games/massive-darkness2/mobs/mob-stand-info/mob-stand-info.component.spec.ts create mode 100644 src/app/games/massive-darkness2/mobs/mob-stand-info/mob-stand-info.component.ts create mode 100644 src/app/services/MD2/md2-init.service.ts create mode 100644 src/app/services/MD2/md2-mob.service.spec.ts create mode 100644 src/app/services/MD2/md2-mob.service.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 54430e1..d3cba49 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -70,6 +70,10 @@ const socialLinks = [ NbChatModule.forRoot({ messageGoogleMapKey: 'AIzaSyA_wNuCzia92MAmdLRemailRGvCF7wCZPY', }), + NbDialogModule.forRoot({ + closeOnBackdropClick: false, + closeOnEsc: false + }), NgxMaskModule.forRoot(maskConfig), NbDateFnsDateModule.forRoot({ format: 'MM/dd/yyyy' }), CoreModule.forRoot(), diff --git a/src/app/games/games.module.ts b/src/app/games/games.module.ts index d5a2dd1..590a4e2 100644 --- a/src/app/games/games.module.ts +++ b/src/app/games/games.module.ts @@ -32,6 +32,7 @@ import { BossActivationComponent } from './massive-darkness2/boss-fight/boss-act import { MobAttackInfoComponent } from './massive-darkness2/mobs/mob-detail-info/mob-attack-info/mob-attack-info.component'; import { MobDefInfoComponent } from './massive-darkness2/mobs/mob-detail-info/mob-def-info/mob-def-info.component'; import { MobCombatInfoComponent } from './massive-darkness2/mobs/mob-detail-info/mob-combat-info/mob-combat-info.component'; +import { MobStandInfoComponent } from './massive-darkness2/mobs/mob-stand-info/mob-stand-info.component'; @NgModule({ @@ -58,7 +59,8 @@ import { MobCombatInfoComponent } from './massive-darkness2/mobs/mob-detail-info BossActivationComponent, MobAttackInfoComponent, MobDefInfoComponent, - MobCombatInfoComponent + MobCombatInfoComponent, + MobStandInfoComponent ], imports: [ CommonModule, diff --git a/src/app/games/massive-darkness2/MD2Base.ts b/src/app/games/massive-darkness2/MD2Base.ts index 6871373..b1a950f 100644 --- a/src/app/games/massive-darkness2/MD2Base.ts +++ b/src/app/games/massive-darkness2/MD2Base.ts @@ -37,6 +37,9 @@ export abstract class MD2Base { } + }); + this.md2Service.refreshUI$.pipe(takeUntil(this.destroy$)).subscribe(result => { + this.cdRef.detectChanges(); }); this.stateService.loginUserService.signalRInitialized.pipe(first()).subscribe(result => { console.log('signalRInitialized'); @@ -78,9 +81,15 @@ export abstract class MD2Base { } abstract refreshUI(); handleSignalRCallback(message: SignalRMessage): void { + if (message.from.isGroup) { + if (!this.isHeroDashboard) return; + } else { + if (this.isHeroDashboard && this.md2Service.playerHero.playerInfo.signalRClientId == message.from.sessionId) return; + } + switch (message.actionType) { case 'hero': - let heroInfo = JSON.parse(message.parameters['hero']) as MD2HeroInfo; + let heroInfo = new MD2HeroInfo(JSON.parse(message.parameters['hero'])); switch (message.actionName) { case 'join': this.md2Service.heros.push(heroInfo); @@ -88,26 +97,43 @@ export abstract class MD2Base { case 'update': let exitingHero = this.md2Service.heros.find(h => h.playerInfo.signalRClientId == heroInfo.playerInfo.signalRClientId); if (exitingHero) { - Object.keys(heroInfo).forEach(key => exitingHero[key] = heroInfo[key]); + let activateBoss = exitingHero.uiActivating && !heroInfo.uiActivating; + + this.md2Service.heros[this.md2Service.heros.indexOf(exitingHero)] = heroInfo; + if (this.isHeroDashboard && this.md2Service.stateService.playerHero.playerInfo.tabId == heroInfo.playerInfo.tabId) { + this.md2Service.stateService.playerHero = heroInfo; + } + if (!this.isHeroDashboard && this.md2Service.info.isBossFight && activateBoss) { + this.md2Service.activateBoss(); + } } else { this.md2Service.heros.push(heroInfo); } if (!this.isHeroDashboard) { if (this.gameInfo.roundPhase == RoundPhase.HeroPhase) { - if (!this.md2Service.heros.some(h => h.remainActions > 0)) { - if (this.md2Service.mobs.length > 0 || this.md2Service.roamingMonsters.length > 0) { - this.md2Service.msgBoxService.show('Enemy Phase', { icon: ADIcon.WARNING }).pipe(first()).subscribe(result => { + if (!this.md2Service.heros.some(h => h.remainActions > 0) && !this.md2Service.heros.some(h => h.uiActivating)) { + if (!this.md2Service.info.isBossFight) { + if (this.md2Service.mobs.length > 0 || this.md2Service.roamingMonsters.length > 0) { + this.md2Service.msgBoxService.show('Enemy Phase', { icon: ADIcon.WARNING }).pipe(first()).subscribe(result => { + this.md2Service.runNextPhase(); + }); + } else { this.md2Service.runNextPhase(); - }); - } else { - this.md2Service.runNextPhase(); + } } + } } } //Object.assign(heroInfo, exitingHero); + break; + case 'updateMyHero': + if (this.isHeroDashboard) { + this.md2Service.stateService.playerHero = heroInfo; + } + break; default: break; @@ -126,6 +152,12 @@ export abstract class MD2Base { this.detectChanges(); } break; + case 'phase': + if (this.isHeroDashboard) { + this.md2Service.info.roundPhase = JSON.parse(message.parameters['phase']); + this.detectChanges(); + } + break; default: break; } @@ -160,17 +192,21 @@ export abstract class MD2Base { break; case 'heroAction': if (!this.isHeroDashboard) { - this.gameInfo.currentActivateHero = this.md2Service.heros.find(h => h.playerInfo.tabId == message.parameters['tabId']); + //this.md2Service.currentActivateHero = this.md2Service.heros.find(h => h.playerInfo.tabId == message.parameters['tabId']); switch (message.actionName) { case 'attackAction': - this.gameInfo.showAttackBtn = true; + if (this.gameInfo.isBossFight) { + this.md2Service.heroAttackingSubject.next(this.md2Service.currentActivateHero); + } else { + this.gameInfo.showAttackBtn = true; + } break; case 'openDoor': //Door component listen for it break; case 'tradeAction': this.md2Service.msgBoxService.show('Trade and Equip', { - text: `every one in the same zone with ${this.md2Service.heroFullName(this.gameInfo.currentActivateHero)} may freely trade and + text: `every one in the same zone with ${this.md2Service.heroFullName(this.md2Service.currentActivateHero)} may freely trade and equip items!`, icon: ADIcon.INFO }); @@ -179,7 +215,7 @@ export abstract class MD2Base { //this.md2Service.roundPhase = Number.parseInt(message.parameters['phase']); break; } - this.heroAction(this.gameInfo.currentActivateHero, message.actionName); + this.heroAction(this.md2Service.currentActivateHero, message.actionName); this.detectChanges(); } break; @@ -227,4 +263,15 @@ export abstract class MD2ComponentBase { iconHtml(icon: MD2Icon, cssClass = '') { return this.md2Service.stateService.iconHtml(icon, cssClass); } + detectChanges() { + if (!this.cdRef['destroyed']) { + this.cdRef.detectChanges(); + this.refreshUI(); + this.md2Service.refreshUI$.next(); + } + + } + refreshUI() { + + } } \ No newline at end of file diff --git a/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.html b/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.html index 9a758fc..94ec154 100644 --- a/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.html +++ b/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.html @@ -1,25 +1,31 @@ - -
-
- + +
+
+
-
diff --git a/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.ts b/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.ts index 38bafe7..dc8e73c 100644 --- a/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.ts +++ b/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.ts @@ -1,10 +1,12 @@ import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { NbDialogRef } from '@nebular/theme'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { MD2Service } from '../../../../services/MD2/md2.service'; import { MsgBoxService } from '../../../../services/msg-box.service'; import { StateService } from '../../../../services/state.service'; -import { MobDlgType, MD2Icon, MD2HeroInfo } from '../../massive-darkness2.model'; +import { MobDlgType, MD2Icon, MD2HeroInfo, RoundPhase } from '../../massive-darkness2.model'; import { MobSkill, IBossFight } from '../../massive-darkness2.model.boss'; import { MD2ComponentBase } from '../../MD2Base'; import { SpawnMobDlgComponent } from '../../mobs/spawn-mob-dlg/spawn-mob-dlg.component'; @@ -14,12 +16,13 @@ import { SpawnMobDlgComponent } from '../../mobs/spawn-mob-dlg/spawn-mob-dlg.com templateUrl: './boss-activation.component.html', styleUrls: ['./boss-activation.component.scss'] }) -export class BossActivationComponent extends MD2ComponentBase implements OnInit { +export class BossActivationComponent implements OnInit { boss: IBossFight; bossAction: MobSkill; - + currentAction: number; + allActions: number; MobDlgType = MobDlgType; - mode: MobDlgType; + mode: MobDlgType = MobDlgType.Activating; title: string; titleHtml: string; @@ -31,16 +34,29 @@ export class BossActivationComponent extends MD2ComponentBase implements OnInit otherAttackTarget: string; constructor( private dlgRef: NbDialogRef, - private msgBoxService: MsgBoxService, public md2Service: MD2Service, protected stateService: StateService, protected route: ActivatedRoute, protected cdRef: ChangeDetectorRef, ) { - super(md2Service, stateService, route, cdRef); + this.md2Service.refreshUI$.pipe(takeUntil(this.destroy$)).subscribe(result => { + if (!this.cdRef['destroyed']) { + this.cdRef.detectChanges(); + } + }); + } + private destroy$: Subject = new Subject(); + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + ngOnInit(): void { + } close() { - this.boss.standUrl + //this.boss.standUrl + this.md2Service.info.roundPhase = RoundPhase.HeroPhase; this.dlgRef.close(); } diff --git a/src/app/games/massive-darkness2/boss-fight/boss-fight.component.html b/src/app/games/massive-darkness2/boss-fight/boss-fight.component.html index 5a57960..6a2b0a5 100644 --- a/src/app/games/massive-darkness2/boss-fight/boss-fight.component.html +++ b/src/app/games/massive-darkness2/boss-fight/boss-fight.component.html @@ -1,5 +1,5 @@ - + {{boss.name}} @@ -9,13 +9,24 @@
- - +
+
+ + + + + +
+
+
+
- - - +
+ + +
@@ -39,63 +41,57 @@
- - - - -
- + - + - + - + (blur)="heroUpdateDebounceTimer.resetTimer()" *ngIf="hero.class==HeroClass.Berserker"> - +
-
+
+ + @@ -121,7 +123,7 @@
-
diff --git a/src/app/games/massive-darkness2/massive-darkness2.component.ts b/src/app/games/massive-darkness2/massive-darkness2.component.ts index 9edf77b..53ad224 100644 --- a/src/app/games/massive-darkness2/massive-darkness2.component.ts +++ b/src/app/games/massive-darkness2/massive-darkness2.component.ts @@ -15,6 +15,7 @@ import { QRCodeService } from '../../services/qrcode.service'; import { StringUtils } from '../../utilities/string-utils'; import { SpawnMobDlgComponent } from './mobs/spawn-mob-dlg/spawn-mob-dlg.component'; import { BossMicheal } from './massive-darkness2.model.boss'; +import { MD2InitService } from '../../services/MD2/md2-init.service'; @Component({ selector: 'ngx-massive-darkness2', @@ -26,6 +27,7 @@ export class MassiveDarkness2Component extends MD2Base implements OnInit { HeroClass: HeroClass constructor( private fileService: FileService, + private initService: MD2InitService, private msgBoxService: MsgBoxService, private qrCodeService: QRCodeService, public gameRoomService: GameRoomService, @@ -42,7 +44,8 @@ export class MassiveDarkness2Component extends MD2Base implements OnInit { this.md2Service.enemyPhaseSubject.pipe(takeUntil(this.destroy$)).subscribe(result => { this.showEnemyPhaseAction(0); }); - + this.initService.initMobDecks(); + this.initService.initTreasureBag(); } override signalRInitialized() { @@ -64,7 +67,7 @@ export class MassiveDarkness2Component extends MD2Base implements OnInit { } showEnemyPhaseAction(index: number) { - let mob = new MobInfo(this.md2Service.enemyPhaseMobs[index]); + let mob = this.md2Service.enemyPhaseMobs[index]; let enemyInfo = `
`; let extraRule = ''; @@ -85,7 +88,13 @@ export class MassiveDarkness2Component extends MD2Base implements OnInit { // break; // } - this.msgBoxService.dlgService.open(SpawnMobDlgComponent, { context: { title: `Enemy Phase(${(index + 1)}/${this.md2Service.enemyPhaseMobs.length})`, mode: MobDlgType.Activating, mob: mob } }) + this.msgBoxService.dlgService.open(SpawnMobDlgComponent, { + context: { + title: `Enemy Phase(${(index + 1)}/${this.md2Service.enemyPhaseMobs.length})`, + mode: MobDlgType.Activating, + mob: mob + } + }) .onClose.pipe(first()).subscribe(result => { index++; if (index < this.md2Service.enemyPhaseMobs.length) { @@ -125,10 +134,7 @@ export class MassiveDarkness2Component extends MD2Base implements OnInit { } enterBossFight() { - this.msgBoxService.showInputbox('Boss Fight', 'Choose the boss').pipe(first()).subscribe(result => { - this.md2Service.info.isBossFight = true; - this.md2Service.info.boss = new BossMicheal(this.md2Service); - this.detectChanges(); - }); + this.md2Service.enterBossFight(); + } } \ No newline at end of file diff --git a/src/app/games/massive-darkness2/massive-darkness2.logic.ts b/src/app/games/massive-darkness2/massive-darkness2.logic.ts new file mode 100644 index 0000000..0106c95 --- /dev/null +++ b/src/app/games/massive-darkness2/massive-darkness2.logic.ts @@ -0,0 +1,69 @@ +import { Observable, Subject } from "rxjs"; +import { environment } from "../../../environments/environment"; +import { MsgBoxService } from "../../services/msg-box.service"; +import { ObjectUtils } from "../../utilities/object-utils"; +import { StringUtils } from "../../utilities/string-utils"; +import { GamePlayer } from "../games.model"; +import { MD2HeroInfo, AttackTarget, HeroClass } from "./massive-darkness2.model"; +import { MobSkill } from "./massive-darkness2.model.boss"; +export class MD2Logic { + + public static getTargetHerosByFilter(heros: MD2HeroInfo[], targetType: AttackTarget, onlyOne: boolean = false) { + let beenAttackedHero = [] as MD2HeroInfo[]; + switch (targetType) { + case AttackTarget.LeastHp: + let lowestHp = Math.min(...heros.map(h => h.hp)); + beenAttackedHero = heros.filter(h => h.hp == lowestHp); + //this.otherAttackTarget = 'attacking the other Lowest HP hero.'; + break; + case AttackTarget.LeastMp: + let lowestMp = Math.min(...heros.map(h => h.mp)); + beenAttackedHero = heros.filter(h => h.hp == lowestMp); + //this.otherAttackTarget = 'attacking the other Lowest HP hero.'; + break; + + case AttackTarget.HighestHp: + let highestHp = Math.max(...heros.map(h => h.hp)); + beenAttackedHero = heros.filter(h => h.hp == highestHp); + //this.otherAttackTarget = 'attacking the other Highest HP hero.'; + break; + case AttackTarget.HighestMp: + let highestMp = Math.max(...heros.map(h => h.mp)); + beenAttackedHero = heros.filter(h => h.mp == highestMp); + //this.otherAttackTarget = 'attacking the other Highest Mp hero.'; + break; + case AttackTarget.LowestLevel: + let lowestLevel = Math.max(...heros.map(h => h.level)); + beenAttackedHero = heros.filter(h => h.level == lowestLevel); + //this.otherAttackTarget = 'attacking the other Lowest Level hero.'; + break; + case AttackTarget.LeastCorruption: + + let leastCor = Math.min(...heros.map(h => h.corruptionToken)); + beenAttackedHero = heros.filter(h => h.corruptionToken == leastCor); + break; + case AttackTarget.MostCorruption: + let mostCor = Math.max(...heros.map(h => h.corruptionToken)); + beenAttackedHero = heros.filter(h => h.corruptionToken == mostCor); + break; + case AttackTarget.Random: + default: + beenAttackedHero = [heros[Math.round(Math.random() * (heros.length - 1))]]; + //this.otherAttackTarget = 'Just act like normal.'; + break; + } + if (onlyOne && beenAttackedHero.length > 1) { + beenAttackedHero = [beenAttackedHero[Math.round(Math.random() * (beenAttackedHero.length - 1))]]; + } + return beenAttackedHero; + } + + public static getTargetHerosHtml(beenAttackedHero: MD2HeroInfo[]) { + return `${StringUtils.makeCommaSeparatedString(beenAttackedHero.map(h => this.heroFullName(h)), false, true)}`; + } + + public static heroFullName(hero: MD2HeroInfo) { + if (!hero) return ''; + return `${hero.playerInfo.name} (${HeroClass[hero.class]} - ${hero.name})` + } +} \ No newline at end of file diff --git a/src/app/games/massive-darkness2/massive-darkness2.model.boss.ts b/src/app/games/massive-darkness2/massive-darkness2.model.boss.ts index d4b8430..342e9aa 100644 --- a/src/app/games/massive-darkness2/massive-darkness2.model.boss.ts +++ b/src/app/games/massive-darkness2/massive-darkness2.model.boss.ts @@ -1,5 +1,5 @@ import { stringify } from "querystring" -import { Subject } from "rxjs" +import { Observable, Subject, Subscription } from "rxjs" import { first } from "rxjs/operators" import { MD2Service } from "../../services/MD2/md2.service" import { StringUtils } from "../../utilities/string-utils" @@ -8,121 +8,6 @@ import { TreasureType, AttackInfo, DefenseInfo, AttackType, MD2Icon, MD2HeroInfo import { RollingBlackDice } from "./massive-darkness2.model.dice" -export interface IBossFight { - name: string - addTreasureToken: Subject - spawnMob: Subject - spawnRoamingMonster: Subject - rounds: number - actions: number - hpPerHero: number - info: MobInfo - actionBlackDice: number - imgUrl: string - standUrl: string - activating(): boolean - prepareForBossFight(): void - nextRound(): void -} -export class BossMicheal implements IBossFight { - - constructor(private md2Service: MD2Service) { - this.corruptionTokenHtml = this.md2Service.stateService.imgHtml('Tokens/CorruptToken.png'); - - this.name = 'Michael - The Corrupted Archangel'; - this.imgUrl = md2Service.stateService.imgUrl('/Boss/Michael - The Corrupted Archangel.jpg'); - this.standUrl = md2Service.stateService.imgUrl('/Boss/Michael.png'); - this.hpPerHero = 15; - this.info = new MobInfo({ - description: this.name, - type: MobType.Boss, - hp: this.md2Service.heros.length * this.hpPerHero, - unitRemainHp: this.md2Service.heros.length * this.hpPerHero, - level: 10, - combatSkill: new MobSkill(`Combat 1 ${this.md2Service.stateService.iconHtml(MD2Icon.EnemySkill)}`, - `Deal 1 Wound for each ${this.corruptionTokenHtml} on the attacking or defending Hero. Discard the tokens afterwards(once per combat).`), - imageUrl: md2Service.stateService.imgUrl('/Boss/Michael.png') - }); - this.info.defenseInfo = new DefenseInfo(5, 1); - this.info.attackInfos = [new AttackInfo(MD2Icon.Melee, 2, 2, 0, 1)]; - this.actions = 1; - this.rounds = 0; - this.actionBlackDice = 2; - } - name: string - addTreasureToken: Subject - spawnMob: Subject - spawnRoamingMonster: Subject - rounds: number - actions: number - hpPerHero: number - info: MobInfo - actionBlackDice: number - imgUrl: string - standUrl: string - corruptionTokenHtml: string - activating(): boolean { - - let actionResult = new RollingBlackDice().roll(this.actionBlackDice); - let actionHtml = ''; - let beenAttackedHero = [] as MD2HeroInfo[]; - let bossAction: MobSkill; - switch (actionResult.claws) { - case 0: - //Justice From Above - beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.MostCorruption, true); - bossAction = new MobSkill('Justice From Above', - `Place Michael in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.`); - - break; - case 1: - //Lance Dash - beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.LeastCorruption, true); - bossAction = new MobSkill('Lance Dash', - `Move Michael and Place 1 ${this.corruptionTokenHtml} in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.`); - break; - case 2: - //Dark Blessing - bossAction = new MobSkill('Dark Blessing', - `Place Michael on the central Zone and add 1 ${this.corruptionTokenHtml} to the Corruption Stone Zone with the least amount of ${this.corruptionTokenHtml}.
` + - `Deal ${this.darkBlessingCorruptionAmt} Wounds per ${this.corruptionTokenHtml} to all Heros in each Tiles distributed as they wish.`); - break; - - default: - break; - } - this.md2Service.dlgService.open(BossActivationComponent, { context: { boss: this, bossAction: bossAction } }).onClose - .pipe(first()).subscribe(result => { - - }); - return true; - } - prepareForBossFight(): void { - - } - darkBlessingCorruptionAmt: number = 1; - nextRound(): void { - this.rounds++; - switch (this.rounds) { - case 3: - case 5: - this.darkBlessingCorruptionAmt++; - break; - case 2: - case 4: - this.info.defenseInfo[0].black += 1; - this.info.attackInfos[0].black += 1; - break; - // case 4: - // this.defInfo.black += 2; - // this.atkInfos[0].black += 2; - // break; - - default: - break; - } - } -} export enum MobSkillType { Attack, Defense, @@ -146,4 +31,287 @@ export class MobSkill { name: string description: string targetHeros: MD2HeroInfo[] +} +export interface IBossFight { + name: string + addTreasureToken: Subject + spawnMob: Subject + spawnRoamingMonster: Subject + rounds: number + actions: number + activatedTimes: number + info: MobInfo + actionBlackDice: number + imgUrl: string + standUrl: string + extraRules: string + activating(): boolean + prepareForBossFight(): void + darknessPhase(): void +} + +export abstract class BossFight implements IBossFight { + name: string + addTreasureToken: Subject + spawnMob: Subject + spawnRoamingMonster: Subject + rounds: number + actions: number + activatedTimes: number + info: MobInfo + actionBlackDice: number + imgUrl: string + standUrl: string + extraRules: string + protected subscription: Subscription + + constructor(protected md2Service: MD2Service) { + this.rounds = 1; + } + activating(): boolean { + this.activatedTimes = this.actions; + this.runAction(); + return true; + } + + runAction() { + this.bossAction().pipe(first()).subscribe(result => { + this.activatedTimes--; + if (this.activatedTimes) { + this.runAction(); + } else { + if (false == this.md2Service.heros.some(h => h.remainActions > 0)) { + this.md2Service.darknessPhase(); + } + } + }); + } + abstract bossAction(): Observable; + protected actionEnd + prepareForBossFight(): void { + throw new Error("Method not implemented.") + } + darknessPhase(): void { + throw new Error("Method not implemented.") + } + +} +export class BossMicheal extends BossFight { + constructor(protected md2Service: MD2Service) { + super(md2Service); + this.corruptionTokenHtml = this.md2Service.stateService.imgHtml('Tokens/CorruptToken.png'); + + this.name = 'Michael - The Corrupted Archangel'; + this.imgUrl = md2Service.stateService.imgUrl('/Boss/Michael - The Corrupted Archangel.jpg'); + this.standUrl = md2Service.stateService.imgUrl('/Boss/Michael.png'); + + this.info = new MobInfo({ + description: this.name, + type: MobType.Boss, + hpPerHero: 15, + level: 10, + combatSkill: new MobSkill( + { + name: `Combat 1 ${this.md2Service.stateService.iconHtml(MD2Icon.EnemySkill)}`, + description: `Deal 1 Wound for each ${this.corruptionTokenHtml} on the attacking or defending Hero. Discard the tokens afterwards(once per combat).` + }), + imageUrl: md2Service.stateService.imgUrl('/Boss/Michael.png') + }); + this.info.defenseInfo = new DefenseInfo(5, 1); + this.info.attackInfos = [new AttackInfo(MD2Icon.Melee, 2, 2, 0, 1)]; + this.actions = 1; + this.actionBlackDice = 2; + + this.extraRules = `Archangel Michael can’t be the target of any attack, skill, ability or take Wounds until there are no Corruption tokens in the whole Tile.

` + + `Any Hero on a Zone with a ${this.corruptionTokenHtml} may spend 1 action to remove it. Each time a Hero removes a ${this.corruptionTokenHtml} from a Zone they must roll 1 ${this.md2Service.stateService.iconHtml(MD2Icon.BlackDice)}.` + + `If ${this.md2Service.stateService.iconHtml(MD2Icon.EnemyClaw)} the Hero takes 1 Wound.
If ${this.md2Service.stateService.iconHtml(MD2Icon.EnemySkill)} place 1 ${this.corruptionTokenHtml} on their Dashboard.
` + + `If ${this.md2Service.stateService.iconHtml(MD2Icon.EnemyClaw)}/${this.md2Service.stateService.iconHtml(MD2Icon.EnemySkill)} the Hero takes 1 Wound and places 1 ${this.corruptionTokenHtml} on their Dashboard.` + } + activatedTimes: number + acted: number + name: string + addTreasureToken: Subject + spawnMob: Subject + spawnRoamingMonster: Subject + rounds: number + actions: number + info: MobInfo + actionBlackDice: number + imgUrl: string + standUrl: string + corruptionTokenHtml: string + + bossAction(): Observable { + + let actionResult = new RollingBlackDice().roll(this.actionBlackDice); + let actionHtml = ''; + let beenAttackedHero = [] as MD2HeroInfo[]; + let bossAction: MobSkill; + switch (actionResult.claws) { + case 0: + //Justice From Above + beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.MostCorruption, true); + bossAction = new MobSkill( + { + name: 'Justice From Above', + description: `Place Michael in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.` + }); + + break; + case 1: + //Lance Dash + beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.LeastCorruption, true); + bossAction = new MobSkill({ + name: 'Lance Dash', + description: + `Move Michael and Place 1 ${this.corruptionTokenHtml} in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.` + }); + break; + case 2: + //Dark Blessing + bossAction = new MobSkill({ + name: 'Dark Blessing', + description: + `Place Michael on the central Zone and add 1 ${this.corruptionTokenHtml} to the Corruption Stone Zone with the least amount of ${this.corruptionTokenHtml}.
` + + `Deal ${this.darkBlessingCorruptionAmt} Wounds per ${this.corruptionTokenHtml} to all Heros in each Tiles distributed as they wish.` + }); + break; + + default: + break; + } + return this.md2Service.dlgService.open(BossActivationComponent, { context: { boss: this, bossAction: bossAction, currentAction: this.activatedTimes, allActions: this.actions } }).onClose; + + } + prepareForBossFight(): void { + this.md2Service.heros.forEach(hero => { + hero.uiShowCorruptionToken = true; + }); + this.md2Service.msgBoxService.show('Prepare Boss Fight', { + text: `
Place ${this.md2Service.heros.length * 2} ${this.corruptionTokenHtml} on the Corruption Stone Zones (Shadow + Zones).
Players choose the Zones, but must distribute + the tokens as equally as possible among them.
` + }); + } + darkBlessingCorruptionAmt: number = 1; + darknessPhase(): void { + this.rounds++; + switch (this.rounds) { + case 3: + case 5: + this.darkBlessingCorruptionAmt++; + break; + case 2: + case 4: + this.info.defenseInfo.black += 1; + this.info.attackInfos[0].black += 1; + break; + // case 4: + // this.defInfo.black += 2; + // this.atkInfos[0].black += 2; + // break; + + default: + break; + } + } +} + + +export class BossReaper extends BossFight { + + constructor(protected md2Service: MD2Service) { + super(md2Service); + this.timeTokenHtml = this.md2Service.stateService.imgHtml('Tokens/TimeToken.png'); + + this.name = 'The Reaper'; + this.imgUrl = md2Service.stateService.imgUrl('/Boss/The Reaper.jpg'); + this.standUrl = md2Service.stateService.imgUrl('/Boss/The Reaper-Stand.png'); + + this.info = new MobInfo({ + description: this.name, + type: MobType.Boss, + hpPerHero: 25, + level: 10, + combatSkill: new MobSkill( + { + description: `If the Hero has no ${this.md2Service.stateService.iconHtml(MD2Icon.Mana)}, they take 1 ${this.md2Service.stateService.iconHtml(MD2Icon.Frost)}`, + type: MobSkillType.Attack, + + }), + imageUrl: md2Service.stateService.imgUrl('/Boss/The Reaper-Stand.png') + }); + this.info.defenseInfo = new DefenseInfo(4, 3); + this.info.attackInfos = [new AttackInfo(MD2Icon.Melee, 1, 2, 0, 3)]; + this.actions = 1; + this.actionBlackDice = 2; + this.extraRules = `A Hero standing in the Hourglass Zone may spend 1 action to add 1 ${this.timeTokenHtml} in each Hourglass Zone.`; + } + name: string + addTreasureToken: Subject + spawnMob: Subject + spawnRoamingMonster: Subject + rounds: number + actions: number + info: MobInfo + actionBlackDice: number + imgUrl: string + standUrl: string + timeTokenHtml: string + bossAction() { + + let actionResult = new RollingBlackDice().roll(this.actionBlackDice); + let actionHtml = ''; + let beenAttackedHero = [] as MD2HeroInfo[]; + let bossAction: MobSkill; + switch (actionResult.claws) { + case 0: + //Justice From Above + beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.LeastMp, true); + bossAction = new MobSkill( + { + name: 'Soul Drain', + description: `Place The Reaper in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.` + }); + + break; + case 1: + //Lance Dash + beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.LeastCorruption, true); + bossAction = new MobSkill({ + name: 'Time Ticking', + description: + `Place The Reaper in the Hourglass Zone withe the least ${this.timeTokenHtml} and remove 1 ${this.timeTokenHtml} from The OtherHourglass Zone.` + }); + break; + case 2: + //Dark Blessing + bossAction = new MobSkill({ + name: 'Death Is Coming', + description: + `Place The Reaper in the central Zone.
` + + `Roll 1 ${this.md2Service.stateService.iconHtml(MD2Icon.YellowDice)}. Remove ${this.timeTokenHtml} equal to ${this.md2Service.stateService.iconHtml(MD2Icon.Melee)} rolled from both Hourglass Zone.
` + + `Each Hero discards ${this.md2Service.stateService.iconHtml(MD2Icon.MP)} equal to ${this.md2Service.stateService.iconHtml(MD2Icon.Melee)} rolled.` + }); + break; + + default: + break; + } + return this.md2Service.dlgService.open(BossActivationComponent, { context: { boss: this, bossAction: bossAction, currentAction: this.activatedTimes, allActions: this.actions } }).onClose; + } + prepareForBossFight(): void { + this.md2Service.msgBoxService.show('Prepare Boss Fight', { + text: `
Place 2 ${this.timeTokenHtml} in each Hourglass Zone.
` + }) + } + darkBlessingCorruptionAmt: number = 1; + darknessPhase(): void { + this.rounds++; + if (this.rounds > 4) { + this.actions = 3; + } else if (this.rounds > 1) { + this.actions = 2; + } + } } \ No newline at end of file diff --git a/src/app/games/massive-darkness2/massive-darkness2.model.ts b/src/app/games/massive-darkness2/massive-darkness2.model.ts index d5a98c5..07b2b31 100644 --- a/src/app/games/massive-darkness2/massive-darkness2.model.ts +++ b/src/app/games/massive-darkness2/massive-darkness2.model.ts @@ -1,7 +1,9 @@ -import { Subject } from "rxjs"; +import { Observable, Subject } from "rxjs"; import { environment } from "../../../environments/environment"; +import { MsgBoxService } from "../../services/msg-box.service"; import { ObjectUtils } from "../../utilities/object-utils"; import { GamePlayer } from "../games.model"; +import { MD2Clone } from "./factorys/md2-clone"; import { MobSkill } from "./massive-darkness2.model.boss"; const MD2_IMG_URL = (id: string = null) => { return `${environment.apiUrl}/Files/Images/MD2/${(id ? `${encodeURI(id)}` : '')}` } @@ -9,7 +11,8 @@ export enum MobDlgType { Spawn, Activating, BeenAttacked, - PreView + PreView, + Dashboard } export enum RoundPhase { HeroPhase, @@ -31,6 +34,7 @@ export enum HeroClass { Ranger, Shaman, Paladin, + Druid, } export enum MobType { Mob, @@ -68,14 +72,16 @@ export enum MD2Icon { RedDice, BlueDice, YellowDice, - OrangeDice + OrangeDice, + BlackDice } export enum AttackTarget { Random = 40, - LowestHp = 50, - HighestHp = 60, - HighestMp = 70, - LowestLevel = 80, + LeastHp = 50, + LeastMp = 60, + HighestHp = 70, + HighestMp = 80, + LowestLevel = 90, MostCorruption = 200, LeastCorruption = 201 } @@ -132,8 +138,12 @@ export class DrawingBag { } drawingItems: IDrawingItem[] removedItems: IDrawingItem[] - public bagIsEmpty() { - return this.drawingItems.reduce((sum, current) => sum + current.drawingWeight, 0) == 0; + public bagIsEmpty(predicate: (value: T) => boolean = undefined) { + if (predicate) { + return this.drawingItems.filter(predicate).reduce((sum, current) => sum + current.drawingWeight, 0) == 0; + } else { + return this.drawingItems.reduce((sum, current) => sum + current.drawingWeight, 0) == 0; + } } public Draw(amount: number): T[] { @@ -144,7 +154,7 @@ export class DrawingBag { public DrawAndRemove(amount: number = 1, predicate: (value: T) => boolean = undefined): T[] { let drawItems: T[] = []; for (let i = 0; i < amount; i++) { - if (!this.bagIsEmpty()) { + if (!this.bagIsEmpty(predicate)) { let drawItem = null as T; let drawingPool = [] as T[]; if (predicate) { @@ -159,7 +169,7 @@ export class DrawingBag { const item = drawingPool[i]; drawCalc += item.drawingWeight; if (drawCalc >= drawIndex) { - drawItem = ObjectUtils.CloneValue(item); + drawItem = MD2Clone.CloneDrawingItem(item); drawItem.drawingWeight = 1; break; } @@ -183,7 +193,7 @@ export class DrawingBag { this.removedItems = []; } public AddItem(item: IDrawingItem) { - let existingItem = this.drawingItems.find(i => i.name == item.name); + let existingItem = this.drawingItems.find(i => i.identifyName == item.identifyName); if (existingItem) { existingItem.drawingWeight += item.drawingWeight; } else { @@ -193,17 +203,34 @@ export class DrawingBag { public RemoveItem(item: IDrawingItem) { if (item) { - let existingItem = this.drawingItems.find(i => i.name == item.name); - if (existingItem) { - existingItem.drawingWeight -= item.drawingWeight; - let removedItem = this.removedItems.find(i => i.name == item.name); - if (removedItem) { - removedItem.drawingWeight += item.drawingWeight; - } else { - this.removedItems.push(item); + if (item.identifyName) { + let existingItem = this.drawingItems.find(i => i.identifyName == item.identifyName); + if (existingItem) { + existingItem.drawingWeight -= item.drawingWeight; + + let removedItem = this.removedItems.find(i => i.identifyName == item.identifyName); + if (removedItem) { + removedItem.drawingWeight += item.drawingWeight; + } else { + this.removedItems.push(item); + } } + } else { + let existingItem = this.drawingItems.find(i => i.name == item.name); + if (existingItem) { + existingItem.drawingWeight -= item.drawingWeight; + + let removedItem = this.removedItems.find(i => i.name == item.name); + if (removedItem) { + removedItem.drawingWeight += item.drawingWeight; + } else { + this.removedItems.push(item); + } + } + } + } } @@ -214,11 +241,13 @@ export class DrawingBag { } } export interface IMobFactory { + mobName: string; generate(level: number): MobInfo; } export interface IDrawingItem { imageUrl: string name: string + get identifyName(): string description: string drawingWeight: number } @@ -234,6 +263,9 @@ export class DrawingItem implements IDrawingItem { this.description = description this.drawingWeight = drawingWeight } + get identifyName(): string { + return this.name; + } imageUrl: string name: string description: string @@ -249,6 +281,9 @@ export class TreasureItem extends DrawingItem { } type: TreasureType; itemAmount: number; + get identifyName(): string { + return this.name; + } } export class MobInfo implements IDrawingItem { constructor( @@ -270,13 +305,15 @@ export class MobInfo implements IDrawingItem { level: number; rewardTokens: number; hp: number; + hpPerHero: number; mobAmount: number; carriedTreasure: TreasureItem[]; fixedCarriedTreasure: TreasureItem[]; unitRemainHp: number; attackInfos: AttackInfo[]; defenseInfo: DefenseInfo; - + actions: number = 0; + activateDescription: string; combatSkill: MobSkill fireToken: number = 0; @@ -288,6 +325,9 @@ export class MobInfo implements IDrawingItem { uiCorruptionTokens: number; uiAttackedBy: string; + get identifyName(): string { + return `${this.name}_${this.level}`; + } public get carriedTreasureHtml(): string { if (!this.carriedTreasure) return ''; return this.carriedTreasure.map(i => ``) @@ -327,7 +367,8 @@ export class MobInfo implements IDrawingItem { return 0; } } - + public actionSubject: Subject; + public activateFunction?: (mob: MobInfo, msgBoxService: MsgBoxService, heros: MD2HeroInfo[]) => void; } export class MD2HeroInfo { @@ -354,10 +395,12 @@ export class MD2HeroInfo { shadowSkillHtml: string; remainActions: number = 3; rage: number = 0; - + uiActivating = false; + uiShowCorruptionToken = false; + uiBossFight = false; public get heroFullName(): string { - return `${this.playerInfo.name} (${HeroClass[this.class]} - ${this.name}` + return `${this.playerInfo.name} (${HeroClass[this.class]} - ${this.name})` } } @@ -403,6 +446,9 @@ export class MD2EnemyPhaseSpecialRule implements IDrawingItem { this.title = title this.description = description } + get identifyName(): string { + return this.name; + } imageUrl: string; name: string; drawingWeight: number; diff --git a/src/app/games/massive-darkness2/md2-icon/md2-icon.component.html b/src/app/games/massive-darkness2/md2-icon/md2-icon.component.html index b6d0770..ac3e628 100644 --- a/src/app/games/massive-darkness2/md2-icon/md2-icon.component.html +++ b/src/app/games/massive-darkness2/md2-icon/md2-icon.component.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/app/games/massive-darkness2/md2-icon/md2-icon.component.ts b/src/app/games/massive-darkness2/md2-icon/md2-icon.component.ts index 2a93e19..97a9df8 100644 --- a/src/app/games/massive-darkness2/md2-icon/md2-icon.component.ts +++ b/src/app/games/massive-darkness2/md2-icon/md2-icon.component.ts @@ -10,10 +10,26 @@ export class MD2IconComponent implements OnInit { @Input() iconClass: string = 'mr-1'; - @Input("icon") icon: string; + private _icon: string | MD2Icon; + + @Input() public set icon(v: string | MD2Icon) { + if (this._icon != v) { + this._icon = v; + + } + if (this.isMD2Icon(v)) { + this.iconName = MD2Icon[v].toLowerCase(); + } else { + this.iconName = v; + } + } + + isMD2Icon(icon: MD2Icon | string): icon is MD2Icon { + return Number.isInteger(icon); + } @Input() size: string = 'sm'; - + iconName: string; constructor() { } ngOnInit(): void { diff --git a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-attack-info/mob-attack-info.component.html b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-attack-info/mob-attack-info.component.html index eb6b62b..12646d4 100644 --- a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-attack-info/mob-attack-info.component.html +++ b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-attack-info/mob-attack-info.component.html @@ -1,28 +1,30 @@ - -
-
- -
-
-
- - x{{info.yellow}} - + + +
+
+
-
- - x{{info.orange}} - -
-
- - x{{info.red}} - -
-
- - x{{mob.defenseInfo.black}} - +
+
+ + x{{info.yellow}} + +
+
+ + x{{info.orange}} + +
+
+ + x{{info.red}} + +
+
+ + x{{mob.defenseInfo.black}} + +
-
\ No newline at end of file +
\ No newline at end of file diff --git a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-attack-info/mob-attack-info.component.ts b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-attack-info/mob-attack-info.component.ts index 55bb80c..8e88f9a 100644 --- a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-attack-info/mob-attack-info.component.ts +++ b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-attack-info/mob-attack-info.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnInit } from '@angular/core'; -import { MD2Icon, MobDlgType, MobInfo } from '../../../massive-darkness2.model'; +import { MD2Icon, MobDlgType, MobInfo, MobType } from '../../../massive-darkness2.model'; @Component({ selector: 'md2-mob-attack-info', @@ -22,9 +22,23 @@ export class MobAttackInfoComponent implements OnInit { } @Input() mode: MobDlgType = MobDlgType.PreView; - constructor() { } + display: boolean = false; + constructor() { } ngOnInit(): void { + switch (this.mode) { + case MobDlgType.Spawn: + this.display = this.mob.type != MobType.Mob; + break; + case MobDlgType.Activating: + case MobDlgType.PreView: + case MobDlgType.Dashboard: + this.display = true; + break; + default: + this.display = false; + break; + } } } diff --git a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-combat-info/mob-combat-info.component.html b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-combat-info/mob-combat-info.component.html index ae8bbd3..c0fe413 100644 --- a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-combat-info/mob-combat-info.component.html +++ b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-combat-info/mob-combat-info.component.html @@ -1,5 +1,24 @@ -
- -
+ +
+ +
+ +
+
+ + x{{mob.minionAmount}} + +
+
+
+ +
\ No newline at end of file diff --git a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-combat-info/mob-combat-info.component.scss b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-combat-info/mob-combat-info.component.scss index b3eb934..c5aa151 100644 --- a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-combat-info/mob-combat-info.component.scss +++ b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-combat-info/mob-combat-info.component.scss @@ -17,3 +17,12 @@ left: 26px; font-size: 30px; } +.skillDesc { + padding-left: 8px; + .MD2Icon { + font-size: 45px; + } +} +.skillDesc .MD2Icon { + font-size: 45px; +} diff --git a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-combat-info/mob-combat-info.component.ts b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-combat-info/mob-combat-info.component.ts index 526af4e..93d9c54 100644 --- a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-combat-info/mob-combat-info.component.ts +++ b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-combat-info/mob-combat-info.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnInit } from '@angular/core'; -import { MD2Icon, MobDlgType, MobInfo } from '../../../massive-darkness2.model'; +import { MD2Icon, MobDlgType, MobInfo, MobType } from '../../../massive-darkness2.model'; import { MobSkillType } from '../../../massive-darkness2.model.boss'; @Component({ @@ -22,6 +22,9 @@ export class MobCombatInfoComponent implements OnInit { } } @Input() mode: MobDlgType = MobDlgType.PreView; + showSkill: boolean = false; + showBlackDice: boolean + skillTriggerHtml: string = ''; constructor() { } ngOnInit(): void { @@ -34,14 +37,17 @@ export class MobCombatInfoComponent implements OnInit { this.showSkill = [MobSkillType.Combat, MobSkillType.Defense].includes(this.mob.combatSkill.type); break; case MobDlgType.PreView: - case MobDlgType.Spawn: - default: this.showSkill = true; break; + case MobDlgType.Spawn: + default: + this.showSkill = false; + break; } + this.skillTriggerHtml = `${MobSkillType[this.mob.combatSkill.type]} ${this.mob.combatSkill.skillRoll} ` } - + this.showBlackDice = this.mob.type == MobType.Mob && (this.mode == MobDlgType.Activating || this.mode == MobDlgType.BeenAttacked) && this.mob.minionAmount > 0;; } - showSkill: boolean = false;; + } diff --git a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-def-info/mob-def-info.component.html b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-def-info/mob-def-info.component.html index 23b30d3..a7999f9 100644 --- a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-def-info/mob-def-info.component.html +++ b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-def-info/mob-def-info.component.html @@ -1,18 +1,20 @@ - -
-
- -
-
-
- - x{{mob.defenseInfo.blue}} - + + +
+
+
-
- - x{{mob.defenseInfo.black}} - +
+
+ + x{{mob.defenseInfo.blue}} + +
+
+ + x{{mob.defenseInfo.black}} + +
-
\ No newline at end of file +
\ No newline at end of file diff --git a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-def-info/mob-def-info.component.ts b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-def-info/mob-def-info.component.ts index bdc2444..ec84b00 100644 --- a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-def-info/mob-def-info.component.ts +++ b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-def-info/mob-def-info.component.ts @@ -1,5 +1,6 @@ import { Component, Input, OnInit } from '@angular/core'; import { MD2Icon, MobInfo, MobDlgType } from '../../../massive-darkness2.model'; +import { MobSkillType } from '../../../massive-darkness2.model.boss'; @Component({ selector: 'md2-mob-def-info', @@ -21,8 +22,22 @@ export class MobDefInfoComponent implements OnInit { } } @Input() mode: MobDlgType = MobDlgType.PreView; + + display: boolean = false; + constructor() { } ngOnInit(): void { + switch (this.mode) { + case MobDlgType.BeenAttacked: + case MobDlgType.PreView: + case MobDlgType.Spawn: + case MobDlgType.Dashboard: + this.display = true; + break; + default: + this.display = false; + break; + } } } diff --git a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-detail-info.component.html b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-detail-info.component.html index 8c1fe23..84840b3 100644 --- a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-detail-info.component.html +++ b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-detail-info.component.html @@ -1,27 +1,53 @@ -
+
+
- -
+
+ + +
+
+ + +
+
+
+ +
+ + +
+
+ + + +
+
+ + + +
+ +
+ + + + + + -

- +
+ - - -
- -
- -
-
- - x{{mob.minionAmount}} - -
-
\ No newline at end of file + + \ No newline at end of file diff --git a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-detail-info.component.ts b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-detail-info.component.ts index 879d1de..c4fdcc4 100644 --- a/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-detail-info.component.ts +++ b/src/app/games/massive-darkness2/mobs/mob-detail-info/mob-detail-info.component.ts @@ -1,5 +1,6 @@ import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; +import { MD2MobService } from '../../../../services/MD2/md2-mob.service'; import { MD2Service } from '../../../../services/MD2/md2.service'; import { StateService } from '../../../../services/state.service'; import { MD2Icon, MobDlgType, MobInfo } from '../../massive-darkness2.model'; @@ -39,11 +40,9 @@ export class MobDetailInfoComponent extends MD2ComponentBase implements OnInit { this.showAttackingInfo = typeof value !== "undefined" && value !== false; } - public get showBlackDice(): boolean { - return (this.mode == MobDlgType.Activating || this.mode == MobDlgType.BeenAttacked) && this.mob.minionAmount > 0; - } constructor( + private mobService: MD2MobService, public md2Service: MD2Service, protected stateService: StateService, protected route: ActivatedRoute, @@ -64,4 +63,21 @@ export class MobDetailInfoComponent extends MD2ComponentBase implements OnInit { } } + + public get showAdjustment(): boolean { + return [MobDlgType.Activating, MobDlgType.BeenAttacked, MobDlgType.Dashboard].includes(this.mode); + } + public get showTokenAdjustment(): boolean { + return [MobDlgType.Activating, MobDlgType.BeenAttacked].includes(this.mode); + } + + adjustMobHp(adjustAmount: number) { + if (adjustAmount < 0) { + this.mobService.attackMob(this.mob, 1); + } else { + this.mobService.healMob(this.mob, 1); + } + this.md2Service.refreshUI$.next(); + } + } \ No newline at end of file diff --git a/src/app/games/massive-darkness2/mobs/mob-stand-info/mob-stand-info.component.html b/src/app/games/massive-darkness2/mobs/mob-stand-info/mob-stand-info.component.html new file mode 100644 index 0000000..2683d32 --- /dev/null +++ b/src/app/games/massive-darkness2/mobs/mob-stand-info/mob-stand-info.component.html @@ -0,0 +1,7 @@ + + +
+ + + +
\ No newline at end of file diff --git a/src/app/games/massive-darkness2/mobs/mob-stand-info/mob-stand-info.component.scss b/src/app/games/massive-darkness2/mobs/mob-stand-info/mob-stand-info.component.scss new file mode 100644 index 0000000..6cb626a --- /dev/null +++ b/src/app/games/massive-darkness2/mobs/mob-stand-info/mob-stand-info.component.scss @@ -0,0 +1,23 @@ +.mobImg { + position: absolute; + z-index: 2; + &.roamingMonster { + width: 95%; + max-height: 80%; + } + &.mobLeader { + z-index: 3; + width: 70%; + max-height: 80%; + top: 40px; + } + &.mobMinion { + width: 60%; + max-height: 80%; + right: 0; + } +} +.mobBg { + position: absolute; + z-index: 1; +} diff --git a/src/app/games/massive-darkness2/mobs/mob-stand-info/mob-stand-info.component.spec.ts b/src/app/games/massive-darkness2/mobs/mob-stand-info/mob-stand-info.component.spec.ts new file mode 100644 index 0000000..4ed7f29 --- /dev/null +++ b/src/app/games/massive-darkness2/mobs/mob-stand-info/mob-stand-info.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MobStandInfoComponent } from './mob-stand-info.component'; + +describe('MobStandInfoComponent', () => { + let component: MobStandInfoComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MobStandInfoComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MobStandInfoComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/games/massive-darkness2/mobs/mob-stand-info/mob-stand-info.component.ts b/src/app/games/massive-darkness2/mobs/mob-stand-info/mob-stand-info.component.ts new file mode 100644 index 0000000..cbf2850 --- /dev/null +++ b/src/app/games/massive-darkness2/mobs/mob-stand-info/mob-stand-info.component.ts @@ -0,0 +1,69 @@ +import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { first } from 'rxjs/operators'; +import { MD2Service } from '../../../../services/MD2/md2.service'; +import { StateService } from '../../../../services/state.service'; +import { StringUtils } from '../../../../utilities/string-utils'; +import { MD2Icon, MobInfo, MobDlgType, MobType } from '../../massive-darkness2.model'; +import { MD2ComponentBase } from '../../MD2Base'; +import { SpawnMobDlgComponent } from '../spawn-mob-dlg/spawn-mob-dlg.component'; + +@Component({ + selector: 'md2-mob-stand-info', + templateUrl: './mob-stand-info.component.html', + styleUrls: ['./mob-stand-info.component.scss'] +}) +export class MobStandInfoComponent extends MD2ComponentBase implements OnInit { + + MD2Icon = MD2Icon; + private _mob: MobInfo; + public get mob(): MobInfo { + return this._mob; + } + + @Input() public set mob(v: MobInfo) { + if (this._mob != v) { + this._mob = v; + + } + } + + @Input() mode: MobDlgType = MobDlgType.PreView; + public get hideWeaponInfo(): boolean { + return this.mode == MobDlgType.Spawn; + } + + + constructor( + public md2Service: MD2Service, + protected stateService: StateService, + protected route: ActivatedRoute, + protected cdRef: ChangeDetectorRef, + ) { + super(md2Service, stateService, route, cdRef); + } + + ngOnInit(): void { + } + + public getMobImageUrl(mob: MobInfo): string { + if (StringUtils.isNullOrWhitespace(mob.leaderImgUrl)) { + return mob.imageUrl; + } else { + return mob.leaderImgUrl; + } + } + + + showMobImage(mob: MobInfo) { + this.md2Service.dlgService.open(SpawnMobDlgComponent, { context: { title: `${mob.description}`, mode: MobDlgType.PreView, mob: mob } }) + .onClose.pipe(first()).subscribe(result => { + + }); + } + + public get isMob(): boolean { + return this.mob.type == MobType.Mob; + } + +} diff --git a/src/app/games/massive-darkness2/mobs/mobs.component.html b/src/app/games/massive-darkness2/mobs/mobs.component.html index a65e63a..9676635 100644 --- a/src/app/games/massive-darkness2/mobs/mobs.component.html +++ b/src/app/games/massive-darkness2/mobs/mobs.component.html @@ -28,21 +28,16 @@
- - +
- + - - + *ngIf="md2Service.info.showAttackBtn">Attack It +
+
+ + +
+
@@ -71,7 +79,7 @@
-
+
diff --git a/src/app/games/massive-darkness2/mobs/spawn-mob-dlg/spawn-mob-dlg.component.ts b/src/app/games/massive-darkness2/mobs/spawn-mob-dlg/spawn-mob-dlg.component.ts index 32e0145..70855b6 100644 --- a/src/app/games/massive-darkness2/mobs/spawn-mob-dlg/spawn-mob-dlg.component.ts +++ b/src/app/games/massive-darkness2/mobs/spawn-mob-dlg/spawn-mob-dlg.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { NbDialogRef } from '@nebular/theme'; +import { first } from 'rxjs/operators'; import { FileService } from '../../../../services/file.service'; import { MD2Service } from '../../../../services/MD2/md2.service'; import { MsgBoxService } from '../../../../services/msg-box.service'; @@ -23,7 +24,7 @@ export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit { titleHtml: string; MD2Icon = MD2Icon; mob: MobInfo; - + actionInfoHtml: string; beenAttackedHero = [] as MD2HeroInfo[]; attackTarget: string; otherAttackTarget: string; @@ -41,6 +42,13 @@ export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit { //this.mob = new MobInfo(this.mob); if (this.mode == MobDlgType.Spawn && this.mob.type == MobType.Mob) { this.mob.attackInfos = [new AttackInfo(MD2Icon.Melee), new AttackInfo(MD2Icon.Range), new AttackInfo(MD2Icon.Magic)]; + } else if (this.mode == MobDlgType.Activating) { + if (this.mob.actionSubject) { + this.mob.actionSubject.pipe(first()).subscribe(result => { + this.actionInfoHtml = result; + }); + this.mob.activateFunction(this.mob, this.md2Service.msgBoxService, this.md2Service.info.heros); + } } this.mob.uiWounds = 0; this.mob.uiFrozenTokens = 0; @@ -52,22 +60,20 @@ export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit { this.mob.attackInfos = this.mob.attackInfos.filter(a => a.yellow > 0 || a.orange > 0 || a.red > 0); } - // switch (this.mode) { - // case MobDlgType.Spawn: - // return 'warning'; - // break; - // case MobDlgType.Activating: - // return 'danger'; - // break; - // case MobDlgType.BeenAttacked: - // return 'info'; - // break; - // case MobDlgType.PreView: - // default: - // return ''; - // break; - // } + switch (this.mode) { + case MobDlgType.Spawn: + this.mob.attackInfos = this.mob.attackInfos.filter(a => a.yellow > 0 || a.orange > 0 || a.red > 0); + break; + case MobDlgType.Activating: + break; + case MobDlgType.BeenAttacked: + break; + case MobDlgType.PreView: + default: + break; + } this.md2Service.info.showAttackBtn = false; + this.md2Service.refreshUI$.next(); this.dlgRef.close(this.mob); } cancel() { @@ -97,7 +103,7 @@ export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit { } } switch (targetType) { - case AttackTarget.LowestHp: + case AttackTarget.LeastHp: let lowestHp = Math.min(...this.md2Service.heros.map(h => h.hp)); this.beenAttackedHero = this.md2Service.heros.filter(h => h.hp == lowestHp); //this.otherAttackTarget = 'attacking the other Lowest HP hero.'; diff --git a/src/app/games/massive-darkness2/treasure-bag/treasure-bag.component.ts b/src/app/games/massive-darkness2/treasure-bag/treasure-bag.component.ts index 94a2dba..2aceca3 100644 --- a/src/app/games/massive-darkness2/treasure-bag/treasure-bag.component.ts +++ b/src/app/games/massive-darkness2/treasure-bag/treasure-bag.component.ts @@ -47,7 +47,7 @@ export class TreasureBagComponent extends MD2ComponentBase implements OnInit { } ngOnInit(): void { - this.md2Service.initTreasureBag(); + //this.md2Service.initTreasureBag(); this.detectChanges(); } detectChanges() { @@ -59,7 +59,7 @@ export class TreasureBagComponent extends MD2ComponentBase implements OnInit { resetTreasureBag(showConfirmMsg = true) { this.msgBoxService.show("Restore Treasure Bag", { text: 'Are you sure?', icon: ADIcon.QUESTION, buttons: ADButtons.YesNo }).pipe(first()).subscribe(result => { if (result) { - this.md2Service.initTreasureBag(); + //this.md2Service.initTreasureBag(); } }); diff --git a/src/app/services/MD2/md2-broadcast.service.ts b/src/app/services/MD2/md2-broadcast.service.ts index b9a7264..dd1692c 100644 --- a/src/app/services/MD2/md2-broadcast.service.ts +++ b/src/app/services/MD2/md2-broadcast.service.ts @@ -23,11 +23,11 @@ export class MD2BroadcastService { ) { } public get playerHero(): MD2HeroInfo { - return this.stateService.info.heros.find(h => h.playerInfo.signalRClientId == this.loginUserService.userAccess.signalRSessionId); + return this.stateService.playerHero; } public broadcastAllHeroInfoToAll() { this.stateService.info.heros.forEach(element => { - this.broadcastHeroInfoToAll(element); + this.broadcastHeroInfoToOwner(element); }); } @@ -55,7 +55,7 @@ export class MD2BroadcastService { receiver: { isGroup: false, sessionId: hero.playerInfo.signalRClientId } as SignalRSession, from: { isGroup: true, sessionId: this.gameRoomService.gameRoomId }, actionType: 'hero', - actionName: 'update', + actionName: 'updateMyHero', } as SignalRMessage; message.parameters = { hero: JSON.stringify(hero) }; this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => { @@ -90,6 +90,12 @@ export class MD2BroadcastService { parameters['gameInfo'] = JSON.stringify(this.stateService.info); this.broadcastMessage('GameRoom', 'update', parameters); } + public broadcastRoundPhase() { + let parameters = {}; + parameters['phase'] = JSON.stringify(this.stateService.info.roundPhase); + this.broadcastMessage('GameRoom', 'phase', parameters); + } + public broadcastMobsInfo() { let parameters = {}; parameters['roamingMonsters'] = JSON.stringify(this.stateService.info.roamingMonsters); diff --git a/src/app/services/MD2/md2-init.service.ts b/src/app/services/MD2/md2-init.service.ts new file mode 100644 index 0000000..9337d96 --- /dev/null +++ b/src/app/services/MD2/md2-init.service.ts @@ -0,0 +1,51 @@ +import { Injectable } from '@angular/core'; +import { CoreGameMobFactories } from '../../games/massive-darkness2/factorys/mobs/CoreGame'; +import { CoreGameRMFactories } from '../../games/massive-darkness2/factorys/roamingMonsters/CoreGame'; +import { DrawingBag, MobInfo, TreasureItem, TreasureType } from '../../games/massive-darkness2/massive-darkness2.model'; +import { MD2StateService } from './md2-state.service'; + +@Injectable({ + providedIn: 'root' +}) +export class MD2InitService { + + constructor( + private stateService: MD2StateService + ) { } + + public initMobDecks() { + this.stateService.mobDeck = new DrawingBag(); + this.stateService.roamingMobDeck = new DrawingBag(); + + this.initCoreGameMobs(); + this.initCoreGameRoamingMonsters(); + } + + private initCoreGameRoamingMonsters() { + CoreGameRMFactories.forEach(factory => { + for (let i = 1; i <= 5; i++) { + this.stateService.roamingMobDeck.AddItem(new MobInfo({ name: factory.mobName, level: i, drawingWeight: 1 })); + i++; + } + }); + + } + private initCoreGameMobs() { + CoreGameMobFactories.forEach(factory => { + for (let i = 1; i <= 5; i++) { + this.stateService.mobDeck.AddItem(new MobInfo({ name: factory.mobName, level: i, drawingWeight: 1 })); + i++; + } + }); + } + + public initTreasureBag() { + this.stateService.treasureBag.ClearAllItems(); + this.addTreasure(TreasureType.Common, 15); + this.addTreasure(TreasureType.Rare, 5); + } + + public addTreasure(type: TreasureType, amount: number = 1) { + this.stateService.treasureBag.AddItem(new TreasureItem(type, amount)); + } +} diff --git a/src/app/services/MD2/md2-mob.service.spec.ts b/src/app/services/MD2/md2-mob.service.spec.ts new file mode 100644 index 0000000..2028f0c --- /dev/null +++ b/src/app/services/MD2/md2-mob.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { MD2MobService } from './md2-mob.service'; + +describe('MD2MobService', () => { + let service: MD2MobService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(MD2MobService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/services/MD2/md2-mob.service.ts b/src/app/services/MD2/md2-mob.service.ts new file mode 100644 index 0000000..0822d6a --- /dev/null +++ b/src/app/services/MD2/md2-mob.service.ts @@ -0,0 +1,140 @@ +import { Injectable } from '@angular/core'; +import { first } from 'rxjs/operators'; +import { MobInfo, MobType } from '../../games/massive-darkness2/massive-darkness2.model'; +import { ADIcon } from '../../ui/alert-dlg/alert-dlg.model'; +import { MsgBoxService } from '../msg-box.service'; +import { MD2StateService } from './md2-state.service'; +import { MD2Service } from './md2.service'; + +@Injectable({ + providedIn: 'root' +}) +export class MD2MobService { + + attackingAllExp: number = 0; + attackingAttackerExp: number = 0; + attackingAttackerReward: string = ''; + constructor( + private md2Service: MD2Service, + private stateService: MD2StateService, + private msgBoxService: MsgBoxService + ) { } + public get roamingMonsters() { + return this.stateService.info.roamingMonsters; + } + public get mobs() { + return this.stateService.info.mobs; + } + + addOneUnit(mob: MobInfo) { + if (mob.type == MobType.Mob) { + mob.mobAmount++; + mob.unitRemainHp = 1; + this.md2Service.refreshUI$.next(); + } + } + + private killOneUnit(mob: MobInfo) { + let attacker = this.md2Service.currentActivateHero; + if (mob.mobAmount > 1) { + mob.mobAmount--; + mob.unitRemainHp = mob.hp; + this.attackingAttackerExp += 1; + + + } else { + if (mob.type == MobType.Mob) { + this.mobs.splice(this.mobs.indexOf(mob), 1); + } else { + this.roamingMonsters.splice(this.roamingMonsters.indexOf(mob), 1); + + } + mob.mobAmount = 0; + + this.attackingAllExp += mob.leaderExp; + this.attackingAttackerExp += 1; + this.attackingAttackerReward = `
and gains ${mob.carriedTreasureHtml}`; + + this.md2Service.mobBeenKilledSubject.next(mob); + + } + this.md2Service.refreshUI$.next(); + } + + healMob(mob: MobInfo, healAmount: number) { + switch (mob.type) { + case MobType.Mob: + mob.unitRemainHp += healAmount; + if (mob.unitRemainHp > mob.hp) { + mob.unitRemainHp -= mob.hp; + mob.mobAmount += 1; + } + + break; + case MobType.RoamingMonster: + case MobType.Boss: + mob.unitRemainHp += healAmount; + if (mob.unitRemainHp > mob.hp) { + mob.unitRemainHp = mob.hp; + } + break; + default: break; + } + + this.md2Service.refreshUI$.next(); + } + attackMob(mob: MobInfo, attackDamage: number) { + + this.attackingAttackerReward = ''; + if (attackDamage) { + do { + if (attackDamage >= mob.unitRemainHp) { + attackDamage -= mob.unitRemainHp; + this.killOneUnit(mob); + } else { + let originAttackDamage = attackDamage; + attackDamage -= mob.unitRemainHp; + mob.unitRemainHp -= originAttackDamage; + } + } while (attackDamage > 0 && mob.mobAmount > 0); + + let attacker = this.md2Service.currentActivateHero; + let attackerTitle = ''; + if (attacker) { + attackerTitle = attacker.heroFullName; + } + if (this.attackingAllExp > 0) { + let attackerRewardText = ''; + if (attacker) { + attackerRewardText = `${attackerTitle} Gains ${this.attackingAttackerExp} Extra Exp.
and gains ${mob.carriedTreasureHtml}`; + } + this.msgBoxService.show(`All Hero Gains ${this.attackingAllExp} Exp`, + { text: attackerRewardText, icon: ADIcon.INFO }) + .pipe(first()).subscribe(result => { + for (let i = 0; i < this.md2Service.heros.length; i++) { + const hero = this.md2Service.heros[i]; + hero.exp += this.attackingAllExp; + } + if (attacker) { + attacker.exp += this.attackingAttackerExp; + } + this.attackingAllExp = 0; + this.attackingAttackerExp = 0; + this.md2Service.broadcastService.broadcastAllHeroInfoToAll(); + }); + } else if (this.attackingAttackerExp > 0 && attacker) { + this.msgBoxService.show(`${attackerTitle} Gains ${this.attackingAttackerExp} Exp`, { icon: ADIcon.INFO }).pipe(first()).subscribe(result => { + attacker.exp += this.attackingAttackerExp; + this.attackingAttackerExp = 0; + this.md2Service.broadcastService.broadcastHeroInfoToOwner(attacker); + }); + + + } + + + this.md2Service.refreshUI$.next(); + } + + } +} diff --git a/src/app/services/MD2/md2-state.service.ts b/src/app/services/MD2/md2-state.service.ts index 840c6d6..3e33c45 100644 --- a/src/app/services/MD2/md2-state.service.ts +++ b/src/app/services/MD2/md2-state.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { MD2Icon, TreasureType } from '../../games/massive-darkness2/massive-darkness2.model'; +import { DrawingBag, MD2HeroInfo, MD2Icon, MobInfo, TreasureItem, TreasureType } from '../../games/massive-darkness2/massive-darkness2.model'; import { FileService } from '../file.service'; import { MD2GameInfo } from './md2.service'; @@ -12,6 +12,10 @@ export class MD2StateService { private _playerAmount: number = 2; public info: MD2GameInfo; + public playerHero: MD2HeroInfo; + public mobDeck: DrawingBag; + public roamingMobDeck: DrawingBag; + public treasureBag: DrawingBag = new DrawingBag(); constructor( public fileService: FileService, @@ -21,7 +25,7 @@ export class MD2StateService { if (icon < MD2Icon.RedDice) { return `${String.fromCharCode(65 + icon)}` } else { - return ``; + return ``; } } diff --git a/src/app/services/MD2/md2.service.ts b/src/app/services/MD2/md2.service.ts index 30b53cb..b757642 100644 --- a/src/app/services/MD2/md2.service.ts +++ b/src/app/services/MD2/md2.service.ts @@ -3,7 +3,7 @@ import { AttackInfo, AttackTarget, CoreGameDarknessPhaseRule, DefenseInfo, Drawi import { first, map, reduce } from "rxjs/operators"; import { NbDialogService } from '@nebular/theme'; import { Subject } from 'rxjs'; -import { IBossFight } from '../../games/massive-darkness2/massive-darkness2.model.boss'; +import { BossMicheal, BossReaper, IBossFight } from '../../games/massive-darkness2/massive-darkness2.model.boss'; import { ADIcon, MessageBoxConfig } from '../../ui/alert-dlg/alert-dlg.model'; import { NumberUtils } from '../../utilities/number-utils'; import { StringUtils } from '../../utilities/string-utils'; @@ -15,6 +15,11 @@ import { SignalRService, SignalRSession, SignalRMessage } from '../signal-r.serv import { MD2StateService } from './md2-state.service'; import { MD2BroadcastService } from './md2-broadcast.service'; import { CoreGameMobFactories } from '../../games/massive-darkness2/factorys/mobs/CoreGame'; +import { MD2Logic } from '../../games/massive-darkness2/massive-darkness2.logic'; +import { CoreGameRMFactories } from '../../games/massive-darkness2/factorys/roamingMonsters/CoreGame'; +import { MD2InitService } from './md2-init.service'; +import { ArrayUtils } from '../../utilities/array-utils'; +import { DropDownOption } from '../../entity/dropDownOption'; @Injectable({ @@ -28,13 +33,12 @@ export class MD2Service { public enemyPhaseSubject = new Subject(); public initialized = false; public mobBeenKilledSubject = new Subject(); - public mobDeck: DrawingBag; - public roamingMobDeck: DrawingBag; - public treasureBag: DrawingBag = new DrawingBag(); public refreshUI$: Subject = new Subject(); public refreshTreasureBagSubject = new Subject>(); + public heroAttackingSubject = new Subject(); + public get heros() { return this.stateService.info.heros; @@ -76,67 +80,6 @@ export class MD2Service { } // #endregion Constructors (1) - private initCoreGameRoamingMonsters() { - let mobs = []; - this.roamingMobDeck.AddItem(new MobInfo({ name: 'Andra', hp: 5, level: 1, rewardTokens: 2, type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Rare)] })); - this.roamingMobDeck.AddItem(new MobInfo({ name: 'Andra', hp: 7, level: 3, rewardTokens: 2, type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic)] })); - this.roamingMobDeck.AddItem(new MobInfo({ name: 'Andra', hp: 5, level: 5, rewardTokens: 0, type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic, 3)] })); - - this.roamingMobDeck.AddItem(new MobInfo({ - name: 'Ytheria, Undead Queen', hp: 4, level: 1, rewardTokens: 2, - attackInfos: [new AttackInfo(MD2Icon.Melee, 1), new AttackInfo(MD2Icon.Rage, 2, 0, 0, 1)], - defenseInfo: new DefenseInfo(1, 1), - type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Rare)] - })); - this.roamingMobDeck.AddItem(new MobInfo({ - name: 'Ytheria, Undead Queen', hp: 6, level: 3, rewardTokens: 2, - attackInfos: [new AttackInfo(MD2Icon.Melee, 0, 1), new AttackInfo(MD2Icon.Rage, 1, 1, 0, 1)], - defenseInfo: new DefenseInfo(2, 1), - type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic)] - })); - this.roamingMobDeck.AddItem(new MobInfo({ - name: 'Ytheria, Undead Queen', hp: 8, level: 5, rewardTokens: 0, - attackInfos: [new AttackInfo(MD2Icon.Melee, 2, 1), new AttackInfo(MD2Icon.Rage, 2, 1, 1, 1)], - defenseInfo: new DefenseInfo(4, 1), - type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic, 3)] - })); - - this.roamingMobDeck.AddItem(new MobInfo({ - name: 'Lyidan, Incubus Lord', hp: 7, level: 1, rewardTokens: 2, - type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Rare)] - })); - this.roamingMobDeck.AddItem(new MobInfo({ - name: 'Lyidan, Incubus Lord', hp: 10, level: 3, rewardTokens: 2, - type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic)] - })); - this.roamingMobDeck.AddItem(new MobInfo({ - name: 'Lyidan, Incubus Lord', hp: 7, level: 5, rewardTokens: 0, - type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic, 3)] - })); - - this.roamingMobDeck.AddItem(new MobInfo({ - name: 'The Ghoul', hp: 5, level: 1, rewardTokens: 2, - type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Rare)] - })); - this.roamingMobDeck.AddItem(new MobInfo({ - name: 'The Ghoul', hp: 8, level: 3, rewardTokens: 2, - type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic)] - })); - this.roamingMobDeck.AddItem(new MobInfo({ - name: 'The Ghoul', hp: 5, level: 5, rewardTokens: 0, - type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic, 3)] - })); - - - } - private initCoreGameMobs() { - CoreGameMobFactories.forEach(factory => { - for (let i = 1; i <= 5; i++) { - this.mobDeck.AddItem(factory.generate(i)); - i++; - } - }); - } // #region Public Getters And Setters (5) public get highestPlayerLevel(): number { @@ -156,13 +99,23 @@ export class MD2Service { } public get playerHero(): MD2HeroInfo { - return this.heros.find(h => h.playerInfo.signalRClientId == this.loginUserService.userAccess.signalRSessionId); + return this.stateService.playerHero; } + public get currentActivateHero(): MD2HeroInfo { + return this.stateService.info.heros.find(h => h.uiActivating); + } + + + // #endregion Public Getters And Setters (5) // #region Public Methods (27) + public get treasureBag() { + return this.stateService.treasureBag + } + public addTreasure(type: TreasureType, amount: number = 1) { this.treasureBag.AddItem(new TreasureItem(type, amount)); this.refreshTreasureBagSubject.next(this.treasureBag); @@ -174,13 +127,19 @@ export class MD2Service { let remainFrozenToken = Math.max(0, hero.frozenToken - hero.remainActions); hero.remainActions = Math.max(0, hero.remainActions - hero.frozenToken); hero.frozenToken = remainFrozenToken; - this.broadcastHeroInfoToOwner(hero); + this.broadcastService.broadcastHeroInfoToOwner(hero); }); this.stateService.info.roundPhase = RoundPhase.HeroPhase; - this.stateService.info.round++; - if (this.darknessPhaseRule.runDarknessPhase()) { - this.msgBoxService.show(`${NumberUtils.Ordinal(this.stateService.info.round)} Hero Phase`, { icon: ADIcon.INFO }); + if (!this.stateService.info.isBossFight) { + this.stateService.info.round++; + if (this.darknessPhaseRule.runDarknessPhase()) { + this.msgBoxService.show(`${NumberUtils.Ordinal(this.stateService.info.round)} Hero Phase`, { icon: ADIcon.INFO }); + } + } else { + this.stateService.info.boss.darknessPhase(); + this.msgBoxService.show(`Boss Fight - ${NumberUtils.Ordinal(this.stateService.info.boss.rounds)} Hero Phase`, { icon: ADIcon.INFO }); } + //this.runNextPhase(); } @@ -194,9 +153,9 @@ export class MD2Service { level = 5; } if (isRoamingMonster) { - mobDeck = this.roamingMobDeck; + mobDeck = this.stateService.roamingMobDeck; } else { - mobDeck = this.mobDeck; + mobDeck = this.stateService.mobDeck; } if (mobDeck.drawingItems.filter(m => (m as MobInfo).level == level) @@ -205,40 +164,44 @@ export class MD2Service { } let newSpawnMob = new MobInfo(mobDeck.DrawAndRemove(1, m => m.level == level)[0]); - if (isRoamingMonster) { - newSpawnMob.unitRemainHp = newSpawnMob.hp * this.playerAmount; - newSpawnMob.mobAmount = 0; - } else { - newSpawnMob.mobAmount = this.playerAmount + 1; - } - newSpawnMob.imageUrl = this.mobImage(newSpawnMob.name, newSpawnMob.level, isRoamingMonster); - let exitingMob = this.mobs.find(m => m.name == newSpawnMob.name); + + let exitingMob = isRoamingMonster ? this.roamingMonsters.find(m => m.name == newSpawnMob.name) : this.mobs.find(m => m.name == newSpawnMob.name); if (exitingMob) { exitingMob.level = newSpawnMob.level; exitingMob.hp = newSpawnMob.hp; exitingMob.imageUrl = newSpawnMob.imageUrl; + exitingMob.attackInfos = newSpawnMob.attackInfos; + exitingMob.defenseInfo = newSpawnMob.defenseInfo; + exitingMob.combatSkill = newSpawnMob.combatSkill; } else { - this.mobs.push(newSpawnMob); + if (isRoamingMonster) { + newSpawnMob = CoreGameRMFactories.find(f => f.mobName == newSpawnMob.name).generate(level); + newSpawnMob.hp = newSpawnMob.hpPerHero * this.playerAmount; + newSpawnMob.unitRemainHp = newSpawnMob.hp; + newSpawnMob.mobAmount = 0; + this.roamingMonsters.push(newSpawnMob); + } else { + newSpawnMob = CoreGameMobFactories.find(f => f.mobName == newSpawnMob.name).generate(level); + newSpawnMob.mobAmount = this.playerAmount + 1; + this.mobs.push(newSpawnMob); + } + newSpawnMob.carriedTreasure = this.treasureBag.DrawAndRemove(newSpawnMob.rewardTokens); this.refreshTreasureBagSubject.next(this.treasureBag); } - + newSpawnMob.imageUrl = this.mobImage(newSpawnMob.name, newSpawnMob.level, isRoamingMonster); return { exitingMob, mob: newSpawnMob }; } public enemyPhase() { //this.msgBoxService - let monsterBag = new DrawingBag(); - monsterBag.drawingItems = this.roamingMonsters.concat(this.mobs); - for (let i = 0; i < monsterBag.drawingItems.length; i++) { - const element = monsterBag.drawingItems[i]; - element.drawingWeight = 1; - } - if (monsterBag.drawingItems.length > 0) { - this.enemyPhaseMobs = monsterBag.DrawAndRemove(monsterBag.drawingItems.length); + this.enemyPhaseMobs = this.roamingMonsters.concat(this.mobs); + + if (this.enemyPhaseMobs.length > 0) { + this.enemyPhaseMobs = ArrayUtils.Shuffle(this.enemyPhaseMobs); //this.showEnemyPhaseAction(); this.enemyPhaseSubject.next(this.enemyPhaseMobs[0]); } else { @@ -247,33 +210,53 @@ export class MD2Service { } public enterBossFight() { + this.msgBoxService.showInputbox('Boss Fight', 'Choose the boss', { + inputType: 'dropdown', + dropDownOptions: [new DropDownOption('The Reaper', 'The Reaper'), new DropDownOption('Michael - The Corrupted Archangel', 'Michael - The Corrupted Archangel')] + }).pipe(first()).subscribe(result => { + if (result) { + this.info.mobs = []; + this.info.roamingMonsters = []; + + if (result == 'The Reaper') { + this.info.boss = new BossReaper(this); + } else { + this.info.boss = new BossMicheal(this); + } + this.stateService.info.roamingMonsters = []; + this.stateService.info.mobs = []; + this.stateService.info.isBossFight = true; + this.info.isBossFight = true; + this.info.boss.info.hp = this.info.boss.info.hpPerHero * this.info.heros.length; + this.info.boss.info.unitRemainHp = this.info.boss.info.hp; + this.refreshUI$.next(); + this.info.boss.prepareForBossFight(); + this.levelUpPhase(false); + this.heros.forEach(hero => { + hero.hp = hero.hpMaximum; + hero.mp = hero.mpMaximum; + hero.remainActions = 3; + hero.uiActivating = false; + hero.uiBossFight = true; + }); + this.broadcastService.broadcastAllHeroInfoToAll(); + } + + }); //this.sendMsgboxMsg } - + public activateBoss() { + this.stateService.info.boss.activating(); + } public fileList(folderPath: string) { return this.fileService.FileList('Images/MD2/' + folderPath); } public heroFullName(hero: MD2HeroInfo) { - if (!hero) return ''; - return `${hero.playerInfo.name} (${HeroClass[hero.class]} - ${hero.name})` + return MD2Logic.heroFullName(hero); } - public initMobDecks() { - this.mobDeck = new DrawingBag(); - this.roamingMobDeck = new DrawingBag(); - - this.initCoreGameMobs(); - this.initCoreGameRoamingMonsters(); - } - - public initTreasureBag() { - this.treasureBag.ClearAllItems(); - this.addTreasure(TreasureType.Common, 15); - this.addTreasure(TreasureType.Rare, 5); - } - - public levelUpPhase() { + public levelUpPhase(runNextPhase: boolean = true) { for (let i = 0; i < this.heros.length; i++) { const hero = this.heros[i]; let levelUpInfo = MD2Rules.checkCoreGameLevelup(hero.level, hero.exp); @@ -289,11 +272,12 @@ export class MD2Service { levelUpInfo = MD2Rules.checkCoreGameLevelup(hero.level, hero.exp); } } - this.runNextPhase(); + if (runNextPhase) { + this.runNextPhase(); + } } public mobImage(name: string, level: number, isRoamingMonsters: boolean) { - name = StringUtils.replaceAll(name, ' ', '').replace(',', ''); if (level < 3) { level = 1; } else if (level < 5) { @@ -314,6 +298,7 @@ export class MD2Service { hero.hp = hero.hpMaximum; hero.mp = hero.mpMaximum; hero.exp = 0; + this.stateService.playerHero = hero; let message = { receiver: { isGroup: true, sessionId: this.gameRoomService.gameRoomId } as SignalRSession, from: { isGroup: false, sessionId: this.loginUserService.userAccess.signalRSessionId }, @@ -368,51 +353,11 @@ export class MD2Service { } public getTargetHerosByFilter(targetType: AttackTarget, onlyOne: boolean = false) { - let beenAttackedHero = [] as MD2HeroInfo[]; - switch (targetType) { - case AttackTarget.LowestHp: - let lowestHp = Math.min(...this.heros.map(h => h.hp)); - beenAttackedHero = this.heros.filter(h => h.hp == lowestHp); - //this.otherAttackTarget = 'attacking the other Lowest HP hero.'; - break; - case AttackTarget.HighestHp: - let highestHp = Math.max(...this.heros.map(h => h.hp)); - beenAttackedHero = this.heros.filter(h => h.hp == highestHp); - //this.otherAttackTarget = 'attacking the other Highest HP hero.'; - break; - case AttackTarget.HighestMp: - let highestMp = Math.max(...this.heros.map(h => h.mp)); - beenAttackedHero = this.heros.filter(h => h.mp == highestMp); - //this.otherAttackTarget = 'attacking the other Highest Mp hero.'; - break; - case AttackTarget.LowestLevel: - let lowestLevel = Math.max(...this.heros.map(h => h.level)); - beenAttackedHero = this.heros.filter(h => h.level == lowestLevel); - //this.otherAttackTarget = 'attacking the other Lowest Level hero.'; - break; - case AttackTarget.LeastCorruption: - - let leastCor = Math.min(...this.heros.map(h => h.corruptionToken)); - beenAttackedHero = this.heros.filter(h => h.corruptionToken == leastCor); - break; - case AttackTarget.MostCorruption: - let mostCor = Math.max(...this.heros.map(h => h.corruptionToken)); - beenAttackedHero = this.heros.filter(h => h.corruptionToken == mostCor); - break; - case AttackTarget.Random: - default: - beenAttackedHero = [this.heros[Math.round(Math.random() * (this.heros.length - 1))]]; - //this.otherAttackTarget = 'Just act like normal.'; - break; - } - if (onlyOne && beenAttackedHero.length > 1) { - beenAttackedHero = [beenAttackedHero[Math.round(Math.random() * (beenAttackedHero.length - 1))]]; - } - return beenAttackedHero; + return MD2Logic.getTargetHerosByFilter(this.stateService.info.heros, targetType, onlyOne); } public getTargetHerosHtml(beenAttackedHero: MD2HeroInfo[]) { - return `${StringUtils.makeCommaSeparatedString(beenAttackedHero.map(h => this.heroFullName(h)), false, true)}`; + return MD2Logic.getTargetHerosHtml(beenAttackedHero); } // #endregion Public Methods (27) @@ -433,12 +378,10 @@ export class MD2GameInfo { if (this.boss.info) { this.boss.info = new MobInfo(this.boss.info); } - this.currentActivateHero = new MD2HeroInfo(this.currentActivateHero); this.heros = this.heros.map(h => new MD2HeroInfo(h)); } } - public currentActivateHero: MD2HeroInfo; public isBossFight: boolean = false; public mobs: MobInfo[] = []; public roamingMonsters: MobInfo[] = []; diff --git a/src/app/ui/currency-input/adjacent-number-input/adjacent-number-input.component.html b/src/app/ui/currency-input/adjacent-number-input/adjacent-number-input.component.html index 0fad3c9..63db9f7 100644 --- a/src/app/ui/currency-input/adjacent-number-input/adjacent-number-input.component.html +++ b/src/app/ui/currency-input/adjacent-number-input/adjacent-number-input.component.html @@ -1,4 +1,4 @@ -
+