import { Observable, Subject, Subscription } from "rxjs" import { first } from "rxjs/operators" import { MD2Service } from "../../services/MD2/md2.service" import { StringUtils } from "../../utilities/string-utils" import { BossActivationComponent } from "./boss-fight/boss-activation/boss-activation.component" import { TreasureType, AttackInfo, AttackType, MD2Icon, MD2HeroInfo, AttackTarget, MobInfo, MobType } from "./massive-darkness2.model" import { RollingBlackDice } from "./massive-darkness2.model.dice" import { MD2DiceSet, MD2MobSkill } from "./massive-darkness2.db.model" export enum MobSkillType { Attack, Defense, Combat, Passive, ConditionalSkill, ActiveSkill } export class MobSkill { constructor(config: Partial = {}) { let defaultConfig = { type: MobSkillType.Combat, skillRoll: 1 } as Partial; Object.assign(defaultConfig, config); Object.assign(this, defaultConfig); // if (StringUtils.isNullOrWhitespace(this.name)) { // this.name=`${MobSkillType[this.type]} ${this.skillRoll} ${}` // } } type: MobSkillType; clawRoll: number; skillRoll: number; 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, imageUrl: md2Service.stateService.imgUrl('/Boss/Michael.png') }); if (!this.info.skills) { this.info.skills = []; } this.info.skills.push({ 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).`, type: MobSkillType.Combat, skillRoll: 1 } as MD2MobSkill); this.info.defenseInfo = { blue: 5, black: 1 } as MD2DiceSet; 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, imageUrl: md2Service.stateService.imgUrl('/Boss/The Reaper-Stand.png') }); if (!this.info.skills) { this.info.skills = []; } this.info.skills.push({ description: `If the Hero has no ${this.md2Service.stateService.iconHtml(MD2Icon.Mana)}, they take 1 ${this.md2Service.stateService.iconHtml(MD2Icon.Frost)}`, type: MobSkillType.Attack, skillRoll: 1 } as MD2MobSkill); this.info.defenseInfo = { blue: 4, black: 3 } as MD2DiceSet; 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; } } }