import { Injectable } from '@angular/core'; import { AttackInfo, AttackTarget, CoreGameDarknessPhaseRule, DefenseInfo, DrawingBag, DrawingItem, HeroClass, IDarknessPhaseRule, MD2HeroInfo, MD2Icon, MD2Rules, MobInfo, MobType, RoundPhase, TreasureItem, TreasureType } from '../../games/massive-darkness2/massive-darkness2.model'; import { first, map, reduce } from "rxjs/operators"; import { NbDialogService } from '@nebular/theme'; import { Subject } from 'rxjs'; 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'; import { FileService } from '../file.service'; import { GameRoomService } from '../game-room.service'; import { LoginUserService } from '../login-user.service'; import { MsgBoxService } from '../msg-box.service'; import { SignalRService, SignalRSession, SignalRMessage } from '../signal-r.service'; 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({ providedIn: 'root' }) export class MD2Service { // #region Properties (24) public darknessPhaseRule: IDarknessPhaseRule; public enemyPhaseMobs: MobInfo[]; public enemyPhaseSubject = new Subject(); public initialized = false; public mobBeenKilledSubject = new Subject(); public refreshUI$: Subject = new Subject(); public refreshTreasureBagSubject = new Subject>(); public heroAttackingSubject = new Subject(); public get heros() { return this.stateService.info.heros; } public get roamingMonsters() { return this.stateService.info.roamingMonsters; } public get mobs() { return this.stateService.info.mobs; } public get info(): MD2GameInfo { return this.stateService.info; } public set info(v: MD2GameInfo) { this.stateService.info = v; } // #endregion Properties (24) // #region Constructors (1) constructor( public fileService: FileService, public msgBoxService: MsgBoxService, private gameRoomService: GameRoomService, private loginUserService: LoginUserService, public stateService: MD2StateService, public signalRService: SignalRService, public dlgService: NbDialogService, public broadcastService: MD2BroadcastService ) { this.darknessPhaseRule = new CoreGameDarknessPhaseRule(); this.stateService.info = new MD2GameInfo(); this.darknessPhaseRule.addTreasureToken.subscribe(treasureType => { this.addTreasure(treasureType, 1); }); } // #endregion Constructors (1) // #region Public Getters And Setters (5) public get highestPlayerLevel(): number { if (this.heros.length > 0) { return Math.max(...this.heros.map(h => h.level)); } else { return 1; } } public get playerAmount(): number { if (this.heros.length > 0) { return this.heros.length; } else { return 1; } } public get playerHero(): MD2HeroInfo { 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); } public darknessPhase() { this.heros.forEach(hero => { hero.remainActions = 3; let remainFrozenToken = Math.max(0, hero.frozenToken - hero.remainActions); hero.remainActions = Math.max(0, hero.remainActions - hero.frozenToken); hero.frozenToken = remainFrozenToken; this.broadcastService.broadcastHeroInfoToOwner(hero); }); this.stateService.info.roundPhase = RoundPhase.HeroPhase; 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(); } public spawnMob(isRoamingMonster: boolean) { let mobDeck = null as DrawingBag; let level = 1; if (this.highestPlayerLevel < 3) { } else if (this.highestPlayerLevel < 5) { level = 3; } else { level = 5; } if (isRoamingMonster) { mobDeck = this.stateService.roamingMobDeck; } else { mobDeck = this.stateService.mobDeck; } if (mobDeck.drawingItems.filter(m => (m as MobInfo).level == level) .map(d => d.drawingWeight).reduce((a, b) => a + b, 0) == 0) { mobDeck.RestoreRemoveItems(); } let newSpawnMob = new MobInfo(mobDeck.DrawAndRemove(1, m => m.level == level)[0]); 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 { 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 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 { this.runNextPhase(); } } 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) { return MD2Logic.heroFullName(hero); } 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); while (levelUpInfo != null) { hero.level = levelUpInfo.level; hero.hp += levelUpInfo.extraHp; hero.hpMaximum += levelUpInfo.extraHp; hero.mp += levelUpInfo.extraMp; hero.mpMaximum += levelUpInfo.extraMp; hero.exp = levelUpInfo.currentExp; this.broadcastService.broadcastHeroInfoToOwner(hero); this.sendMsgboxMsg(hero.playerInfo.signalRClientId, { title: 'Level Up', text: 'Please do a skill level up!', icon: ADIcon.INFO }); levelUpInfo = MD2Rules.checkCoreGameLevelup(hero.level, hero.exp); } } if (runNextPhase) { this.runNextPhase(); } } public mobImage(name: string, level: number, isRoamingMonsters: boolean) { if (level < 3) { level = 1; } else if (level < 5) { level = 2; } else { level = 3; } if (isRoamingMonsters) { return this.stateService.imgUrl(`Mobs/CoreGame/RoamingMonsters/${name}/${level}.png`); } else { return this.stateService.imgUrl(`Mobs/CoreGame/Mobs/${name}/${level}.png`); } } public playerJoin(hero: MD2HeroInfo) { hero.playerInfo = this.gameRoomService.currentPlayer(); hero.level = 1; 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 }, actionType: 'hero', actionName: 'join', } as SignalRMessage; message.parameters = { hero: JSON.stringify(hero) }; this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => { }); } public runNextPhase() { this.stateService.info.roundPhase++; switch (this.stateService.info.roundPhase) { case RoundPhase.HeroPhase: this.heros.forEach(hero => { hero.remainActions = 3; let remainFrozenToken = Math.max(0, hero.frozenToken - hero.remainActions); hero.remainActions = Math.max(0, hero.remainActions - hero.frozenToken); hero.frozenToken = remainFrozenToken; this.broadcastService.broadcastHeroInfoToOwner(hero); }); break; case RoundPhase.EnemyPhase: this.enemyPhase(); break; case RoundPhase.LevelUpPhase: this.levelUpPhase(); break; case RoundPhase.DarknessPhase: this.darknessPhase(); break; default: break; } let parameters = {}; parameters['phase'] = this.stateService.info.roundPhase; this.broadcastService.broadcastMessage('roundPhase', '', parameters); } public sendMsgboxMsg(playerSessionId: string, msg: Partial) { let message = { receiver: { isGroup: false, sessionId: playerSessionId } as SignalRSession, from: { isGroup: false, sessionId: this.loginUserService.userAccess.signalRSessionId }, actionType: 'message', actionName: 'popup', } as SignalRMessage; message.parameters = { msg: JSON.stringify(msg) }; this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => { }); } public getTargetHerosByFilter(targetType: AttackTarget, onlyOne: boolean = false) { return MD2Logic.getTargetHerosByFilter(this.stateService.info.heros, targetType, onlyOne); } public getTargetHerosHtml(beenAttackedHero: MD2HeroInfo[]) { return MD2Logic.getTargetHerosHtml(beenAttackedHero); } // #endregion Public Methods (27) } export class MD2GameInfo { /** * */ constructor( config: Partial = undefined ) { if (config) { Object.assign(this, config); this.mobs = this.mobs.map(m => new MobInfo(m)); this.roamingMonsters = this.roamingMonsters.map(m => new MobInfo(m)); if (this.boss.info) { this.boss.info = new MobInfo(this.boss.info); } this.heros = this.heros.map(h => new MD2HeroInfo(h)); } } public isBossFight: boolean = false; public mobs: MobInfo[] = []; public roamingMonsters: MobInfo[] = []; public heros: MD2HeroInfo[] = []; public round = 1; public roundPhase: RoundPhase = RoundPhase.HeroPhase; public showAttackBtn: boolean = false; public boss: IBossFight; }