317 lines
12 KiB
TypeScript
317 lines
12 KiB
TypeScript
|
||
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 can’t 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;
|
||
}
|
||
}
|
||
} |