ChurchAngular/src/app/games/massive-darkness2/massive-darkness2.model.boss.ts
2025-11-03 15:35:52 -08:00

317 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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, DefenseInfo, AttackType, MD2Icon, MD2HeroInfo, AttackTarget, MobInfo, MobType } from "./massive-darkness2.model"
import { RollingBlackDice } from "./massive-darkness2.model.dice"
export enum MobSkillType {
Attack,
Defense,
Combat
}
export class MobSkill {
constructor(config: Partial<MobSkill> = {}) {
let defaultConfig = {
type: MobSkillType.Combat,
skillRoll: 1
} as Partial<MobSkill>;
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<TreasureType>
spawnMob: Subject<void>
spawnRoamingMonster: Subject<void>
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<TreasureType>
spawnMob: Subject<void>
spawnRoamingMonster: Subject<void>
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<boolean>;
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 cant be the target of any attack, skill, ability or take Wounds until there are no Corruption tokens in the whole Tile.<br><br>` +
`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.<br>If ${this.md2Service.stateService.iconHtml(MD2Icon.EnemySkill)} place 1 ${this.corruptionTokenHtml} on their Dashboard.<br>` +
`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<TreasureType>
spawnMob: Subject<void>
spawnRoamingMonster: Subject<void>
rounds: number
actions: number
info: MobInfo
actionBlackDice: number
imgUrl: string
standUrl: string
corruptionTokenHtml: string
bossAction(): Observable<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(
{
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}.<br>` +
`Deal <b>${this.darkBlessingCorruptionAmt}</b> Wounds per ${this.corruptionTokenHtml} to all Heros in each Tiles <b>distributed as they wish</b>.`
});
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: `<h6>Place ${this.md2Service.heros.length * 2} ${this.corruptionTokenHtml} on the Corruption Stone Zones (Shadow
Zones).<br>Players choose the Zones, but must distribute
the tokens as equally as possible among them.</h6>`
});
}
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<TreasureType>
spawnMob: Subject<void>
spawnRoamingMonster: Subject<void>
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 <b>Hourglass Zone</b> withe the least ${this.timeTokenHtml} and remove 1 ${this.timeTokenHtml} from <b>The OtherHourglass Zone</b>.`
});
break;
case 2:
//Dark Blessing
bossAction = new MobSkill({
name: 'Death Is Coming',
description:
`Place The Reaper in the central Zone.<br>` +
`Roll 1 ${this.md2Service.stateService.iconHtml(MD2Icon.YellowDice)}. Remove ${this.timeTokenHtml} equal to ${this.md2Service.stateService.iconHtml(MD2Icon.Melee)} rolled from both <b>Hourglass Zone</b>.<br>` +
`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: `<h6>Place 2 ${this.timeTokenHtml} in each Hourglass Zone.</h6>`
})
}
darkBlessingCorruptionAmt: number = 1;
darknessPhase(): void {
this.rounds++;
if (this.rounds > 4) {
this.actions = 3;
} else if (this.rounds > 1) {
this.actions = 2;
}
}
}