Compare commits
2 Commits
b46392bc41
...
d486fe9594
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d486fe9594 | ||
|
|
6a031ca478 |
@ -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(),
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -2,7 +2,7 @@ import { ChangeDetectorRef, Injectable } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { Subject } from "rxjs";
|
||||
import { first, takeUntil } from "rxjs/operators";
|
||||
import { MD2GameInfo, MD2Service } from "../../services/md2.service";
|
||||
import { MD2GameInfo, MD2Service } from "../../services/MD2/md2.service";
|
||||
import { SignalRMessage } from "../../services/signal-r.service";
|
||||
import { StateService } from "../../services/state.service";
|
||||
import { ADIcon, MessageBoxConfig } from "../../ui/alert-dlg/alert-dlg.model";
|
||||
@ -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');
|
||||
@ -55,14 +58,14 @@ export abstract class MD2Base {
|
||||
}
|
||||
|
||||
imgUrl(imgPath: string) {
|
||||
return this.md2Service.imgUrl(imgPath);
|
||||
return this.md2Service.stateService.imgUrl(imgPath);
|
||||
}
|
||||
fileList(folderPath: string) {
|
||||
return this.md2Service.fileList(folderPath);
|
||||
}
|
||||
|
||||
iconHtml(icon: MD2Icon, cssClass = '') {
|
||||
return this.md2Service.iconHtml(icon, cssClass);
|
||||
return this.md2Service.stateService.iconHtml(icon, cssClass);
|
||||
}
|
||||
|
||||
imgHtml(imgFile: string, cssClass = '') {
|
||||
@ -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 <b>same zone</b> with ${this.md2Service.heroFullName(this.gameInfo.currentActivateHero)} may freely trade and
|
||||
text: `every one in the <b>same zone with <b>${this.md2Service.heroFullName(this.md2Service.currentActivateHero)}</b> 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;
|
||||
@ -218,13 +254,24 @@ export abstract class MD2ComponentBase {
|
||||
this.destroy$.complete();
|
||||
}
|
||||
imgUrl(imgPath: string) {
|
||||
return this.md2Service.imgUrl(imgPath);
|
||||
return this.md2Service.stateService.imgUrl(imgPath);
|
||||
}
|
||||
fileList(folderPath: string) {
|
||||
return this.md2Service.fileList(folderPath);
|
||||
}
|
||||
|
||||
iconHtml(icon: MD2Icon, cssClass = '') {
|
||||
return this.md2Service.iconHtml(icon, cssClass);
|
||||
return this.md2Service.stateService.iconHtml(icon, cssClass);
|
||||
}
|
||||
detectChanges() {
|
||||
if (!this.cdRef['destroyed']) {
|
||||
this.cdRef.detectChanges();
|
||||
this.refreshUI();
|
||||
this.md2Service.refreshUI$.next();
|
||||
}
|
||||
|
||||
}
|
||||
refreshUI() {
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,21 +1,31 @@
|
||||
<nb-card>
|
||||
|
||||
<nb-card-body>
|
||||
<div class="row form-group" style="
|
||||
height: 53vh;
|
||||
overflow: auto;
|
||||
">
|
||||
<div class="col-md-5">
|
||||
<img src="{{boss.standUrl}}" class="img-fluid">
|
||||
<nb-card-body class="g-overflow-hidden">
|
||||
<div class="row form-group">
|
||||
<div class="col-md-5 g-height-700px">
|
||||
<md2-mob-stand-info [mob]="boss.info" [mode]="mode"></md2-mob-stand-info>
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
<label class="MD2text g-font-size-40 mt-5" [innerHtml]="bossAction.skillName">
|
||||
<label class="MD2text g-font-size-40 mt-4" [innerHtml]="bossAction.name">
|
||||
|
||||
</label>
|
||||
<label class="g-font-size-20 mt-3" [innerHtml]="bossAction.skillDescription">
|
||||
<label class="mt-2 g-font-size-20 my-3 MD2IconContainer-lg" [innerHtml]="bossAction.description">
|
||||
|
||||
</label>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
|
||||
<md2-mob-attack-info [mob]="boss.info"></md2-mob-attack-info>
|
||||
</div>
|
||||
<div class="col-md-8 MD2IconContainer-lg">
|
||||
|
||||
<md2-mob-combat-info [mob]="boss.info"></md2-mob-combat-info>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NbDialogRef } from '@nebular/theme';
|
||||
import { MD2Service } from '../../../../services/md2.service';
|
||||
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<SpawnMobDlgComponent>,
|
||||
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<void> = new Subject<void>();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<nb-card>
|
||||
<nb-card-header>
|
||||
<nb-card-header class="MD2text g-font-size-28">
|
||||
{{boss.name}}
|
||||
<button nbButton hero status="primary" (click)="activate()">Action</button>
|
||||
</nb-card-header>
|
||||
@ -9,18 +9,29 @@
|
||||
<img src="{{boss.standUrl}}" class="w-100 g-max-height-80vh">
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<md2-mob-detail-info [mob]="boss.info">
|
||||
</md2-mob-detail-info>
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<adj-number-input name="mob{{boss.info.name}}" [(ngModel)]="boss.info.unitRemainHp" minimum="0"
|
||||
class="mb-3" title="Boss HP" (hitMinimum)="WIN()">
|
||||
</adj-number-input>
|
||||
<md2-mob-attack-info [mob]="boss.info">
|
||||
</md2-mob-attack-info>
|
||||
<md2-mob-def-info [mob]="boss.info"></md2-mob-def-info>
|
||||
</div>
|
||||
<div class="col-md-9 h6" *ngIf="boss.extraRules">
|
||||
<div [innerHtml]="boss.extraRules"></div>
|
||||
</div>
|
||||
|
||||
<adj-number-input name="mob{{boss.info.name}}" [(ngModel)]="boss.info.unitRemainHp" minimum="0"
|
||||
title="Boss HP" (hitMinimum)="WIN()">
|
||||
</adj-number-input>
|
||||
<button nbButton hero status="danger" size="small" (click)="attack(boss.info)">Attack It</button>
|
||||
</div>
|
||||
|
||||
<label class="MD2Text mt-3" [innerHtml]="boss.combatInfo.skillName">
|
||||
</label>
|
||||
<label class="MD2Text" [innerHtml]="boss.combatInfo.skillDescription">
|
||||
<md2-mob-combat-info [mob]="boss.info"></md2-mob-combat-info>
|
||||
<!--
|
||||
<button nbButton hero status="danger" size="small" (click)="attack(boss.info)">Attack It</button> -->
|
||||
|
||||
<!-- <label class="MD2Text mt-3" [innerHtml]="boss.info.combatSkill.skillName">
|
||||
</label>
|
||||
<label class="MD2Text" [innerHtml]="boss.info.combatInfo.skillDescription">
|
||||
</label> -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@ -2,8 +2,8 @@ import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NbDialogService } from '@nebular/theme';
|
||||
import { Subject, Observable } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { MD2Service } from '../../../services/md2.service';
|
||||
import { first, 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 { ADIcon } from '../../../ui/alert-dlg/alert-dlg.model';
|
||||
@ -37,7 +37,16 @@ export class BossFightComponent extends MD2ComponentBase {
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
this.md2Service.heroAttackingSubject.pipe(takeUntil(this.destroy$)).subscribe(result => {
|
||||
if (this.md2Service.info.isBossFight) {
|
||||
this.attack(this.boss.info);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
activate() {
|
||||
this.boss.activating();
|
||||
|
||||
@ -5,7 +5,7 @@ import { ADButtons } from '../../../ui/alert-dlg/alert-dlg.model';
|
||||
import { DrawingBag, DrawingItem } from '../massive-darkness2.model';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { MD2Service } from '../../../services/md2.service';
|
||||
import { MD2Service } from '../../../services/MD2/md2.service';
|
||||
import { StateService } from '../../../services/state.service';
|
||||
import { MD2Base, MD2ComponentBase } from '../MD2Base';
|
||||
import { SignalRMessage } from '../../../services/signal-r.service';
|
||||
@ -58,7 +58,7 @@ export class DoorEventsComponent extends MD2ComponentBase implements OnInit {
|
||||
this.msgBoxService.show('', { text: `<img src="${door.imageUrl}" class="g-height-70vh g-max-width-80vw">`, buttons: ADButtons.YesNo, confirmButtonText: 'Keep It', cancelButtonText: 'Discard' })
|
||||
.pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
door.name = this.md2Service.heroFullName(this.md2Service.info.currentActivateHero);
|
||||
door.name = this.md2Service.heroFullName(this.md2Service.currentActivateHero);
|
||||
this.drawDoorEvents.push(door);
|
||||
}
|
||||
this.cdRef.detectChanges();
|
||||
|
||||
20
src/app/games/massive-darkness2/factorys/md2-clone.ts
Normal file
20
src/app/games/massive-darkness2/factorys/md2-clone.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { ObjectUtils } from "../../../utilities/object-utils";
|
||||
import { IDrawingItem, MobInfo, TreasureItem } from "../massive-darkness2.model";
|
||||
|
||||
export class MD2Clone {
|
||||
|
||||
public static CloneDrawingItem(obj: IDrawingItem) {
|
||||
let type = obj.constructor.name;
|
||||
let cloneObj = null;
|
||||
switch (type) {
|
||||
case "TreasureItem":
|
||||
return new TreasureItem(obj['type'], 1);
|
||||
break;
|
||||
case "MobInfo":
|
||||
return new MobInfo(obj);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
return ObjectUtils.CloneValue(obj);
|
||||
}
|
||||
}
|
||||
222
src/app/games/massive-darkness2/factorys/mobs/CoreGame.ts
Normal file
222
src/app/games/massive-darkness2/factorys/mobs/CoreGame.ts
Normal file
@ -0,0 +1,222 @@
|
||||
import { environment } from "../../../../../environments/environment";
|
||||
import { DefenseInfo, IMobFactory, MD2Icon, MobInfo } from "../../massive-darkness2.model";
|
||||
import { MobSkill, MobSkillType } from "../../massive-darkness2.model.boss";
|
||||
|
||||
const MD2_IMG_URL = (id: string = null) => { return `${environment.apiUrl}/Files/Images/MD2/Mobs${(id ? `${encodeURI(id)}` : '')}` }
|
||||
const CORE_GAME_MOB_LEVEL = [
|
||||
{ name: 'Gargoyles', level: 1, hp: 2, rewardTokens: 1, defBlue: 1 },
|
||||
{ name: 'Gargoyles', level: 3, hp: 3, rewardTokens: 1, defBlue: 2 },
|
||||
{ name: 'Gargoyles', level: 5, hp: 6, rewardTokens: 2, defBlue: 3 },
|
||||
|
||||
{ name: 'Demons', level: 1, hp: 3, rewardTokens: 1, defBlue: 1 },
|
||||
{ name: 'Demons', level: 3, hp: 4, rewardTokens: 1, defBlue: 2 },
|
||||
{ name: 'Demons', level: 5, hp: 6, rewardTokens: 2, defBlue: 4 },
|
||||
|
||||
{ name: 'Undead', level: 1, hp: 4, rewardTokens: 1, defBlue: 1 },
|
||||
{ name: 'Undead', level: 3, hp: 5, rewardTokens: 1, defBlue: 1 },
|
||||
{ name: 'Undead', level: 5, hp: 8, rewardTokens: 2, defBlue: 1 },
|
||||
|
||||
{ name: 'Fire Entities', level: 1, hp: 3, rewardTokens: 1, defBlue: 1 },
|
||||
{ name: 'Fire Entities', level: 3, hp: 4, rewardTokens: 1, defBlue: 2 },
|
||||
{ name: 'Fire Entities', level: 5, hp: 7, rewardTokens: 2, defBlue: 3 },
|
||||
|
||||
{ name: 'Fallen Angels', level: 1, hp: 2, rewardTokens: 1, defBlue: 2 },
|
||||
{ name: 'Fallen Angels', level: 3, hp: 3, rewardTokens: 1, defBlue: 3 },
|
||||
{ name: 'Fallen Angels', level: 5, hp: 5, rewardTokens: 2, defBlue: 5 },
|
||||
|
||||
{ name: 'Infernal Imps', level: 1, hp: 3, rewardTokens: 1, defBlue: 1 },
|
||||
{ name: 'Infernal Imps', level: 3, hp: 4, rewardTokens: 1, defBlue: 1 },
|
||||
{ name: 'Infernal Imps', level: 5, hp: 5, rewardTokens: 2, defBlue: 3 },
|
||||
|
||||
{ name: 'Skeletons', level: 1, hp: 2, rewardTokens: 1, defBlue: 1 },
|
||||
{ name: 'Skeletons', level: 3, hp: 3, rewardTokens: 1, defBlue: 2 },
|
||||
{ name: 'Skeletons', level: 5, hp: 5, rewardTokens: 2, defBlue: 4 },
|
||||
|
||||
{ name: 'Satyrs', level: 1, hp: 3, rewardTokens: 1, defBlue: 1 },
|
||||
{ name: 'Satyrs', level: 3, hp: 4, rewardTokens: 1, defBlue: 2 },
|
||||
{ name: 'Satyrs', level: 5, hp: 6, rewardTokens: 2, defBlue: 4 }]
|
||||
|
||||
export abstract class MobFactory implements IMobFactory {
|
||||
abstract mobName: string;
|
||||
abstract generate(level: number): MobInfo
|
||||
protected mob: MobInfo;
|
||||
protected loadLevelInfo(mobName: string, level: number) {
|
||||
let levelInfo = CORE_GAME_MOB_LEVEL.find(m => m.name == mobName && level >= m.level);
|
||||
|
||||
this.mob = new MobInfo({
|
||||
name: mobName, hp: levelInfo.hp, level: level, rewardTokens: levelInfo.rewardTokens,
|
||||
defenseInfo: new DefenseInfo(levelInfo.defBlue)
|
||||
});
|
||||
this.mob.leaderImgUrl = MD2_IMG_URL(`/CoreGame/Mobs/${this.mob.name}/Leader.png`);
|
||||
this.mob.minionImgUrl = MD2_IMG_URL(`/CoreGame/Mobs/${this.mob.name}/Minion.png`);
|
||||
|
||||
}
|
||||
iconHtml(icon: MD2Icon, cssClass = 'g-font-size-24') {
|
||||
if (icon == MD2Icon.Fire) {
|
||||
cssClass += ' g-color-google-plus ';
|
||||
}
|
||||
if (icon == MD2Icon.Frost || icon == MD2Icon.Mana) {
|
||||
cssClass += ' g-color-aqua ';
|
||||
}
|
||||
|
||||
if (icon < MD2Icon.RedDice) {
|
||||
return `<span class='MD2Icon ${cssClass}'>${String.fromCharCode(65 + icon)}</span>`
|
||||
|
||||
}
|
||||
else {
|
||||
return `<span class='MD2Icon dice ${MD2Icon[icon].replace('Dice', '')} ${cssClass}'></span>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
export class MobDemonsFactory extends MobFactory {
|
||||
mobName: string = 'Demons';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('Demons', level);
|
||||
this.mob.combatSkill = new MobSkill(
|
||||
{
|
||||
description: `Attacking or defending Hero discards 1 ${this.iconHtml(MD2Icon.Mana)}`
|
||||
}
|
||||
)
|
||||
return this.mob;
|
||||
}
|
||||
|
||||
}
|
||||
export class MobFallenAngelFactory extends MobFactory {
|
||||
mobName: string = 'Fallen Angels';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('Fallen Angels', level);
|
||||
this.mob.combatSkill = new MobSkill(
|
||||
{
|
||||
description: `Defender -${level == 1 ? 1 : 2} ${this.iconHtml(MD2Icon.Defense)}`,
|
||||
type: MobSkillType.Attack
|
||||
}
|
||||
)
|
||||
return this.mob;
|
||||
}
|
||||
}
|
||||
|
||||
export class MobFireEntitiesFactory extends MobFactory {
|
||||
mobName: string = 'Fire Entities';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('Fire Entities', level);
|
||||
this.mob.combatSkill = new MobSkill(
|
||||
{
|
||||
description: `Add 1 ${this.iconHtml(MD2Icon.Fire)} to the attacking or defending Hero.`,
|
||||
type: MobSkillType.Combat
|
||||
}
|
||||
)
|
||||
return this.mob;
|
||||
}
|
||||
}
|
||||
|
||||
export class MobGargoylesFactory extends MobFactory {
|
||||
mobName: string = 'Gargoyles';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('Gargoyles', level);
|
||||
this.mob.combatSkill = new MobSkill(
|
||||
{
|
||||
description: `+ ${level < 5 ? 1 : 2} ${this.iconHtml(MD2Icon.Defense)}`,
|
||||
type: MobSkillType.Defense
|
||||
}
|
||||
)
|
||||
return this.mob;
|
||||
}
|
||||
}
|
||||
export class MobInfernalImpsFactory extends MobFactory {
|
||||
mobName: string = 'Infernal Imps';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('Infernal Imps', level);
|
||||
let damage = 1;
|
||||
switch (level) {
|
||||
case 1:
|
||||
case 2:
|
||||
damage = 1;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
damage = 2;
|
||||
break;
|
||||
case 5:
|
||||
damage = 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
damage = 1;
|
||||
break;
|
||||
}
|
||||
this.mob.combatSkill = new MobSkill(
|
||||
{
|
||||
description: `Kill 1 Imp, then deal ${damage} Wound to each Hero in the attacker's Zone(once per roll).`,
|
||||
type: MobSkillType.Defense
|
||||
}
|
||||
)
|
||||
return this.mob;
|
||||
}
|
||||
}
|
||||
export class MobSatyrsFactory extends MobFactory {
|
||||
mobName: string = 'Satyrs';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('Satyrs', level);
|
||||
this.mob.combatSkill = new MobSkill(
|
||||
{
|
||||
description: `+ ${level < 3 ? 1 : 2} ${this.iconHtml(MD2Icon.Attack)}`,
|
||||
type: MobSkillType.Attack
|
||||
}
|
||||
)
|
||||
return this.mob;
|
||||
}
|
||||
}
|
||||
|
||||
export class MobSkeletonsFactory extends MobFactory {
|
||||
mobName: string = 'Skeletons';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('Skeletons', level);
|
||||
this.mob.combatSkill = new MobSkill(
|
||||
{
|
||||
description: `Add 1 minion to this Mob(if possible) unless the Hero discards ${level < 5 ? 1 : 2} ${this.iconHtml(MD2Icon.Mana)}.`,
|
||||
type: MobSkillType.Defense,
|
||||
skillRoll: 2
|
||||
}
|
||||
)
|
||||
return this.mob;
|
||||
}
|
||||
}
|
||||
|
||||
export class MobUndeadFactory extends MobFactory {
|
||||
mobName: string = 'Undead';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('Undead', level);
|
||||
|
||||
let skillDesc = '';
|
||||
if (level < 3) {
|
||||
skillDesc = `+1 ${this.iconHtml(MD2Icon.YellowDice)}`;
|
||||
} else if (level < 5) {
|
||||
skillDesc = `+2 ${this.iconHtml(MD2Icon.YellowDice)}`;
|
||||
|
||||
} else {
|
||||
skillDesc = `+1 ${this.iconHtml(MD2Icon.YellowDice)} 1 ${this.iconHtml(MD2Icon.OrangeDice)}`;
|
||||
|
||||
}
|
||||
skillDesc += ' and this Mob takes 2 wounds';
|
||||
this.mob.combatSkill = new MobSkill(
|
||||
{
|
||||
description: skillDesc,
|
||||
type: MobSkillType.Attack
|
||||
}
|
||||
)
|
||||
this.mob.drawingWeight = 1;
|
||||
return this.mob;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const CoreGameMobFactories = [
|
||||
new MobDemonsFactory(),
|
||||
new MobFallenAngelFactory(),
|
||||
new MobFireEntitiesFactory(),
|
||||
new MobGargoylesFactory(),
|
||||
new MobInfernalImpsFactory(),
|
||||
new MobSatyrsFactory(),
|
||||
new MobSkeletonsFactory(),
|
||||
new MobUndeadFactory(),
|
||||
];
|
||||
@ -0,0 +1,337 @@
|
||||
import { Subject } from "rxjs";
|
||||
import { first, map } from "rxjs/operators";
|
||||
import { environment } from "../../../../../environments/environment";
|
||||
import { ADButtons, ADIcon } from "../../../../ui/alert-dlg/alert-dlg.model";
|
||||
import { MD2Logic } from "../../massive-darkness2.logic";
|
||||
import { AttackInfo, AttackTarget, DefenseInfo, IMobFactory, MD2Icon, MobInfo, MobType, TreasureItem, TreasureType } from "../../massive-darkness2.model";
|
||||
import { MobSkill, MobSkillType } from "../../massive-darkness2.model.boss";
|
||||
|
||||
const MD2_IMG_URL = (id: string = null) => { return `${environment.apiUrl}/Files/Images/MD2/Mobs${(id ? `${encodeURI(id)}` : '')}` }
|
||||
const CORE_GAME_MOB_LEVEL = [
|
||||
|
||||
new MobInfo({
|
||||
name: 'Andra', level: 1, hp: 5,
|
||||
|
||||
attackInfos: [new AttackInfo(MD2Icon.Melee, 1, 0, 0, 1), new AttackInfo(MD2Icon.Range, 1, 0, 0, 1)],
|
||||
defenseInfo: new DefenseInfo(2, 1),
|
||||
}),
|
||||
new MobInfo({
|
||||
name: 'Andra', level: 3, hp: 7,
|
||||
|
||||
attackInfos: [new AttackInfo(MD2Icon.Melee, 1, 1, 0, 1), new AttackInfo(MD2Icon.Range, 1, 1, 0, 1)],
|
||||
defenseInfo: new DefenseInfo(3, 1),
|
||||
}),
|
||||
new MobInfo({
|
||||
name: 'Andra', level: 5, hp: 5,
|
||||
attackInfos: [new AttackInfo(MD2Icon.Melee, 1, 2, 0, 1), new AttackInfo(MD2Icon.Range, 1, 2, 0, 1)],
|
||||
defenseInfo: new DefenseInfo(5, 1),
|
||||
}),
|
||||
|
||||
new MobInfo({
|
||||
name: 'Ytheria, Undead Queen', level: 1, hp: 4,
|
||||
attackInfos: [new AttackInfo(MD2Icon.Melee, 1), new AttackInfo(MD2Icon.Range, 2, 0, 0, 1)],
|
||||
defenseInfo: new DefenseInfo(1, 1),
|
||||
|
||||
}),
|
||||
new MobInfo({
|
||||
name: 'Ytheria, Undead Queen', level: 3, hp: 6,
|
||||
attackInfos: [new AttackInfo(MD2Icon.Melee, 0, 1), new AttackInfo(MD2Icon.Range, 1, 1, 0, 1)],
|
||||
defenseInfo: new DefenseInfo(2, 1),
|
||||
|
||||
}),
|
||||
new MobInfo({
|
||||
name: 'Ytheria, Undead Queen', level: 5, hp: 8,
|
||||
attackInfos: [new AttackInfo(MD2Icon.Melee, 2, 1), new AttackInfo(MD2Icon.Range, 2, 1, 0, 1)],
|
||||
defenseInfo: new DefenseInfo(4, 1),
|
||||
}),
|
||||
|
||||
new MobInfo({
|
||||
name: 'Lyidan, Incubus Lord', level: 1, hp: 7,
|
||||
attackInfos: [new AttackInfo(MD2Icon.Melee, 0, 1, 0, 2)],
|
||||
defenseInfo: new DefenseInfo(2, 1),
|
||||
}),
|
||||
new MobInfo({
|
||||
name: 'Lyidan, Incubus Lord', level: 3, hp: 10,
|
||||
attackInfos: [new AttackInfo(MD2Icon.Melee, 0, 2, 0, 1)],
|
||||
defenseInfo: new DefenseInfo(2, 1),
|
||||
}),
|
||||
new MobInfo({
|
||||
name: 'Lyidan, Incubus Lord', level: 5, hp: 12,
|
||||
attackInfos: [new AttackInfo(MD2Icon.Melee, 2, 2, 0, 1)],
|
||||
defenseInfo: new DefenseInfo(4, 1),
|
||||
}),
|
||||
|
||||
new MobInfo({
|
||||
name: 'The Ghoul', level: 1, hp: 5,
|
||||
attackInfos: [new AttackInfo(MD2Icon.Melee, 0, 1, 0, 1)],
|
||||
defenseInfo: new DefenseInfo(2, 1),
|
||||
}),
|
||||
new MobInfo({
|
||||
name: 'The Ghoul', level: 3, hp: 8,
|
||||
attackInfos: [new AttackInfo(MD2Icon.Melee, 0, 2, 0, 2)],
|
||||
defenseInfo: new DefenseInfo(3, 1),
|
||||
}),
|
||||
new MobInfo({
|
||||
name: 'The Ghoul', level: 5, hp: 10,
|
||||
attackInfos: [new AttackInfo(MD2Icon.Melee, 0, 3, 0, 3)],
|
||||
defenseInfo: new DefenseInfo(4, 1),
|
||||
}),
|
||||
]
|
||||
|
||||
export abstract class CoreGameRMFactory implements IMobFactory {
|
||||
abstract mobName: string;
|
||||
abstract generate(level: number): MobInfo
|
||||
protected mob: MobInfo;
|
||||
protected loadLevelInfo(mobName: string, level: number) {
|
||||
let levelInfo = CORE_GAME_MOB_LEVEL.find(m => m.name == mobName && level >= m.level);
|
||||
|
||||
this.mob = new MobInfo({
|
||||
type: MobType.RoamingMonster,
|
||||
name: mobName, hpPerHero: levelInfo.hp, level: level, rewardTokens: levelInfo.rewardTokens,
|
||||
attackInfos: levelInfo.attackInfos,
|
||||
defenseInfo: levelInfo.defenseInfo,
|
||||
actionSubject: new Subject<string>()
|
||||
});
|
||||
this.mob.leaderImgUrl = MD2_IMG_URL(`/CoreGame/RoamingMonsters/${this.mob.name}/Stand.png`);
|
||||
//this.mob.minionImgUrl = MD2_IMG_URL(`CoreGame/Mobs/${encodeURI(this.mob.name)}/Minion.png`);
|
||||
|
||||
|
||||
if (level < 3) {
|
||||
this.mob.rewardTokens = 2;
|
||||
this.mob.fixedCarriedTreasure = [new TreasureItem(TreasureType.Rare)];
|
||||
} else if (level < 5) {
|
||||
this.mob.rewardTokens = 2;
|
||||
this.mob.fixedCarriedTreasure = [new TreasureItem(TreasureType.Epic)];
|
||||
} else {
|
||||
this.mob.rewardTokens = 0;
|
||||
this.mob.fixedCarriedTreasure = [new TreasureItem(TreasureType.Epic, 3)];
|
||||
}
|
||||
|
||||
}
|
||||
iconHtml(icon: MD2Icon, cssClass = '') {
|
||||
if (icon == MD2Icon.Fire) {
|
||||
cssClass += ' g-color-google-plus ';
|
||||
}
|
||||
if (icon == MD2Icon.Frost || icon == MD2Icon.Mana) {
|
||||
cssClass += ' g-color-aqua ';
|
||||
}
|
||||
|
||||
if (icon < MD2Icon.RedDice) {
|
||||
return `<span class='MD2Icon ${cssClass}'>${String.fromCharCode(65 + icon)}</span>`
|
||||
|
||||
}
|
||||
else {
|
||||
return `<span class='MD2Icon dice ${MD2Icon[icon].replace('Dice', '')} ${cssClass}'></span>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
export class RMUndeadQueenFactory extends CoreGameRMFactory {
|
||||
mobName: string = 'Ytheria, Undead Queen';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('Ytheria, Undead Queen', level);
|
||||
|
||||
this.mob.activateFunction = (mob, msgBoxService, heros) => {
|
||||
let actionResult = '';
|
||||
|
||||
msgBoxService.show('Is There more than 1 Hero in LoS of Undead Queen?', {
|
||||
icon: ADIcon.QUESTION,
|
||||
buttons: ADButtons.YesNo
|
||||
}).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
mob.actions = 0;
|
||||
mob.actionSubject.next(
|
||||
`Undead Queen attacks each Hero in LoS(resolve each attack separately).`
|
||||
);
|
||||
} else {
|
||||
msgBoxService.show('Is There any Hero in LoS of Undead Queen?', {
|
||||
icon: ADIcon.QUESTION,
|
||||
buttons: ADButtons.YesNo
|
||||
}).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
if (level < 3) {
|
||||
actionResult = `Undead Queen +1 ${this.iconHtml(MD2Icon.YellowDice)} when attack`;
|
||||
} else if (level < 5) {
|
||||
actionResult = `Undead Queen +1 ${this.iconHtml(MD2Icon.YellowDice)} 1 ${this.iconHtml(MD2Icon.OrangeDice)} when attack`;
|
||||
} else {
|
||||
actionResult = `Undead Queen +2 ${this.iconHtml(MD2Icon.OrangeDice)} when attack`;
|
||||
}
|
||||
mob.actions = 0;
|
||||
mob.actionSubject.next(
|
||||
actionResult
|
||||
);
|
||||
} else {
|
||||
|
||||
mob.actions = 2;
|
||||
mob.actionSubject.next(
|
||||
`Undead Queen Gains 2 Actions`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});;
|
||||
}
|
||||
|
||||
this.mob.combatSkill = new MobSkill(
|
||||
{
|
||||
description: `Add 1 Minion to each Mob in the Dungeon, if possible.`,
|
||||
type: MobSkillType.Attack
|
||||
}
|
||||
)
|
||||
return this.mob;
|
||||
}
|
||||
}
|
||||
export class RMAndraFactory extends CoreGameRMFactory {
|
||||
mobName: string = 'Andra';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('Andra', level);
|
||||
let damage = 2;
|
||||
|
||||
if (level < 3) {
|
||||
damage = 1;
|
||||
}
|
||||
|
||||
this.mob.activateFunction = (mob, msgBoxService, heros) => {
|
||||
let actionResult = '';
|
||||
|
||||
mob.actions = 0;
|
||||
msgBoxService.show('Is Andra in the Dungeon?', {
|
||||
icon: ADIcon.QUESTION,
|
||||
buttons: ADButtons.YesNo
|
||||
}).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
mob.actions = 0;
|
||||
mob.actionSubject.next(
|
||||
`Undead Queen attacks each Hero in LoS(resolve each attack separately).`
|
||||
);
|
||||
|
||||
msgBoxService.show('Is Any Hero in the LoS of Andra?', {
|
||||
icon: ADIcon.QUESTION,
|
||||
buttons: ADButtons.YesNo
|
||||
}).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
|
||||
mob.actionSubject.next(
|
||||
`Andra attack the Hero with the lowest HP in LoS.<br>then Put Andra to out side of Dungeon.(Hero is not reachable but not dead)`
|
||||
);
|
||||
} else {
|
||||
mob.actions = 0;
|
||||
mob.actionSubject.next(
|
||||
`Put Andra to out side of Dungeon.(Hero is not reachable but not dead)`
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let beenAttackHero = MD2Logic.getTargetHerosByFilter(heros, AttackTarget.LeastHp, true)[0];
|
||||
mob.actionSubject.next(
|
||||
`Place Andra in the same zone of ${MD2Logic.heroFullName(beenAttackHero)} and attack that Hero.`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.mob.combatSkill = new MobSkill(
|
||||
{
|
||||
description: `Deal ${damage} wound to another Hero with the lowest HP in LoS`,
|
||||
type: MobSkillType.Combat
|
||||
}
|
||||
)
|
||||
return this.mob;
|
||||
}
|
||||
}
|
||||
export class RMTheGhoulFactory extends CoreGameRMFactory {
|
||||
mobName: string = 'The Ghoul';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('The Ghoul', level);
|
||||
let health = 2;
|
||||
|
||||
if (level < 3) {
|
||||
health = 5;
|
||||
} else if (level < 5) {
|
||||
health = 8;
|
||||
} else {
|
||||
health = 10;
|
||||
}
|
||||
|
||||
this.mob.activateFunction = (mob, msgBoxService, heros) => {
|
||||
let actionResult = '';
|
||||
|
||||
mob.actions = 0;
|
||||
msgBoxService.show('Is there any <b>Mob with minion</b> in The Ghoul zone?', {
|
||||
icon: ADIcon.QUESTION,
|
||||
buttons: ADButtons.YesNo
|
||||
}).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
mob.unitRemainHp += health;
|
||||
mob.actionSubject.next(
|
||||
`Kill 1 minion in The Ghoul zone(player choose), The Ghoul heals ${health}.`
|
||||
);
|
||||
} else {
|
||||
mob.actionSubject.next(
|
||||
`The Ghoul moves 3 Zones toward the closest Hero and attack him/her, if possible.`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.mob.combatSkill = new MobSkill(
|
||||
{
|
||||
description: `Move the closest <b>Mob with minion</b> 1 Zone toward The Ghoul.`,
|
||||
type: MobSkillType.Combat
|
||||
}
|
||||
)
|
||||
return this.mob;
|
||||
}
|
||||
}
|
||||
export class RMLyidanIncubusLordFactory extends CoreGameRMFactory {
|
||||
mobName: string = 'Lyidan, Incubus Lord';
|
||||
generate(level: number): MobInfo {
|
||||
this.loadLevelInfo('Lyidan, Incubus Lord', level);
|
||||
this.mob.activateFunction = (mob, msgBoxService, heros) => {
|
||||
let actionResult = '';
|
||||
|
||||
mob.actions = 0;
|
||||
msgBoxService.show('Is Incubus Lord in a Light Zone?', {
|
||||
icon: ADIcon.QUESTION,
|
||||
buttons: ADButtons.YesNo
|
||||
}).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
mob.unitRemainHp -= 3;
|
||||
mob.actionSubject.next(
|
||||
`The Incubus Lord got 3 Wounds, Move it to the closest Shadow Zone.`
|
||||
);
|
||||
} else {
|
||||
msgBoxService.show('Is there a Herp up to 3 Zones away(regardless of LoS) from The Incubus Lord?', {
|
||||
icon: ADIcon.QUESTION,
|
||||
buttons: ADButtons.YesNo
|
||||
}).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
mob.actionSubject.next(
|
||||
`Place The Incubus Lord in the zone of furthest Hero up to 3 Zones away.<br>` +
|
||||
`Add 1 ${this.iconHtml(MD2Icon.Fire)} to that Hero and attack him/her.`
|
||||
);
|
||||
} else {
|
||||
mob.actions = 2;
|
||||
mob.actionSubject.next(
|
||||
`The Incubus Lord 2 Actions`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.mob.combatSkill = new MobSkill(
|
||||
{
|
||||
description: `After combat, resolve all ${this.iconHtml(MD2Icon.Fire)} on the defending Hero(once per combat).`,
|
||||
type: MobSkillType.Attack
|
||||
}
|
||||
)
|
||||
return this.mob;
|
||||
}
|
||||
}
|
||||
export const CoreGameRMFactories = [
|
||||
new RMUndeadQueenFactory(),
|
||||
new RMAndraFactory(),
|
||||
new RMTheGhoulFactory(),
|
||||
new RMLyidanIncubusLordFactory(),
|
||||
];
|
||||
@ -1,9 +1,9 @@
|
||||
<nb-card *ngIf="!md2Service.playerHero">
|
||||
<nb-card *ngIf="!hero">
|
||||
<nb-card-body>
|
||||
<button nbButton hero status="primary" fullWidth (click)="initHero()">Choose Hero</button>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
<div *ngIf="md2Service.playerHero">
|
||||
<div *ngIf="hero">
|
||||
<div class="row no-gutters">
|
||||
<div class="col-12 col-sm-7">
|
||||
<div class="tp-wrapper mb-2">
|
||||
@ -11,24 +11,26 @@
|
||||
[@flipState]="flip">
|
||||
<div class="tp-box__side tp-box__front ">
|
||||
|
||||
<img class="MD2HeroCard " src="{{md2Service.playerHero.imgUrl}}">
|
||||
<img class="MD2HeroCard " src="{{hero.imgUrl}}">
|
||||
</div>
|
||||
<div class="tp-box__side tp-box__back">
|
||||
|
||||
|
||||
<img class="MD2HeroCard " src="{{imgUrl('Heros/Guide/'+className+'.jpg')}}">
|
||||
|
||||
<img class="MD2HeroCard " src="{{imgUrl('Sets/Shadowbane/'+className+'.png')}}">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="g-max-height-80vh mb-2">
|
||||
<img class="MD2HeroCard" src="{{md2Service.playerHero.imgUrl}}">
|
||||
<img class="MD2HeroCard" src="{{hero.imgUrl}}">
|
||||
<div class="MD2HeroCard">
|
||||
<span class="MD2text MD2Name">{{md2Service.playerHero.name}}</span>
|
||||
<span class="MD2text MD2Hp">{{md2Service.playerHero.hpMaximum}}</span>
|
||||
<span class="MD2text MD2Mp">{{md2Service.playerHero.mpMaximum}}</span>
|
||||
<span class="MD2text MD2Name">{{hero.name}}</span>
|
||||
<span class="MD2text MD2Hp">{{hero.hpMaximum}}</span>
|
||||
<span class="MD2text MD2Mp">{{hero.mpMaximum}}</span>
|
||||
</div>
|
||||
<img class="MD2HeroCard" src="{{md2Service.playerHero.imgUrl}}">
|
||||
<img class="MD2HeroCard" src="{{hero.imgUrl}}">
|
||||
<img class="MD2HeroCard HpMpBar" src="{{imgUrl('/Heros/Template/Border.png')}}">
|
||||
</div> -->
|
||||
</div>
|
||||
@ -39,63 +41,57 @@
|
||||
<div class="row no-gutters">
|
||||
<div class="col-6">
|
||||
|
||||
<!-- <adj-number-input name="heroHP" [(ngModel)]="md2Service.playerHero.hp"
|
||||
[maximum]="md2Service.playerHero.hpMaximum" minimum="0"
|
||||
<!-- <adj-number-input name="heroHP" [(ngModel)]="hero.hp"
|
||||
[maximum]="hero.hpMaximum" minimum="0"
|
||||
title="{{iconHtml(MD2Icon.HP,'g-color-google-plus mr-1 g-font-size-18')}}HP" showMaximum
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()" (hitDecreasing)="increaseRage()">
|
||||
</adj-number-input> -->
|
||||
|
||||
|
||||
<adj-number-input name="heroHP" [(ngModel)]="md2Service.playerHero.hp"
|
||||
[maximum]="md2Service.playerHero.hpMaximum" minimum="0"
|
||||
<adj-number-input name="heroHP" [(ngModel)]="hero.hp" [maximum]="hero.hpMaximum" minimum="0"
|
||||
title="{{imgHtml('HpIcon.png','g-height-25 mr-1')}}HP" showMaximum
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()" (hitDecreasing)="increaseRage()">
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroMana" [(ngModel)]="md2Service.playerHero.mp"
|
||||
[maximum]="md2Service.playerHero.mpMaximum" minimum="0"
|
||||
title="{{imgHtml('HeroIcon.png','g-height-25 mr-1')}}Mana" showMaximum
|
||||
<adj-number-input name="heroMana" [(ngModel)]="hero.mp" [maximum]="hero.mpMaximum"
|
||||
minimum="0" title="{{imgHtml('HeroIcon.png','g-height-25 mr-1')}}Mana" showMaximum
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
|
||||
<adj-number-input name="heroFire" [(ngModel)]="md2Service.playerHero.fireToken" minimum="0"
|
||||
<adj-number-input name="heroFire" [(ngModel)]="hero.fireToken" minimum="0"
|
||||
title="{{iconHtml(MD2Icon.Fire,'g-color-google-plus mr-1 g-font-size-18')}}Fire Token"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroFire" [(ngModel)]="md2Service.playerHero.frozenToken"
|
||||
minimum="0"
|
||||
<adj-number-input name="heroFire" [(ngModel)]="hero.frozenToken" minimum="0"
|
||||
title="{{iconHtml(MD2Icon.Frost,'g-color-aqua mr-1 g-font-size-18')}}Frozen Token"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
|
||||
<adj-number-input name="remainActions" [(ngModel)]="md2Service.playerHero.remainActions"
|
||||
minimum="0" title="Remain Actions" (blur)="heroUpdateDebounceTimer.resetTimer()"
|
||||
hideIncreaseBtn>
|
||||
<adj-number-input name="remainActions" [(ngModel)]="hero.remainActions" minimum="0"
|
||||
title="Remain Actions" (blur)="heroUpdateDebounceTimer.resetTimer()" hideIncreaseBtn
|
||||
*ngIf="hero.uiActivating">
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroLevel" [(ngModel)]="md2Service.playerHero.level" minimum="1"
|
||||
maximum="5" title="Level" (blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
<adj-number-input name="heroLevel" [(ngModel)]="hero.level" minimum="1" maximum="5"
|
||||
title="Level" (blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroExp" [(ngModel)]="md2Service.playerHero.exp" minimum="0"
|
||||
title="Exp" (blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
<adj-number-input name="heroExp" [(ngModel)]="hero.exp" minimum="0" title="Exp"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroRage" [(ngModel)]="md2Service.playerHero.rage" minimum="0"
|
||||
maximum="7"
|
||||
<adj-number-input name="heroRage" [(ngModel)]="hero.rage" minimum="0" maximum="7"
|
||||
title="{{iconHtml(MD2Icon.Rage,'g-color-google-plus mr-1 g-font-size-18')}}Rage"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()"
|
||||
*ngIf="md2Service.playerHero.class==HeroClass.Berserker">
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()" *ngIf="hero.class==HeroClass.Berserker">
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroCorruption" [(ngModel)]="md2Service.playerHero.corruptionToken"
|
||||
minimum="0" title="{{imgHtml('Tokens/CorruptToken.png','g-height-18')}} Corruption"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()"
|
||||
*ngIf="md2Service.playerHero.corruptionToken>0">
|
||||
<adj-number-input name="heroCorruption" [(ngModel)]="hero.corruptionToken" minimum="0"
|
||||
title="{{imgHtml('Tokens/CorruptToken.png','g-height-18')}} Corruption"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()" *ngIf="hero.uiShowCorruptionToken">
|
||||
</adj-number-input>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div *ngIf="md2Service.info.isBossFight"></div>
|
||||
<div *ngIf="md2Service.playerHero.remainActions>0">
|
||||
<div *ngIf="hero.uiActivating&&hero.remainActions>0">
|
||||
<button nbButton hero class="mr-2" status="info" (click)="moveAction()"
|
||||
*ngIf="!showMoveAction">Move</button>
|
||||
<button nbButton hero class="mr-2" status="info" (click)="moveActionEnd()"
|
||||
@ -109,8 +105,14 @@
|
||||
|
||||
</div>
|
||||
|
||||
<button nbButton hero fullWidth status="info" *ngIf="allowStartAction"
|
||||
(click)="startActivation()">Start Activation</button>
|
||||
|
||||
<button nbButton hero status="info" class="mt-2" (click)="openDoor()" *ngIf="showMoveAction">Open
|
||||
Door</button>
|
||||
<button nbButton hero fullWidth status="warning" class="mt-3" *ngIf="hero.uiActivating"
|
||||
(click)="endActivation()">End
|
||||
Activation</button>
|
||||
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
@ -121,7 +123,7 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- <nb-flip-card *ngIf="md2Service.playerHero">
|
||||
<!-- <nb-flip-card *ngIf="hero">
|
||||
<nb-card-front>
|
||||
<nb-card>
|
||||
|
||||
|
||||
@ -4,10 +4,11 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { DropDownOption } from '../../../entity/dropDownOption';
|
||||
import { GameRoomService } from '../../../services/game-room.service';
|
||||
import { MD2Service } from '../../../services/md2.service';
|
||||
import { MD2Service } from '../../../services/MD2/md2.service';
|
||||
import { MsgBoxService } from '../../../services/msg-box.service';
|
||||
import { StateService } from '../../../services/state.service';
|
||||
import { ADButtonColor, ADButtons } from '../../../ui/alert-dlg/alert-dlg.model';
|
||||
import { ArrayUtils } from '../../../utilities/array-utils';
|
||||
import { StringUtils } from '../../../utilities/string-utils';
|
||||
import { DebounceTimer } from '../../../utilities/timer-utils';
|
||||
import { HeroClass, MD2HeroInfo } from '../massive-darkness2.model';
|
||||
@ -51,6 +52,7 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
|
||||
new DropDownOption(HeroClass.Rogue, 'Rogue'),
|
||||
new DropDownOption(HeroClass.Wizard, 'Wizard'),
|
||||
new DropDownOption(HeroClass.Shaman, 'Shaman'),
|
||||
new DropDownOption(HeroClass.Druid, 'Druid'),
|
||||
];
|
||||
heros = [] as MD2HeroInfo[];
|
||||
wizards: MD2HeroInfo[] = [
|
||||
@ -76,7 +78,7 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
|
||||
}
|
||||
|
||||
public get allowAttack(): boolean {
|
||||
return (!!this.md2Service.mobs && this.md2Service.mobs.length > 0) || (!!this.md2Service.roamingMonsters && this.md2Service.roamingMonsters.length > 0);
|
||||
return this.md2Service.playerHero.uiBossFight || (!!this.md2Service.mobs && this.md2Service.mobs.length > 0) || (!!this.md2Service.roamingMonsters && this.md2Service.roamingMonsters.length > 0);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
@ -130,7 +132,7 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
|
||||
class: heroClass
|
||||
}))
|
||||
}
|
||||
this.heros = this.heros.sort((a, b) => StringUtils.compareSemVer(a.name, b.name));
|
||||
this.heros = ArrayUtils.Shuffle(this.heros);//.sort((a, b) => StringUtils.compareSemVer(a.name, b.name));
|
||||
this.showHeroList(heroClass, 0);
|
||||
});
|
||||
}
|
||||
@ -157,16 +159,16 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
|
||||
}
|
||||
|
||||
broadcastHeroInfo() {
|
||||
this.md2Service.broadcastMyHeroInfo();
|
||||
this.md2Service.broadcastService.broadcastMyHeroInfo();
|
||||
this.heroUpdateDebounceTimer.clearOut();
|
||||
}
|
||||
increaseRage() {
|
||||
if (this.md2Service.playerHero.rage < 7) {
|
||||
this.md2Service.playerHero.rage++;
|
||||
if (this.hero.rage < 7) {
|
||||
this.hero.rage++;
|
||||
}
|
||||
}
|
||||
openDoor() {
|
||||
this.md2Service.broadcastHeroAction('openDoor');
|
||||
this.md2Service.broadcastService.broadcastHeroAction('openDoor');
|
||||
this.showMoveAction = false;
|
||||
this.detectChanges();
|
||||
}
|
||||
@ -183,17 +185,19 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
|
||||
this.showMoveAction = false;
|
||||
switch (action) {
|
||||
case 'recoveryAction':
|
||||
this.msgBoxService.show('Recovery', { text: 'takes the Recover action may gain up to 2 Health or Mana in any combination (either 2 Health, 2 Mana, or 1 of each).' })
|
||||
this.msgBoxService.show('Recovery', { text: 'takes the Recover action may gain up to 2 Health or Mana in any combination (either 2 Health, 2 Mana, or 1 of each).' });
|
||||
break;
|
||||
case 'attackAction':
|
||||
this.msgBoxService.show('Attacking', { text: 'Please process attacking action in Dashboard.' });
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this.md2Service.broadcastHeroAction(action);
|
||||
this.md2Service.broadcastService.broadcastHeroAction(action);
|
||||
this.reduceAction();
|
||||
}
|
||||
reduceAction() {
|
||||
this.md2Service.playerHero.remainActions -= 1;
|
||||
this.hero.remainActions -= 1;
|
||||
this.detectChanges();
|
||||
this.broadcastHeroInfo();
|
||||
}
|
||||
@ -202,4 +206,30 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
|
||||
toggleFlip() {
|
||||
this.flip = (this.flip == 'inactive') ? 'active' : 'inactive';
|
||||
}
|
||||
|
||||
get allowStartAction() {
|
||||
return !this.md2Service.heros.some(h => h.uiActivating) && !this.hero.uiActivating && this.hero.remainActions > 0;
|
||||
}
|
||||
|
||||
public get hero() {
|
||||
return this.md2Service.playerHero;
|
||||
}
|
||||
startActivation() {
|
||||
this.hero.uiActivating = true;
|
||||
this.broadcastHeroInfo();
|
||||
}
|
||||
endActivation() {
|
||||
if (this.hero.remainActions > 0) {
|
||||
this.msgBoxService.show('Are you sure?', { text: `End Activation will lose ${this.hero.remainActions} remaining actions.`, buttons: ADButtons.YesNo }).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
this.hero.remainActions = 0;
|
||||
this.endActivation();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.hero.uiActivating = false;
|
||||
this.broadcastHeroInfo();
|
||||
this.detectChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
|
||||
<div class="row" *ngIf="md2Service.heros.length==0">
|
||||
<div class="row" *ngIf="md2Service.heros.length==0&& false">
|
||||
<div class="col-6">
|
||||
|
||||
<adj-number-input name="heroLevel" [(ngModel)]="md2Service.playerAmount" [maximum]="6"
|
||||
@ -59,9 +59,10 @@
|
||||
<span class="badge badge-success mr-1">Exp: {{hero.exp}}</span>
|
||||
<span class="badge badge-danger mr-1" *ngIf="hero.fireToken">Fire:{{hero.fireToken}}</span>
|
||||
<span class="badge badge-info mr-1" *ngIf="hero.frozenToken">Frozen:{{hero.frozenToken}}</span>
|
||||
<span class="badge badge-light mr-1" *ngIf="hero.remainActions==0">Inactive</span>
|
||||
<span class="badge badge-success mr-1" *ngIf="hero.remainActions>0">Remain
|
||||
Actions: {{hero.remainActions}}</span>
|
||||
<span class="badge badge-light mr-1" *ngIf=" !hero.uiActivating">Inactive</span>
|
||||
<span class="badge badge-primary mr-1" *ngIf="hero.uiActivating">Activating</span>
|
||||
<!-- <span class="badge badge-success mr-1">{{hero.playerInfo.signalRClientId}}</span> -->
|
||||
|
||||
</div>
|
||||
|
||||
@ -6,7 +6,7 @@ import { ArrayUtils } from '../../utilities/array-utils';
|
||||
import { ObjectUtils } from '../../utilities/object-utils';
|
||||
import { first, map, take, takeUntil } from 'rxjs/operators';
|
||||
import { TreasureType, DrawingBag, DrawingItem, HeroClass, MD2HeroInfo, RoundPhase, MobInfo, MobDlgType } from './massive-darkness2.model';
|
||||
import { MD2Service } from '../../services/md2.service';
|
||||
import { MD2Service } from '../../services/MD2/md2.service';
|
||||
import { GameRoomService } from '../../services/game-room.service';
|
||||
import { MD2Base } from './MD2Base';
|
||||
import { StateService } from '../../services/state.service';
|
||||
@ -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 = `<img src="${mob.imageUrl}" class='g-height-70vh'><br>`;
|
||||
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();
|
||||
|
||||
}
|
||||
}
|
||||
69
src/app/games/massive-darkness2/massive-darkness2.logic.ts
Normal file
69
src/app/games/massive-darkness2/massive-darkness2.logic.ts
Normal file
@ -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 <b>Lowest HP</b> 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 <b>Lowest HP</b> 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 <b>Highest HP</b> 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 <b>Highest Mp</b> 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 <b>Lowest Level</b> 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 `<b>${StringUtils.makeCommaSeparatedString(beenAttackedHero.map(h => this.heroFullName(h)), false, true)}</b>`;
|
||||
}
|
||||
|
||||
public static heroFullName(hero: MD2HeroInfo) {
|
||||
if (!hero) return '';
|
||||
return `${hero.playerInfo.name} (${HeroClass[hero.class]} - ${hero.name})`
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,37 @@
|
||||
import { Subject } from "rxjs"
|
||||
import { stringify } from "querystring"
|
||||
import { Observable, Subject, Subscription } from "rxjs"
|
||||
import { first } from "rxjs/operators"
|
||||
import { MD2Service } from "../../services/md2.service"
|
||||
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 } from "./massive-darkness2.model"
|
||||
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>
|
||||
@ -14,53 +39,109 @@ export interface IBossFight {
|
||||
spawnRoamingMonster: Subject<void>
|
||||
rounds: number
|
||||
actions: number
|
||||
hpPerHero: number
|
||||
activatedTimes: number
|
||||
info: MobInfo
|
||||
actionBlackDice: number
|
||||
imgUrl: string
|
||||
standUrl: string
|
||||
combatInfo: MobSkill
|
||||
extraRules: string
|
||||
activating(): boolean
|
||||
prepareForBossFight(): void
|
||||
nextRound(): void
|
||||
darknessPhase(): void
|
||||
}
|
||||
export class BossMicheal implements IBossFight {
|
||||
|
||||
constructor(private md2Service: MD2Service) {
|
||||
this.name = 'Michael - The Corrupted Archangel';
|
||||
this.imgUrl = md2Service.imgUrl('/Boss/Michael - The Corrupted Archangel.jpg');
|
||||
this.standUrl = md2Service.imgUrl('/Boss/Michael.png');
|
||||
this.hpPerHero = 15;
|
||||
this.info = new MobInfo({
|
||||
isRoamingMonster: true,
|
||||
hp: this.md2Service.heros.length * this.hpPerHero,
|
||||
level: 10
|
||||
});
|
||||
this.info.defenseInfos = new DefenseInfo(5, 1);
|
||||
this.info.attackInfos = [new AttackInfo(MD2Icon.Melee, 2, 2, 0, 1)];
|
||||
this.actions = 1;
|
||||
this.rounds = 0;
|
||||
this.actionBlackDice = 2;
|
||||
this.corruptionTokenHtml = this.md2Service.imgHtml('Tokens/CorruptToken.png');
|
||||
|
||||
this.combatInfo = new MobSkill(`Combat 1${this.md2Service.iconHtml(MD2Icon.EnemySkill)}`,
|
||||
`Deal 1 Wound for each ${this.corruptionTokenHtml} on the attacking or defending Hero. Discard the tokens afterwards(once per combat).`);;
|
||||
|
||||
}
|
||||
export abstract class BossFight implements IBossFight {
|
||||
name: string
|
||||
addTreasureToken: Subject<TreasureType>
|
||||
spawnMob: Subject<void>
|
||||
spawnRoamingMonster: Subject<void>
|
||||
rounds: number
|
||||
actions: number
|
||||
hpPerHero: number
|
||||
activatedTimes: number
|
||||
info: MobInfo
|
||||
actionBlackDice: number
|
||||
imgUrl: string
|
||||
standUrl: string
|
||||
combatInfo: MobSkill
|
||||
corruptionTokenHtml: 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 = '';
|
||||
@ -70,37 +151,50 @@ export class BossMicheal implements IBossFight {
|
||||
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.`, beenAttackedHero);
|
||||
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('Lance Dash',
|
||||
`Move Michael and Place 1 ${this.corruptionTokenHtml} in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.`, beenAttackedHero);
|
||||
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('Dark Blessing',
|
||||
`Place Michael ion 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>.`, beenAttackedHero);
|
||||
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;
|
||||
}
|
||||
this.md2Service.dlgService.open(BossActivationComponent, { context: { boss: this, bossAction: bossAction } }).onClose
|
||||
.pipe(first()).subscribe(result => {
|
||||
return this.md2Service.dlgService.open(BossActivationComponent, { context: { boss: this, bossAction: bossAction, currentAction: this.activatedTimes, allActions: this.actions } }).onClose;
|
||||
|
||||
});
|
||||
return true;
|
||||
}
|
||||
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;
|
||||
nextRound(): void {
|
||||
darknessPhase(): void {
|
||||
this.rounds++;
|
||||
switch (this.rounds) {
|
||||
case 3:
|
||||
@ -109,7 +203,7 @@ export class BossMicheal implements IBossFight {
|
||||
break;
|
||||
case 2:
|
||||
case 4:
|
||||
this.info.defenseInfos[0].black += 1;
|
||||
this.info.defenseInfo.black += 1;
|
||||
this.info.attackInfos[0].black += 1;
|
||||
break;
|
||||
// case 4:
|
||||
@ -123,13 +217,101 @@ export class BossMicheal implements IBossFight {
|
||||
}
|
||||
}
|
||||
|
||||
export class MobSkill {
|
||||
constructor(skillName: string, skillDescription: string, targetHeros: MD2HeroInfo[] = []) {
|
||||
this.skillName = skillName
|
||||
this.skillDescription = skillDescription
|
||||
this.targetHeros = targetHeros
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
skillName: string
|
||||
skillDescription: string
|
||||
targetHeros: MD2HeroInfo[]
|
||||
}
|
||||
@ -1,13 +1,18 @@
|
||||
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)}` : '')}` }
|
||||
export enum MobDlgType {
|
||||
Spawn,
|
||||
Activating,
|
||||
BeenAttacked,
|
||||
PreView
|
||||
PreView,
|
||||
Dashboard
|
||||
}
|
||||
export enum RoundPhase {
|
||||
HeroPhase,
|
||||
@ -29,7 +34,14 @@ export enum HeroClass {
|
||||
Ranger,
|
||||
Shaman,
|
||||
Paladin,
|
||||
Druid,
|
||||
}
|
||||
export enum MobType {
|
||||
Mob,
|
||||
RoamingMonster,
|
||||
Boss
|
||||
}
|
||||
|
||||
export enum MD2Icon {
|
||||
Attack,
|
||||
Defense,
|
||||
@ -60,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
|
||||
}
|
||||
@ -95,7 +109,6 @@ export class AttackInfo {
|
||||
red: number
|
||||
yellow: number
|
||||
black: number
|
||||
attackSkill: MobSkill
|
||||
}
|
||||
export class DefenseInfo {
|
||||
constructor(blue: number, black: number = 0) {
|
||||
@ -104,7 +117,6 @@ export class DefenseInfo {
|
||||
}
|
||||
blue: number
|
||||
black: number
|
||||
defenseSkill: MobSkill
|
||||
}
|
||||
export class MD2LevelUpReward {
|
||||
constructor(config: Partial<MD2LevelUpReward>) {
|
||||
@ -126,8 +138,12 @@ export class DrawingBag<T extends IDrawingItem> {
|
||||
}
|
||||
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[] {
|
||||
@ -138,7 +154,7 @@ export class DrawingBag<T extends IDrawingItem> {
|
||||
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) {
|
||||
@ -153,7 +169,7 @@ export class DrawingBag<T extends IDrawingItem> {
|
||||
const item = drawingPool[i];
|
||||
drawCalc += item.drawingWeight;
|
||||
if (drawCalc >= drawIndex) {
|
||||
drawItem = ObjectUtils.CloneValue(item);
|
||||
drawItem = MD2Clone.CloneDrawingItem(item);
|
||||
drawItem.drawingWeight = 1;
|
||||
break;
|
||||
}
|
||||
@ -177,7 +193,7 @@ export class DrawingBag<T extends IDrawingItem> {
|
||||
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 {
|
||||
@ -187,17 +203,34 @@ export class DrawingBag<T extends IDrawingItem> {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -207,10 +240,14 @@ export class DrawingBag<T extends IDrawingItem> {
|
||||
this.removedItems = [];
|
||||
}
|
||||
}
|
||||
|
||||
export interface IMobFactory {
|
||||
mobName: string;
|
||||
generate(level: number): MobInfo;
|
||||
}
|
||||
export interface IDrawingItem {
|
||||
imageUrl: string
|
||||
name: string
|
||||
get identifyName(): string
|
||||
description: string
|
||||
drawingWeight: number
|
||||
}
|
||||
@ -226,12 +263,28 @@ export class DrawingItem implements IDrawingItem {
|
||||
this.description = description
|
||||
this.drawingWeight = drawingWeight
|
||||
}
|
||||
get identifyName(): string {
|
||||
return this.name;
|
||||
}
|
||||
imageUrl: string
|
||||
name: string
|
||||
description: string
|
||||
drawingWeight: number
|
||||
}
|
||||
|
||||
export class TreasureItem extends DrawingItem {
|
||||
constructor(type: TreasureType, itemAmount: number = 1) {
|
||||
super(`${TreasureType[type]} Treasure`,
|
||||
`It's a ${TreasureType[type]} Treasure!`,
|
||||
MD2_IMG_URL(`TreasureToken/${TreasureType[type]}.png`), itemAmount);
|
||||
this.type = type;
|
||||
this.itemAmount = itemAmount;
|
||||
}
|
||||
type: TreasureType;
|
||||
itemAmount: number;
|
||||
get identifyName(): string {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
export class MobInfo implements IDrawingItem {
|
||||
constructor(
|
||||
config: Partial<MobInfo> = {}
|
||||
@ -241,21 +294,28 @@ export class MobInfo implements IDrawingItem {
|
||||
this.drawingWeight = 1;
|
||||
this.unitRemainHp = config.hp
|
||||
}
|
||||
type: MobType = MobType.Mob;
|
||||
imageUrl: string
|
||||
standUrl: string
|
||||
leaderImgUrl: string
|
||||
minionImgUrl: string
|
||||
name: string
|
||||
description: string
|
||||
drawingWeight: number
|
||||
level: number;
|
||||
rewardTokens: number;
|
||||
hp: number;
|
||||
hpPerHero: number;
|
||||
mobAmount: number;
|
||||
carriedTreasure: DrawingItem[];
|
||||
fixedCarriedTreasure: DrawingItem[];
|
||||
carriedTreasure: TreasureItem[];
|
||||
fixedCarriedTreasure: TreasureItem[];
|
||||
unitRemainHp: number;
|
||||
isRoamingMonster: boolean = false;
|
||||
attackInfos: AttackInfo[];
|
||||
defenseInfos: DefenseInfo;
|
||||
defenseInfo: DefenseInfo;
|
||||
actions: number = 0;
|
||||
activateDescription: string;
|
||||
combatSkill: MobSkill
|
||||
|
||||
fireToken: number = 0;
|
||||
frozenToken: number = 0;
|
||||
corruptionToken: number = 0;
|
||||
@ -265,47 +325,50 @@ 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 => `<img src="${i.imageUrl}" class='mr-1' width="40px">`)
|
||||
.concat(this.fixedCarriedTreasure?.map(i => `<img src="${i.imageUrl}" class='mr-1' width="40px">`)).join();
|
||||
.concat(this.fixedCarriedTreasure?.map(i => `<img src="${i.imageUrl}" class='mr-1' width="40px">`.repeat(i.drawingWeight))).join();
|
||||
}
|
||||
|
||||
public get totalHp(): number {
|
||||
return this.isRoamingMonster ? this.unitRemainHp : (this.mobAmount - 1) * this.hp + this.unitRemainHp;
|
||||
switch (this.type) {
|
||||
case MobType.Mob:
|
||||
return (this.mobAmount - 1) * this.hp + this.unitRemainHp;
|
||||
case MobType.RoamingMonster:
|
||||
return this.unitRemainHp;
|
||||
case MobType.Boss:
|
||||
default:
|
||||
return this.unitRemainHp;
|
||||
}
|
||||
}
|
||||
|
||||
public get minionAmount(): number {
|
||||
return (this.mobAmount - 1);
|
||||
switch (this.type) {
|
||||
case MobType.Mob:
|
||||
return (this.mobAmount - 1);
|
||||
case MobType.RoamingMonster:
|
||||
case MobType.Boss:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
public get leaderExp(): number {
|
||||
return this.isRoamingMonster ? 4 : 2;
|
||||
}
|
||||
|
||||
public get mobInfoHtml(): string {
|
||||
let html = `<img src="${this.imageUrl}" class="g-height-50vh">`
|
||||
+ `<br>Target Unit HP:${this.unitRemainHp}`;
|
||||
|
||||
if (this.isRoamingMonster) {
|
||||
html += `<br><label class="label">Alive Units:${this.mobAmount}`;
|
||||
} else {
|
||||
html += `<br>Total HP:${this.totalHp}`;
|
||||
switch (this.type) {
|
||||
case MobType.Mob:
|
||||
return 2;
|
||||
case MobType.RoamingMonster:
|
||||
return 4;
|
||||
case MobType.Boss:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
public getCssClass(): string {
|
||||
let levelString = '';
|
||||
if (this.level < 3) {
|
||||
levelString = '-lv1-2';
|
||||
} else if (this.level < 5) {
|
||||
|
||||
levelString = '-lv3-4';
|
||||
} else {
|
||||
levelString = '-lv5';
|
||||
}
|
||||
return `${this.name.replace(' ', '')}${levelString}`;
|
||||
}
|
||||
public actionSubject: Subject<string>;
|
||||
public activateFunction?: (mob: MobInfo, msgBoxService: MsgBoxService, heros: MD2HeroInfo[]) => void;
|
||||
}
|
||||
|
||||
export class MD2HeroInfo {
|
||||
@ -332,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})`
|
||||
}
|
||||
|
||||
}
|
||||
@ -344,7 +409,7 @@ export class MD2Rules {
|
||||
public static CoreGameLevelBoard = [
|
||||
new MD2LevelUpReward({ level: 2, needExp: 5, extraHp: 1, extraMp: 0, extraRareToken: 1 }),
|
||||
new MD2LevelUpReward({ level: 3, needExp: 10, extraHp: 1, extraMp: 1, extraEpicToken: 1 }),
|
||||
new MD2LevelUpReward({ level: 4, needExp: 12, extraHp: 2, extraMp: 2, extraEpicToken: 1 }),
|
||||
new MD2LevelUpReward({ level: 4, needExp: 12, extraHp: 2, extraMp: 1, extraEpicToken: 1 }),
|
||||
new MD2LevelUpReward({ level: 5, needExp: 18, extraHp: 2, extraMp: 2, extraEpicToken: 1 }),
|
||||
];
|
||||
public static checkCoreGameLevelup(currentLevel: number, currentExp: number): MD2LevelUpReward {
|
||||
@ -381,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;
|
||||
|
||||
@ -3,7 +3,7 @@ import { ControlValueAccessor, Validator, AbstractControl, ValidationErrors, NG_
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { DropDownOption } from '../../../entity/dropDownOption';
|
||||
import { MD2Service } from '../../../services/md2.service';
|
||||
import { MD2Service } from '../../../services/MD2/md2.service';
|
||||
import { ArrayUtils } from '../../../utilities/array-utils';
|
||||
import { HeroClass, MD2HeroInfo } from '../massive-darkness2.model';
|
||||
|
||||
|
||||
@ -1 +1 @@
|
||||
<span class="MD2Icon {{icon}} {{iconClass}}"></span>
|
||||
<span class="MD2Icon {{iconName}} {{iconClass}} {{sizeClass}}"></span>
|
||||
@ -8,14 +8,49 @@ import { MD2Icon } from '../massive-darkness2.model';
|
||||
})
|
||||
export class MD2IconComponent implements OnInit {
|
||||
|
||||
@Input() iconClass: string = '';
|
||||
|
||||
@Input("icon") icon: MD2Icon;
|
||||
@Input() iconClass: string = 'mr-1';
|
||||
|
||||
|
||||
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 {
|
||||
}
|
||||
|
||||
public get sizeClass(): string {
|
||||
switch (this.size) {
|
||||
case 'sm':
|
||||
return 'g-font-size-18'
|
||||
break;
|
||||
case 'med':
|
||||
return 'g-font-size-30'
|
||||
break;
|
||||
case 'lg':
|
||||
return 'g-font-size-50'
|
||||
break;
|
||||
|
||||
default:
|
||||
return 'g-font-size-' + this.size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,23 +1,30 @@
|
||||
<label class='label'>Weapon Info</label>
|
||||
<div class="g-brd-3 g-brd-bottom--dashed g-brd-gray-light-v2 mb-3 mt-2 row" *ngFor="let info of mob.attackInfos">
|
||||
<div class="col-md-4">
|
||||
<span class=" g-font-size-50" [innerHtml]="iconHtml(info.type)"></span>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div *ngIf="info.yellow" class="g-height-45">
|
||||
<span class="MD2Icon Yellow dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{info.yellow}}</span>
|
||||
</span>
|
||||
<!-- <label class='label'>Weapon Info</label> -->
|
||||
<ng-container *ngIf="display">
|
||||
<div class="g-brd-3 g-brd-bottom--dashed g-brd-gray-light-v2 mb-3 mt-4 row" *ngFor="let info of mob.attackInfos">
|
||||
<div class="col-md-4">
|
||||
<md2-icon [icon]="info.type" size="lg"></md2-icon>
|
||||
</div>
|
||||
<div *ngIf="info.orange" class="g-height-45 mt-1">
|
||||
<span class="MD2Icon Orange dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{info.orange}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="info.red" class="g-height-45 mt-1">
|
||||
<span class="MD2Icon Red dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{info.red}}</span>
|
||||
</span>
|
||||
<div class="col-md-8">
|
||||
<div *ngIf="info.yellow" class="g-height-45">
|
||||
<span class="MD2Icon Yellow dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{info.yellow}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="info.orange" class="g-height-45 mt-1">
|
||||
<span class="MD2Icon Orange dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{info.orange}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="info.red" class="g-height-45 mt-1">
|
||||
<span class="MD2Icon Red dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{info.red}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="mob.defenseInfo.black" class="g-height-45 mt-1">
|
||||
<span class="MD2Icon Black dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{mob.defenseInfo.black}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
@ -0,0 +1,19 @@
|
||||
.diceAmount {
|
||||
color: white;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
left: 25px;
|
||||
font-size: 30px;
|
||||
}
|
||||
.dice {
|
||||
&::before {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
.blackDiceAmount {
|
||||
color: white;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
left: 26px;
|
||||
font-size: 30px;
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { MD2Icon, MobInfo } from '../../../massive-darkness2.model';
|
||||
import { MD2Icon, MobDlgType, MobInfo, MobType } from '../../../massive-darkness2.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-mob-attack-info',
|
||||
selector: 'md2-mob-attack-info',
|
||||
templateUrl: './mob-attack-info.component.html',
|
||||
styleUrls: ['./mob-attack-info.component.scss']
|
||||
})
|
||||
@ -20,10 +20,25 @@ export class MobAttackInfoComponent implements OnInit {
|
||||
|
||||
}
|
||||
}
|
||||
@Input() mode: MobDlgType = MobDlgType.PreView;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1 +1,24 @@
|
||||
<p>mob-combat-info works!</p>
|
||||
<!-- <div class='form-group row' *ngIf="showSkill">
|
||||
<label class='label col-sm-3 form-control-label MD2text g-font-size-30'>
|
||||
{{mob.combatSkill.skillRoll}} <md2-icon icon="enemySkill" size="md"></md2-icon>
|
||||
</label>
|
||||
<div class='col-sm' [innerHtml]="mob.combatSkill.description"></div>
|
||||
</div> -->
|
||||
<div *ngIf="showBlackDice" class="row">
|
||||
<!-- <md2-icon></md2-icon> -->
|
||||
<div class="col-md-4">
|
||||
<md2-icon icon="enemySkill" size="lg"></md2-icon>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<span class="MD2Icon Black dice g-font-size-50">
|
||||
<span class="MD2text blackDiceAmount">x{{mob.minionAmount}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class='form-group' *ngIf="showSkill">
|
||||
<label for='' class='MD2text g-font-size-22 label mb-2'>
|
||||
<md2-icon icon="blackDice" size="lg"></md2-icon> {{skillTriggerHtml}} <md2-icon icon="enemySkill" size="md">
|
||||
</md2-icon>
|
||||
</label>
|
||||
<div class='g-font-size-20 skillDesc MD2text' [innerHtml]="mob.combatSkill.description"></div>
|
||||
</div>
|
||||
@ -0,0 +1,28 @@
|
||||
.diceAmount {
|
||||
color: white;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
left: 25px;
|
||||
font-size: 30px;
|
||||
}
|
||||
.dice {
|
||||
&::before {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
.blackDiceAmount {
|
||||
color: white;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
left: 26px;
|
||||
font-size: 30px;
|
||||
}
|
||||
.skillDesc {
|
||||
padding-left: 8px;
|
||||
.MD2Icon {
|
||||
font-size: 45px;
|
||||
}
|
||||
}
|
||||
.skillDesc .MD2Icon {
|
||||
font-size: 45px;
|
||||
}
|
||||
@ -1,15 +1,53 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { MD2Icon, MobDlgType, MobInfo, MobType } from '../../../massive-darkness2.model';
|
||||
import { MobSkillType } from '../../../massive-darkness2.model.boss';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-mob-combat-info',
|
||||
selector: 'md2-mob-combat-info',
|
||||
templateUrl: './mob-combat-info.component.html',
|
||||
styleUrls: ['./mob-combat-info.component.scss']
|
||||
})
|
||||
export class MobCombatInfoComponent 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;
|
||||
showSkill: boolean = false;
|
||||
showBlackDice: boolean
|
||||
skillTriggerHtml: string = '';
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.mob.combatSkill) {
|
||||
switch (this.mode) {
|
||||
case MobDlgType.Activating:
|
||||
this.showSkill = [MobSkillType.Combat, MobSkillType.Attack].includes(this.mob.combatSkill.type);
|
||||
break;
|
||||
case MobDlgType.BeenAttacked:
|
||||
this.showSkill = [MobSkillType.Combat, MobSkillType.Defense].includes(this.mob.combatSkill.type);
|
||||
break;
|
||||
case MobDlgType.PreView:
|
||||
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;;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1 +1,20 @@
|
||||
<p>mob-def-info works!</p>
|
||||
<!-- <label class='label'>Defense Info</label> -->
|
||||
<ng-container *ngIf="display">
|
||||
<div class="g-brd-3 g-brd-bottom--dashed g-brd-gray-light-v2 mb-3 mt-2 row">
|
||||
<div class="col-md-4">
|
||||
<md2-icon icon="defense" size="lg"></md2-icon>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div *ngIf="mob.defenseInfo.blue" class="g-height-45">
|
||||
<span class="MD2Icon Blue dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{mob.defenseInfo.blue}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="mob.defenseInfo.black" class="g-height-45 mt-1">
|
||||
<span class="MD2Icon Black dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{mob.defenseInfo.black}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
@ -0,0 +1,19 @@
|
||||
.diceAmount {
|
||||
color: white;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
left: 25px;
|
||||
font-size: 30px;
|
||||
}
|
||||
.dice {
|
||||
&::before {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
.blackDiceAmount {
|
||||
color: white;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
left: 26px;
|
||||
font-size: 30px;
|
||||
}
|
||||
@ -1,15 +1,43 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { MD2Icon, MobInfo, MobDlgType } from '../../../massive-darkness2.model';
|
||||
import { MobSkillType } from '../../../massive-darkness2.model.boss';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-mob-def-info',
|
||||
selector: 'md2-mob-def-info',
|
||||
templateUrl: './mob-def-info.component.html',
|
||||
styleUrls: ['./mob-def-info.component.scss']
|
||||
})
|
||||
export class MobDefInfoComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,48 +1,53 @@
|
||||
<label class='label'>Level <b class="MD2text g-font-size-18">{{mob.level}}</b></label><br>
|
||||
<div class="row no-gutters">
|
||||
<div class="col-md-6">
|
||||
|
||||
<ng-container *ngIf="mob.mobAmount">
|
||||
<label class='label'>Alive Units <b class="MD2text g-font-size-18">{{mob.mobAmount}}</b></label><br>
|
||||
<label class='label g-text-nowrap'>Level <b class="MD2text g-font-size-18">{{mob.level}}</b></label><br>
|
||||
|
||||
<label class='label g-text-nowrap'>Total HP <b class="MD2text g-font-size-18">{{mob.totalHp}}</b></label>
|
||||
</div>
|
||||
<div class="pl-2 col-md-6" *ngIf="mob.mobAmount">
|
||||
|
||||
<ng-container>
|
||||
<label class='label g-text-nowrap'>Alive Units <b
|
||||
class="MD2text g-font-size-18">{{mob.mobAmount}}</b></label><br>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6" *ngIf="showTokenAdjustment||mob.fireToken>0">
|
||||
|
||||
<adj-number-input name="mobFire" [(ngModel)]="mob.fireToken" minimum="0"
|
||||
title="{{iconHtml(MD2Icon.Fire,'g-color-google-plus mr-1 g-font-size-18')}}Fire Token">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
<div class="col-md-6" *ngIf="showTokenAdjustment||mob.frozenToken>0">
|
||||
|
||||
<adj-number-input name="mobFrost" [(ngModel)]="mob.frozenToken" minimum="0"
|
||||
title="{{iconHtml(MD2Icon.Frost,'g-color-aqua mr-1 g-font-size-18')}}Frozen Token">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<ng-container *ngIf="showAdjustment">
|
||||
|
||||
<adj-number-input name="mob{{mob.name}}" [ngModel]="mob.unitRemainHp" [maximum]="mob.hp" minimum="1"
|
||||
title="Target Unit HP" (hitChange)="adjustMobHp($event)" (hitMinimum)="adjustMobHp(-1)"
|
||||
(hitMaximum)="adjustMobHp(1)">
|
||||
</adj-number-input>
|
||||
</ng-container>
|
||||
|
||||
<label class='label'>Total HP <b class="MD2text g-font-size-18">{{mob.totalHp}}</b></label><br>
|
||||
|
||||
<ng-container *ngIf="mob.carriedTreasureHtml">
|
||||
<label class='label'>Carried Treasure</label><br>
|
||||
<div [innerHtml]="mob.carriedTreasureHtml"></div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="mob.attackInfos&&mob.attackInfos.length>0&& !hideWeaponInfo">
|
||||
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="mob.defenseInfos&& !hideWeaponInfo">
|
||||
<label class='label'>Defense Info</label>
|
||||
<div class="g-brd-3 g-brd-bottom--dashed g-brd-gray-light-v2 mb-3 mt-2 row">
|
||||
<div class="col-md-4">
|
||||
<span class="g-font-size-50" [innerHtml]="iconHtml(MD2Icon.Defense)"></span>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div *ngIf="mob.defenseInfos.blue" class="g-height-45">
|
||||
<span class="MD2Icon Blue dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{mob.defenseInfos.blue}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="mob.defenseInfos.black" class="g-height-45 mt-1">
|
||||
<span class="MD2Icon Black dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{mob.defenseInfos.black}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div *ngIf="showBlackDice" class="row">
|
||||
<!-- <md2-icon></md2-icon> -->
|
||||
<div class="col-md-4">
|
||||
<span class=" g-font-size-50" [innerHtml]="iconHtml(MD2Icon.EnemySkill)"></span>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<span class="MD2Icon Black dice g-font-size-50">
|
||||
<span class="MD2text blackDiceAmount">x{{mob.minionAmount}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="g-height-20 mb-1"></div>
|
||||
<md2-mob-attack-info [mob]="mob" [mode]="mode">
|
||||
</md2-mob-attack-info>
|
||||
<md2-mob-def-info [mob]="mob" [mode]="mode"></md2-mob-def-info>
|
||||
<md2-mob-combat-info [mob]="mob" [mode]="mode"></md2-mob-combat-info>
|
||||
@ -1,6 +1,7 @@
|
||||
import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { MD2Service } from '../../../../services/md2.service';
|
||||
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';
|
||||
import { MD2ComponentBase } from '../../MD2Base';
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
<img class="g-width-95x img-thumbnail mobBg" src="{{imgUrl('/Mobs/BG.png')}}" />
|
||||
<img class="mobImg roamingMonster" src="{{getMobImageUrl(mob)}}" (click)="showMobImage(mob)" *ngIf="!isMob" />
|
||||
<div *ngIf="isMob">
|
||||
<img class="mobImg mobLeader" src="{{mob.leaderImgUrl}}" (click)="showMobImage(mob)" />
|
||||
<img class="mobImg mobMinion" src="{{mob.minionImgUrl}}" (click)="showMobImage(mob)" />
|
||||
|
||||
</div>
|
||||
@ -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;
|
||||
}
|
||||
@ -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<MobStandInfoComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MobStandInfoComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MobStandInfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -28,21 +28,16 @@
|
||||
<div class="col col-sm-6 col-md-6 col-lg-4 mb-4" *ngFor="let mob of this.mobs">
|
||||
<div class="row no-gutters">
|
||||
<div class="col-12 col-md-8">
|
||||
|
||||
<img class="mobImg g-width-95x" src="{{mob.imageUrl}}" (click)="showMobImage(mob)" />
|
||||
<md2-mob-stand-info [mob]="mob"></md2-mob-stand-info>
|
||||
</div>
|
||||
<div class=" col-12 col-md-4">
|
||||
|
||||
|
||||
<md2-mob-detail-info name="mobDetail{{mob.name}}" [mob]="mob" [mode]="MobDlgType.PreView">
|
||||
<md2-mob-detail-info name="mobDetail{{mob.name}}" [mob]="mob" [mode]="MobDlgType.Dashboard">
|
||||
</md2-mob-detail-info>
|
||||
|
||||
<adj-number-input name="mob{{mob.name}}" [(ngModel)]="mob.unitRemainHp"
|
||||
[maximum]="isRoamingMonster?null: mob.hp" minimum="1" title="Target Unit HP"
|
||||
(hitMinimum)="killOneUnit(mob)" (hitMaximum)="addOneUnit(mob)">
|
||||
</adj-number-input>
|
||||
<button nbButton hero status="danger" size="small" (click)="attackMob(mob)"
|
||||
*ngIf="md2Service.showAttackBtn">Attack It</button>
|
||||
*ngIf="md2Service.info.showAttackBtn">Attack It</button>
|
||||
<!-- <div class='form-group'>
|
||||
<label for='playerAmount' class='label'>Current Unit HP</label>
|
||||
<div>
|
||||
|
||||
@ -18,83 +18,6 @@ $mob-card-height: 556px;
|
||||
/* right: auto; */
|
||||
z-index: 99;
|
||||
}
|
||||
.mobImg {
|
||||
object-fit: cover;
|
||||
&.Gargoyles-lv1-2 {
|
||||
object-position: 0 0;
|
||||
}
|
||||
&.Demons-lv1-2 {
|
||||
object-position: calc(#{$mob-card-width} * -1) 0;
|
||||
}
|
||||
&.Undead-lv1-2 {
|
||||
object-position: calc(#{$mob-card-width} * -2) 0;
|
||||
}
|
||||
&.FireEntities-lv1-2 {
|
||||
object-position: calc(#{$mob-card-width} * -3) 0;
|
||||
}
|
||||
&.FallenAngels-lv1-2 {
|
||||
object-position: calc(#{$mob-card-width} * -4) 0;
|
||||
}
|
||||
&.InfernalImps-lv1-2 {
|
||||
object-position: calc(#{$mob-card-width} * -5) 0;
|
||||
}
|
||||
&.Skeletons-lv1-2 {
|
||||
object-position: calc(#{$mob-card-width} * -6) 0;
|
||||
}
|
||||
&.Satyrs-lv1-2 {
|
||||
object-position: calc(#{$mob-card-width} * -7) 0;
|
||||
}
|
||||
|
||||
&.Gargoyles-lv3-4 {
|
||||
object-position: 0 calc(#{$mob-card-height} * -1);
|
||||
}
|
||||
&.FireEntities-lv3-4 {
|
||||
object-position: calc(#{$mob-card-width} * -1) calc(#{$mob-card-height} * -1);
|
||||
}
|
||||
&.Skeletons-lv3-4 {
|
||||
object-position: calc(#{$mob-card-width} * -2) calc(#{$mob-card-height} * -1);
|
||||
}
|
||||
&.InfernalImps-lv3-4 {
|
||||
object-position: calc(#{$mob-card-width} * -3) calc(#{$mob-card-height} * -1);
|
||||
}
|
||||
&.Undead-lv3-4 {
|
||||
object-position: calc(#{$mob-card-width} * -4) calc(#{$mob-card-height} * -1);
|
||||
}
|
||||
&.Demons-lv3-4 {
|
||||
object-position: calc(#{$mob-card-width} * -5) calc(#{$mob-card-height} * -1);
|
||||
}
|
||||
&.Satyrs-lv3-4 {
|
||||
object-position: calc(#{$mob-card-width} * -6) calc(#{$mob-card-height} * -1);
|
||||
}
|
||||
&.FallenAngels-lv3-4 {
|
||||
object-position: calc(#{$mob-card-width} * -7) calc(#{$mob-card-height} * -1);
|
||||
}
|
||||
|
||||
&.Satyrs-lv5 {
|
||||
object-position: 0 calc(#{$mob-card-height} * -2);
|
||||
}
|
||||
&.FallenAngels-lv5 {
|
||||
object-position: calc(#{$mob-card-width} * -1) calc(#{$mob-card-height} * -2);
|
||||
}
|
||||
&.Gargoyles-lv5 {
|
||||
object-position: calc(#{$mob-card-width} * -2) calc(#{$mob-card-height} * -2);
|
||||
}
|
||||
&.FireEntities-lv5 {
|
||||
object-position: calc(#{$mob-card-width} * -3) calc(#{$mob-card-height} * -2);
|
||||
}
|
||||
&.InfernalImps-lv5 {
|
||||
object-position: calc(#{$mob-card-width} * -4) calc(#{$mob-card-height} * -2);
|
||||
}
|
||||
&.Undead-lv5 {
|
||||
object-position: calc(#{$mob-card-width} * -5) calc(#{$mob-card-height} * -2);
|
||||
}
|
||||
&.Skeletons-lv5 {
|
||||
object-position: calc(#{$mob-card-width} * -6) calc(#{$mob-card-height} * -2);
|
||||
}
|
||||
&.Demons-lv5 {
|
||||
object-position: calc(#{$mob-card-width} * -7) calc(#{$mob-card-height} * -2);
|
||||
}
|
||||
}
|
||||
}
|
||||
#clip {
|
||||
position: absolute;
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NbDialogService } from '@nebular/theme';
|
||||
import { stringify } from 'querystring';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { FileService } from '../../../services/file.service';
|
||||
import { MD2Service } from '../../../services/md2.service';
|
||||
import { MD2MobService } from '../../../services/MD2/md2-mob.service';
|
||||
import { MD2Service } from '../../../services/MD2/md2.service';
|
||||
import { MsgBoxService } from '../../../services/msg-box.service';
|
||||
import { StateService } from '../../../services/state.service';
|
||||
import { ADIcon } from '../../../ui/alert-dlg/alert-dlg.model';
|
||||
import { NumberUtils } from '../../../utilities/number-utils';
|
||||
import { StringUtils } from '../../../utilities/string-utils';
|
||||
import { DrawingBag, DrawingItem, MD2Icon, MobDlgType, MobInfo, TreasureType } from '../massive-darkness2.model';
|
||||
import { MD2Base, MD2ComponentBase } from '../MD2Base';
|
||||
import { SpawnMobDlgComponent } from './spawn-mob-dlg/spawn-mob-dlg.component';
|
||||
@ -34,6 +37,7 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
|
||||
private fileService: FileService,
|
||||
private msgBoxService: MsgBoxService,
|
||||
private dlgService: NbDialogService,
|
||||
private mobService: MD2MobService,
|
||||
public md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
@ -44,6 +48,7 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initMobDecks();
|
||||
super.ngOnInit();
|
||||
}
|
||||
|
||||
|
||||
@ -61,7 +66,6 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
|
||||
|
||||
|
||||
initMobDecks() {
|
||||
this.md2Service.initMobDecks();
|
||||
this.mobs = [];
|
||||
this.cdRef.detectChanges();
|
||||
let spawn$ = this.isRoamingMonster ? this.md2Service.darknessPhaseRule.spawnRoamingMonster : this.md2Service.darknessPhaseRule.spawnMob;
|
||||
@ -71,25 +75,20 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
|
||||
});
|
||||
}
|
||||
spawnMob() {
|
||||
let result = this.md2Service.drawMob(this.isRoamingMonster);
|
||||
if (result.exitingMob) {
|
||||
this.dlgService.open(SpawnMobDlgComponent, { context: { title: `${result.mob.description} Activate One Action Now!`, mode: MobDlgType.Activating, mob: result.mob } })
|
||||
.onClose.pipe(first()).subscribe(result => {
|
||||
this.afterSpawn()
|
||||
});
|
||||
} else {
|
||||
this.md2Service.refreshTreasureBagSubject.next(this.md2Service.treasureBag);
|
||||
//this.msgBoxService.show(`${newSpawnMob.description} Shows Up`, { text: actionText });
|
||||
this.dlgService.open(SpawnMobDlgComponent, { context: { title: `${result.mob.description} Shows Up`, mode: MobDlgType.Spawn, mob: result.mob } })
|
||||
.onClose.pipe(first()).subscribe(result => {
|
||||
this.afterSpawn();
|
||||
});
|
||||
}
|
||||
let result = this.md2Service.spawnMob(this.isRoamingMonster);
|
||||
let titleText = result.exitingMob == null ? `${result.mob.description} Shows Up` : `${result.mob.description} Activate One Action Now!`;
|
||||
let actType = result.exitingMob == null ? MobDlgType.Spawn : MobDlgType.Activating;
|
||||
let mob = result.exitingMob == null ? result.mob : result.exitingMob;
|
||||
|
||||
this.dlgService.open(SpawnMobDlgComponent, { context: { title: titleText, mode: actType, mob: mob } })
|
||||
.onClose.pipe(first()).subscribe(result => {
|
||||
this.afterSpawn();
|
||||
});
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
afterSpawn() {
|
||||
this.cdRef.detectChanges();
|
||||
this.md2Service.broadcastMobsInfo();
|
||||
this.md2Service.broadcastService.broadcastMobsInfo();
|
||||
if (this.showRoundMessage) {
|
||||
this.msgBoxService.show(`${NumberUtils.Ordinal(this.md2Service.info.round)} Hero Phase`, { icon: ADIcon.INFO });
|
||||
}
|
||||
@ -109,14 +108,21 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
killOneUnit(mob: MobInfo) {
|
||||
let attacker = this.md2Service.currentActivateHero;
|
||||
if (mob.mobAmount > 1) {
|
||||
mob.mobAmount--;
|
||||
mob.unitRemainHp = mob.hp;
|
||||
if (this.attacking) {
|
||||
this.attackingAttackerExp += 1;
|
||||
} else {
|
||||
this.msgBoxService.show('The Attacker Gain 1 Exp', { icon: ADIcon.INFO }).pipe(first()).subscribe(result => {
|
||||
});
|
||||
|
||||
if (attacker) {
|
||||
attacker.exp += 1;
|
||||
this.msgBoxService.show(`${attacker.heroFullName} Gain 1 Exp`, { icon: ADIcon.INFO }).pipe(first()).subscribe(result => {
|
||||
this.md2Service.broadcastService.broadcastHeroInfoToOwner(attacker);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -128,7 +134,11 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
|
||||
this.attackingAttackerExp += 1;
|
||||
this.attackingAttackerReward = `<br> and gains ${mob.carriedTreasureHtml}`;
|
||||
} else {
|
||||
this.msgBoxService.show(`All Hero Gain ${mob.leaderExp} Exp`, { text: `The Attacker Gain 1 Extra Exp.<br> and gains ${mob.carriedTreasureHtml}`, icon: ADIcon.INFO }).pipe(first()).subscribe(result => {
|
||||
let messageText = '';
|
||||
if (attacker) {
|
||||
messageText = `<b>${attacker.heroFullName}</b> Gain 1 Extra Exp.<br> and gains ${mob.carriedTreasureHtml}`;
|
||||
}
|
||||
this.msgBoxService.show(`All Hero Gain ${mob.leaderExp} Exp`, { text: messageText, icon: ADIcon.INFO }).pipe(first()).subscribe(result => {
|
||||
|
||||
});
|
||||
}
|
||||
@ -154,49 +164,7 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
|
||||
.onClose.pipe(first()).subscribe(mobResult => {
|
||||
if (mobResult) {
|
||||
let attackDamage = mobResult.uiWounds;
|
||||
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);
|
||||
|
||||
this.attacking = false;
|
||||
|
||||
let attacker = this.md2Service.info.currentActivateHero;
|
||||
let attackerTitle = 'The Attacker';
|
||||
if (attacker) {
|
||||
attackerTitle = this.md2Service.heroFullName(attacker);
|
||||
}
|
||||
if (this.attackingAllExp > 0) {
|
||||
this.msgBoxService.show(`All Hero Gains ${this.attackingAllExp} Exp`,
|
||||
{ text: `${attackerTitle} Gains ${this.attackingAttackerExp} Extra Exp.<br> and gains ${mob.carriedTreasureHtml}`, 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;
|
||||
}
|
||||
attacker.exp += this.attackingAttackerExp;
|
||||
this.md2Service.broadcastAllHeroInfoToAll();
|
||||
});
|
||||
} else if (this.attackingAttackerExp > 0) {
|
||||
|
||||
this.msgBoxService.show(`${attackerTitle} Gains ${this.attackingAttackerExp} Exp`, { icon: ADIcon.INFO }).pipe(first()).subscribe(result => {
|
||||
attacker.exp += this.attackingAttackerExp;
|
||||
this.md2Service.broadcastAllHeroInfoToAll();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
this.cdRef.detectChanges();
|
||||
this.md2Service.broadcastMobsInfo();
|
||||
this.mobService.attackMob(mobResult, attackDamage);
|
||||
}
|
||||
|
||||
});
|
||||
@ -209,12 +177,21 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public getMobImageUrl(mob: MobInfo): string {
|
||||
if (StringUtils.isNullOrWhitespace(mob.leaderImgUrl)) {
|
||||
return mob.imageUrl;
|
||||
} else {
|
||||
return mob.leaderImgUrl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
showMobImage(mob: MobInfo) {
|
||||
this.dlgService.open(SpawnMobDlgComponent, { context: { title: `${mob.description}`, mode: MobDlgType.PreView, mob: mob } })
|
||||
.onClose.pipe(first()).subscribe(result => {
|
||||
|
||||
});
|
||||
//this.msgBoxService.show('', { text: mob.mobInfoHtml.replace('g-height-50vh', 'g-height-70vh') });
|
||||
}
|
||||
weaponHtml(mob: MobInfo) {
|
||||
let html = '<br>';
|
||||
|
||||
@ -1,18 +1,26 @@
|
||||
<nb-card status="{{headerStatus}}" class="mobCard" size="giant">
|
||||
<nb-card-header>
|
||||
<img src="{{imgUrl('Mobs/MobToken.png')}}" width="40px"> {{(mob.isRoamingMonster?'Roaming Monster':'Mob')}}
|
||||
<img src="{{imgUrl('Mobs/MobToken.png')}}" width="40px"> {{cardTitle}}
|
||||
<span [innerHtml]="titleHtml"></span>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="row no-gutters">
|
||||
<div class="col-md-7">
|
||||
<img src="{{mob.imageUrl}}" class="g-width-90x">
|
||||
<div class="col-md-7 g-height-90vh">
|
||||
<!-- <img src="{{mob.imageUrl}}" class="g-width-90x"> -->
|
||||
<md2-mob-stand-info [mob]="mob" [mode]="mode"></md2-mob-stand-info>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
|
||||
<md2-mob-detail-info [mob]="mob" [mode]="mode">
|
||||
</md2-mob-detail-info>
|
||||
<ng-container *ngIf="mode==MobDlgType.Spawn&&!mob.isRoamingMonster">
|
||||
<div *ngIf="actionInfoHtml">
|
||||
|
||||
<div class="alert alert-warning" role="alert" [innerHtml]="actionInfoHtml">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="mode==MobDlgType.Spawn&&mob.type==MobType.Mob">
|
||||
<div class="row form-group mt-2">
|
||||
<div class="col-md-2">
|
||||
<span class="MD2Icon melee g-font-size-40"></span>
|
||||
@ -71,7 +79,7 @@
|
||||
<label for='damages' class='label'>Cause How Many Damages?</label>
|
||||
<input type='number' nbInput fullWidth id='damages' name='damages' [(ngModel)]='mob.uiWounds'>
|
||||
</div>
|
||||
<div class='form-group'>
|
||||
<!-- <div class='form-group'>
|
||||
<label for='damages' class='label'>Cause How Many <span
|
||||
[innerHtml]="iconHtml(MD2Icon.Fire,'g-color-google-plus mr-1 g-font-size-18')"></span>?</label>
|
||||
<input type='number' nbInput fullWidth id='uiFireTokens' name='uiFireTokens'
|
||||
@ -82,7 +90,7 @@
|
||||
[innerHtml]="iconHtml(MD2Icon.Frost,'g-color-aqua mr-1 g-font-size-18')"></span>?</label>
|
||||
<input type='number' nbInput fullWidth id='uiFrozenTokens' name='uiFrozenTokens'
|
||||
[(ngModel)]='mob.uiFrozenTokens'>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
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.service';
|
||||
import { MD2Service } from '../../../../services/MD2/md2.service';
|
||||
import { MsgBoxService } from '../../../../services/msg-box.service';
|
||||
import { StateService } from '../../../../services/state.service';
|
||||
import { StringUtils } from '../../../../utilities/string-utils';
|
||||
import { AttackInfo, AttackTarget, AttackType, MD2HeroInfo, MD2Icon, MobDlgType, MobInfo } from '../../massive-darkness2.model';
|
||||
import { AttackInfo, AttackTarget, AttackType, MD2HeroInfo, MD2Icon, MobDlgType, MobInfo, MobType } from '../../massive-darkness2.model';
|
||||
import { MD2ComponentBase } from '../../MD2Base';
|
||||
|
||||
@Component({
|
||||
@ -16,13 +17,14 @@ import { MD2ComponentBase } from '../../MD2Base';
|
||||
})
|
||||
export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit {
|
||||
MobDlgType = MobDlgType;
|
||||
MobType = MobType;
|
||||
mode: MobDlgType;
|
||||
|
||||
cardTitle: string;
|
||||
title: string;
|
||||
titleHtml: string;
|
||||
MD2Icon = MD2Icon;
|
||||
mob: MobInfo;
|
||||
|
||||
actionInfoHtml: string;
|
||||
beenAttackedHero = [] as MD2HeroInfo[];
|
||||
attackTarget: string;
|
||||
otherAttackTarget: string;
|
||||
@ -38,28 +40,15 @@ export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit {
|
||||
}
|
||||
ngOnInit(): void {
|
||||
//this.mob = new MobInfo(this.mob);
|
||||
if (this.mode == MobDlgType.Spawn) {
|
||||
this.mob.attackInfos = [
|
||||
{
|
||||
type: MD2Icon.Melee,
|
||||
red: 0,
|
||||
yellow: 0,
|
||||
orange: 0
|
||||
} as AttackInfo,
|
||||
{
|
||||
type: MD2Icon.Range,
|
||||
red: 0,
|
||||
yellow: 0,
|
||||
orange: 0
|
||||
} as AttackInfo,
|
||||
{
|
||||
type: MD2Icon.Magic,
|
||||
red: 0,
|
||||
yellow: 0,
|
||||
orange: 0
|
||||
} as AttackInfo
|
||||
|
||||
]
|
||||
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;
|
||||
@ -71,28 +60,29 @@ 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() {
|
||||
this.dlgRef.close();
|
||||
}
|
||||
initTitleHtml() {
|
||||
|
||||
this.cardTitle = MobType[this.mob.type];
|
||||
|
||||
let htmlText = '';
|
||||
if (this.mode == MobDlgType.Spawn) {
|
||||
htmlText = `${this.mob.description} Shows Up`;
|
||||
@ -113,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 <b>Lowest HP</b> hero.';
|
||||
|
||||
@ -3,7 +3,7 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { Subject } from 'rxjs-compat';
|
||||
import { first, takeUntil } from 'rxjs/operators';
|
||||
import { FileService } from '../../../services/file.service';
|
||||
import { MD2Service } from '../../../services/md2.service';
|
||||
import { MD2Service } from '../../../services/MD2/md2.service';
|
||||
import { MsgBoxService } from '../../../services/msg-box.service';
|
||||
import { StateService } from '../../../services/state.service';
|
||||
import { ADButtons, ADIcon } from '../../../ui/alert-dlg/alert-dlg.model';
|
||||
@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
16
src/app/services/MD2/md2-broadcast.service.spec.ts
Normal file
16
src/app/services/MD2/md2-broadcast.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MD2BroadcastService } from './md2-broadcast.service';
|
||||
|
||||
describe('MD2BroadcastService', () => {
|
||||
let service: MD2BroadcastService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(MD2BroadcastService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
109
src/app/services/MD2/md2-broadcast.service.ts
Normal file
109
src/app/services/MD2/md2-broadcast.service.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { NbDialogService } from '@nebular/theme';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { MD2HeroInfo } from '../../games/massive-darkness2/massive-darkness2.model';
|
||||
import { FileService } from '../file.service';
|
||||
import { GameRoomService } from '../game-room.service';
|
||||
import { LoginUserService } from '../login-user.service';
|
||||
import { SignalRService, SignalRSession, SignalRMessage } from '../signal-r.service';
|
||||
import { MD2StateService } from './md2-state.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MD2BroadcastService {
|
||||
|
||||
constructor(
|
||||
public fileService: FileService,
|
||||
private gameRoomService: GameRoomService,
|
||||
private loginUserService: LoginUserService,
|
||||
public stateService: MD2StateService,
|
||||
public signalRService: SignalRService,
|
||||
public dlgService: NbDialogService
|
||||
) { }
|
||||
|
||||
public get playerHero(): MD2HeroInfo {
|
||||
return this.stateService.playerHero;
|
||||
}
|
||||
public broadcastAllHeroInfoToAll() {
|
||||
this.stateService.info.heros.forEach(element => {
|
||||
this.broadcastHeroInfoToOwner(element);
|
||||
});
|
||||
}
|
||||
|
||||
public broadcastHeroAction(action: string, extraValue: any = null) {
|
||||
let parameters = {};
|
||||
parameters['tabId'] = this.loginUserService.sessionTabId;
|
||||
parameters['extraValue'] = JSON.stringify(extraValue);
|
||||
this.broadcastMessage('heroAction', action, parameters);
|
||||
}
|
||||
|
||||
public broadcastHeroInfoToAll(hero: MD2HeroInfo) {
|
||||
let message = {
|
||||
receiver: { isGroup: true, sessionId: this.gameRoomService.gameRoomId } as SignalRSession,
|
||||
from: { isGroup: false, sessionId: hero.playerInfo.signalRClientId },
|
||||
actionType: 'hero',
|
||||
actionName: 'update',
|
||||
} as SignalRMessage;
|
||||
message.parameters = { hero: JSON.stringify(hero) };
|
||||
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
|
||||
});
|
||||
}
|
||||
|
||||
public broadcastHeroInfoToOwner(hero: MD2HeroInfo) {
|
||||
let message = {
|
||||
receiver: { isGroup: false, sessionId: hero.playerInfo.signalRClientId } as SignalRSession,
|
||||
from: { isGroup: true, sessionId: this.gameRoomService.gameRoomId },
|
||||
actionType: 'hero',
|
||||
actionName: 'updateMyHero',
|
||||
} as SignalRMessage;
|
||||
message.parameters = { hero: JSON.stringify(hero) };
|
||||
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* `sessionId` = null means broadcast to all
|
||||
*/
|
||||
public broadcastMessage(actionType: string,
|
||||
actionName: string,
|
||||
parameters: { [key: string]: string; } = {},
|
||||
sessionId: string = null) {
|
||||
let message = {
|
||||
receiver: { isGroup: !sessionId, sessionId: sessionId } as SignalRSession,
|
||||
from: { isGroup: false, sessionId: this.loginUserService.userAccess.signalRSessionId },
|
||||
actionType: actionType,
|
||||
actionName: actionName,
|
||||
parameters: parameters
|
||||
} as SignalRMessage;
|
||||
|
||||
if (sessionId == null) {
|
||||
message.receiver = { isGroup: true, sessionId: this.gameRoomService.gameRoomId } as SignalRSession
|
||||
} else {
|
||||
message.receiver = { isGroup: false, sessionId: this.gameRoomService.gameRoomId } as SignalRSession
|
||||
}
|
||||
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
|
||||
});
|
||||
}
|
||||
public broadcastGameInfo() {
|
||||
let parameters = {};
|
||||
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);
|
||||
parameters['mobs'] = JSON.stringify(this.stateService.info.mobs);
|
||||
this.broadcastMessage('mobs', 'update', parameters);
|
||||
}
|
||||
|
||||
public broadcastMyHeroInfo() {
|
||||
this.broadcastHeroInfoToAll(this.playerHero);
|
||||
}
|
||||
}
|
||||
51
src/app/services/MD2/md2-init.service.ts
Normal file
51
src/app/services/MD2/md2-init.service.ts
Normal file
@ -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));
|
||||
}
|
||||
}
|
||||
16
src/app/services/MD2/md2-mob.service.spec.ts
Normal file
16
src/app/services/MD2/md2-mob.service.spec.ts
Normal file
@ -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();
|
||||
});
|
||||
});
|
||||
140
src/app/services/MD2/md2-mob.service.ts
Normal file
140
src/app/services/MD2/md2-mob.service.ts
Normal file
@ -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 = `<br> 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 = `<b>${attackerTitle}</b> Gains ${this.attackingAttackerExp} Extra Exp.<br> 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(`<b>${attackerTitle}</b> 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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
16
src/app/services/MD2/md2-state.service.spec.ts
Normal file
16
src/app/services/MD2/md2-state.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MD2StateService } from './md2-state.service';
|
||||
|
||||
describe('MD2StateService', () => {
|
||||
let service: MD2StateService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(MD2StateService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
42
src/app/services/MD2/md2-state.service.ts
Normal file
42
src/app/services/MD2/md2-state.service.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { DrawingBag, MD2HeroInfo, MD2Icon, MobInfo, TreasureItem, TreasureType } from '../../games/massive-darkness2/massive-darkness2.model';
|
||||
import { FileService } from '../file.service';
|
||||
import { MD2GameInfo } from './md2.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MD2StateService {
|
||||
|
||||
private _highestPlayerLevel: number = 1;
|
||||
private _playerAmount: number = 2;
|
||||
|
||||
public info: MD2GameInfo;
|
||||
public playerHero: MD2HeroInfo;
|
||||
public mobDeck: DrawingBag<MobInfo>;
|
||||
public roamingMobDeck: DrawingBag<MobInfo>;
|
||||
public treasureBag: DrawingBag<TreasureItem> = new DrawingBag<TreasureItem>();
|
||||
|
||||
constructor(
|
||||
public fileService: FileService,
|
||||
) { }
|
||||
|
||||
public iconHtml(icon: MD2Icon, cssClass = '') {
|
||||
if (icon < MD2Icon.RedDice) {
|
||||
return `<span class='MD2Icon ${cssClass}'>${String.fromCharCode(65 + icon)}</span>`
|
||||
} else {
|
||||
return `<span class='MD2Icon dice ${MD2Icon[icon].replace('Dice', '')} ${cssClass}'></span>`;
|
||||
}
|
||||
}
|
||||
|
||||
public imgHtml(imgPath: string, cssClass = 'g-height-25 mr-1') {
|
||||
return `<img src='${this.imgUrl(imgPath)}' class='${cssClass}'>`;
|
||||
}
|
||||
|
||||
public imgUrl(imgPath: string) {
|
||||
return this.fileService.ImageUrl('MD2/' + imgPath);
|
||||
}
|
||||
public treasureImage(type: TreasureType) {
|
||||
return this.imgUrl(`TreasureToken/${TreasureType[type]}.png`);
|
||||
}
|
||||
}
|
||||
393
src/app/services/MD2/md2.service.ts
Normal file
393
src/app/services/MD2/md2.service.ts
Normal file
@ -0,0 +1,393 @@
|
||||
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<MobInfo>();
|
||||
public initialized = false;
|
||||
public mobBeenKilledSubject = new Subject<MobInfo>();
|
||||
|
||||
public refreshUI$: Subject<void> = new Subject<void>();
|
||||
public refreshTreasureBagSubject = new Subject<DrawingBag<TreasureItem>>();
|
||||
|
||||
public heroAttackingSubject = new Subject<MD2HeroInfo>();
|
||||
|
||||
|
||||
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<MobInfo>;
|
||||
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<MessageBoxConfig>) {
|
||||
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<MD2GameInfo> = 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;
|
||||
}
|
||||
15
src/app/services/MD2/md2.spawn.mob.service.ts
Normal file
15
src/app/services/MD2/md2.spawn.mob.service.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { MD2StateService } from "./md2-state.service";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MD2SpawnMobService {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor(
|
||||
public stateService: MD2StateService,) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,589 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AttackInfo, AttackTarget, CoreGameDarknessPhaseRule, DefenseInfo, DrawingBag, DrawingItem, HeroClass, IDarknessPhaseRule, MD2HeroInfo, MD2Icon, MD2Rules, MobInfo, RoundPhase, TreasureType } from '../games/massive-darkness2/massive-darkness2.model';
|
||||
import { first, map, reduce } from "rxjs/operators";
|
||||
import { Subject } from 'rxjs';
|
||||
import { FileService } from './file.service';
|
||||
import { StringUtils } from '../utilities/string-utils';
|
||||
import { MsgBoxService } from './msg-box.service';
|
||||
import { ADButtons, ADIcon, MessageBoxConfig } from '../ui/alert-dlg/alert-dlg.model';
|
||||
import { GameRoomService } from './game-room.service';
|
||||
import { SignalRMessage, SignalRService, SignalRSession } from './signal-r.service';
|
||||
import { LoginUserService } from './login-user.service';
|
||||
import { NumberUtils } from '../utilities/number-utils';
|
||||
import { SpawnMobDlgComponent } from '../games/massive-darkness2/mobs/spawn-mob-dlg/spawn-mob-dlg.component';
|
||||
import { stringify } from 'querystring';
|
||||
import { NbDialogService } from '@nebular/theme';
|
||||
import { IBossFight } from '../games/massive-darkness2/massive-darkness2.model.boss';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MD2Service {
|
||||
// #region Properties (24)
|
||||
|
||||
private _highestPlayerLevel: number = 1;
|
||||
private _playerAmount: number = 2;
|
||||
|
||||
public info: MD2GameInfo;
|
||||
|
||||
public darknessPhaseRule: IDarknessPhaseRule;
|
||||
public enemyPhaseMobs: MobInfo[];
|
||||
public enemyPhaseSubject = new Subject<MobInfo>();
|
||||
public initialized = false;
|
||||
public mobBeenKilledSubject = new Subject<MobInfo>();
|
||||
public mobDeck: DrawingBag<MobInfo>;
|
||||
public roamingMobDeck: DrawingBag<MobInfo>;
|
||||
public treasureBag: DrawingBag<DrawingItem> = new DrawingBag<DrawingItem>();
|
||||
|
||||
public refreshUI$: Subject<void> = new Subject<void>();
|
||||
public refreshTreasureBagSubject = new Subject<DrawingBag<DrawingItem>>();
|
||||
|
||||
|
||||
public get heros() {
|
||||
return this.info.heros;
|
||||
}
|
||||
public get roamingMonsters() {
|
||||
return this.info.roamingMonsters;
|
||||
}
|
||||
public get mobs() {
|
||||
return this.info.mobs;
|
||||
}
|
||||
|
||||
// #endregion Properties (24)
|
||||
|
||||
// #region Constructors (1)
|
||||
|
||||
constructor(
|
||||
public fileService: FileService,
|
||||
public msgBoxService: MsgBoxService,
|
||||
private gameRoomService: GameRoomService,
|
||||
private loginUserService: LoginUserService,
|
||||
public signalRService: SignalRService,
|
||||
public dlgService: NbDialogService
|
||||
) {
|
||||
this.darknessPhaseRule = new CoreGameDarknessPhaseRule();
|
||||
this.info = new MD2GameInfo();
|
||||
this.darknessPhaseRule.addTreasureToken.subscribe(treasureType => {
|
||||
this.addTreasure(treasureType, 1);
|
||||
});
|
||||
}
|
||||
|
||||
// #endregion Constructors (1)
|
||||
private initCoreGameRoamingMonsters() {
|
||||
let mobs = [];
|
||||
this.roamingMobDeck.AddItem(new MobInfo({ name: 'Andra', hp: 5, level: 1, rewardTokens: 2, isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Rare)] }));
|
||||
this.roamingMobDeck.AddItem(new MobInfo({ name: 'Andra', hp: 7, level: 3, rewardTokens: 2, isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic)] }));
|
||||
this.roamingMobDeck.AddItem(new MobInfo({ name: 'Andra', hp: 5, level: 5, rewardTokens: 0, isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic)] }));
|
||||
|
||||
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)],
|
||||
defenseInfos: new DefenseInfo(1, 1),
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(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)],
|
||||
defenseInfos: new DefenseInfo(2, 1),
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(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)],
|
||||
defenseInfos: new DefenseInfo(4, 1),
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic)]
|
||||
}));
|
||||
|
||||
this.roamingMobDeck.AddItem(new MobInfo({
|
||||
name: 'Lyidan, Incubus Lord', hp: 7, level: 1, rewardTokens: 2,
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Rare)]
|
||||
}));
|
||||
this.roamingMobDeck.AddItem(new MobInfo({
|
||||
name: 'Lyidan, Incubus Lord', hp: 10, level: 3, rewardTokens: 2,
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic)]
|
||||
}));
|
||||
this.roamingMobDeck.AddItem(new MobInfo({
|
||||
name: 'Lyidan, Incubus Lord', hp: 7, level: 5, rewardTokens: 0,
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic)]
|
||||
}));
|
||||
|
||||
this.roamingMobDeck.AddItem(new MobInfo({
|
||||
name: 'The Ghoul', hp: 5, level: 1, rewardTokens: 2,
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Rare)]
|
||||
}));
|
||||
this.roamingMobDeck.AddItem(new MobInfo({
|
||||
name: 'The Ghoul', hp: 8, level: 3, rewardTokens: 2,
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic)]
|
||||
}));
|
||||
this.roamingMobDeck.AddItem(new MobInfo({
|
||||
name: 'The Ghoul', hp: 5, level: 5, rewardTokens: 0,
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic)]
|
||||
}));
|
||||
|
||||
|
||||
}
|
||||
// #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 this._highestPlayerLevel;
|
||||
}
|
||||
}
|
||||
|
||||
public set highestPlayerLevel(v: number) {
|
||||
this._highestPlayerLevel = v;
|
||||
}
|
||||
|
||||
public get playerAmount(): number {
|
||||
if (this.heros.length > 0) {
|
||||
return this.heros.length;
|
||||
} else {
|
||||
return this._playerAmount;
|
||||
}
|
||||
}
|
||||
|
||||
public set playerAmount(v: number) {
|
||||
this._playerAmount = v;
|
||||
}
|
||||
|
||||
public get playerHero(): MD2HeroInfo {
|
||||
return this.heros.find(h => h.playerInfo.signalRClientId == this.loginUserService.userAccess.signalRSessionId);
|
||||
}
|
||||
|
||||
// #endregion Public Getters And Setters (5)
|
||||
|
||||
// #region Public Methods (27)
|
||||
|
||||
public addTreasure(type: TreasureType, amount: number = 1) {
|
||||
let item = this.getTreasureDrawingItem(type, amount);
|
||||
this.treasureBag.AddItem(item);
|
||||
this.refreshTreasureBagSubject.next(this.treasureBag);
|
||||
}
|
||||
|
||||
public broadcastAllHeroInfoToAll() {
|
||||
this.heros.forEach(element => {
|
||||
this.broadcastHeroInfoToAll(element);
|
||||
});
|
||||
}
|
||||
|
||||
public broadcastHeroAction(action: string, extraValue: any = null) {
|
||||
let parameters = {};
|
||||
parameters['tabId'] = this.loginUserService.sessionTabId;
|
||||
parameters['extraValue'] = JSON.stringify(extraValue);
|
||||
this.broadcastMessage('heroAction', action, parameters);
|
||||
}
|
||||
|
||||
public broadcastHeroInfoToAll(hero: MD2HeroInfo) {
|
||||
let message = {
|
||||
receiver: { isGroup: true, sessionId: this.gameRoomService.gameRoomId } as SignalRSession,
|
||||
from: { isGroup: false, sessionId: hero.playerInfo.signalRClientId },
|
||||
actionType: 'hero',
|
||||
actionName: 'update',
|
||||
} as SignalRMessage;
|
||||
message.parameters = { hero: JSON.stringify(hero) };
|
||||
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
|
||||
});
|
||||
}
|
||||
|
||||
public broadcastHeroInfoToOwner(hero: MD2HeroInfo) {
|
||||
let message = {
|
||||
receiver: { isGroup: false, sessionId: hero.playerInfo.signalRClientId } as SignalRSession,
|
||||
from: { isGroup: true, sessionId: this.gameRoomService.gameRoomId },
|
||||
actionType: 'hero',
|
||||
actionName: 'update',
|
||||
} as SignalRMessage;
|
||||
message.parameters = { hero: JSON.stringify(hero) };
|
||||
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* `sessionId` = null means broadcast to all
|
||||
*/
|
||||
public broadcastMessage(actionType: string,
|
||||
actionName: string,
|
||||
parameters: { [key: string]: string; } = {},
|
||||
sessionId: string = null) {
|
||||
let message = {
|
||||
receiver: { isGroup: !sessionId, sessionId: sessionId } as SignalRSession,
|
||||
from: { isGroup: false, sessionId: this.loginUserService.userAccess.signalRSessionId },
|
||||
actionType: actionType,
|
||||
actionName: actionName,
|
||||
parameters: parameters
|
||||
} as SignalRMessage;
|
||||
|
||||
if (sessionId == null) {
|
||||
message.receiver = { isGroup: true, sessionId: this.gameRoomService.gameRoomId } as SignalRSession
|
||||
} else {
|
||||
message.receiver = { isGroup: false, sessionId: this.gameRoomService.gameRoomId } as SignalRSession
|
||||
}
|
||||
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
|
||||
});
|
||||
}
|
||||
public broadcastGameInfo() {
|
||||
let parameters = {};
|
||||
parameters['gameInfo'] = JSON.stringify(this.info);
|
||||
this.broadcastMessage('GameRoom', 'update', parameters);
|
||||
}
|
||||
public broadcastMobsInfo() {
|
||||
let parameters = {};
|
||||
parameters['roamingMonsters'] = JSON.stringify(this.roamingMonsters);
|
||||
parameters['mobs'] = JSON.stringify(this.mobs);
|
||||
this.broadcastMessage('mobs', 'update', parameters);
|
||||
}
|
||||
|
||||
public broadcastMyHeroInfo() {
|
||||
this.broadcastHeroInfoToAll(this.playerHero);
|
||||
}
|
||||
|
||||
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.broadcastHeroInfoToOwner(hero);
|
||||
});
|
||||
this.info.roundPhase = RoundPhase.HeroPhase;
|
||||
this.info.round++;
|
||||
if (this.darknessPhaseRule.runDarknessPhase()) {
|
||||
this.msgBoxService.show(`${NumberUtils.Ordinal(this.info.round)} Hero Phase`, { icon: ADIcon.INFO });
|
||||
}
|
||||
//this.runNextPhase();
|
||||
}
|
||||
|
||||
public drawMob(isRoamingMonster: boolean) {
|
||||
let mobDeck = null as DrawingBag<MobInfo>;
|
||||
let level = 1;
|
||||
if (this.highestPlayerLevel < 3) {
|
||||
} else if (this.highestPlayerLevel < 5) {
|
||||
level = 3;
|
||||
} else {
|
||||
level = 5;
|
||||
}
|
||||
if (isRoamingMonster) {
|
||||
mobDeck = this.roamingMobDeck;
|
||||
} else {
|
||||
mobDeck = this.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]);
|
||||
if (isRoamingMonster) {
|
||||
newSpawnMob.unitRemainHp = newSpawnMob.hp * this.playerAmount;
|
||||
newSpawnMob.mobAmount = 1;
|
||||
} 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);
|
||||
|
||||
if (exitingMob) {
|
||||
exitingMob.level = newSpawnMob.level;
|
||||
exitingMob.hp = newSpawnMob.hp;
|
||||
exitingMob.imageUrl = newSpawnMob.imageUrl;
|
||||
} else {
|
||||
this.mobs.push(newSpawnMob);
|
||||
newSpawnMob.carriedTreasure = this.treasureBag.DrawAndRemove(newSpawnMob.rewardTokens);
|
||||
}
|
||||
|
||||
|
||||
return { exitingMob, mob: newSpawnMob };
|
||||
}
|
||||
|
||||
public enemyPhase() {
|
||||
//this.msgBoxService
|
||||
let monsterBag = new DrawingBag<MobInfo>();
|
||||
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.showEnemyPhaseAction();
|
||||
this.enemyPhaseSubject.next(this.enemyPhaseMobs[0]);
|
||||
} else {
|
||||
this.runNextPhase();
|
||||
}
|
||||
}
|
||||
|
||||
public enterBossFight() {
|
||||
//this.sendMsgboxMsg
|
||||
}
|
||||
|
||||
public fileList(folderPath: string) {
|
||||
return this.fileService.FileList('Images/MD2/' + folderPath);
|
||||
}
|
||||
|
||||
public getTreasureDrawingItem(type: TreasureType, amount: number = 1) {
|
||||
return new DrawingItem(`${TreasureType[type]} Treasure`, `It's a ${TreasureType[type]} Treasure!`, this.treasureImage(type), amount);
|
||||
}
|
||||
|
||||
public heroFullName(hero: MD2HeroInfo) {
|
||||
if (!hero) return '';
|
||||
return `${hero.playerInfo.name} (${HeroClass[hero.class]} - ${hero.name})`
|
||||
}
|
||||
|
||||
public iconHtml(icon: MD2Icon, cssClass = '') {
|
||||
if (icon < MD2Icon.RedDice) {
|
||||
return `<span class='MD2Icon ${cssClass}'>${String.fromCharCode(65 + icon)}</span>`
|
||||
} else {
|
||||
return `<span class='MD2Icon dice ${MD2Icon[icon].replace('Dice', '')} ${cssClass}'></span>`;
|
||||
}
|
||||
}
|
||||
|
||||
public imgHtml(imgPath: string, cssClass = 'g-height-25 mr-1') {
|
||||
return `<img src='${this.imgUrl(imgPath)}' class='${cssClass}'>`;
|
||||
}
|
||||
|
||||
public imgUrl(imgPath: string) {
|
||||
return this.fileService.ImageUrl('MD2/' + imgPath);
|
||||
}
|
||||
|
||||
public initMobDecks() {
|
||||
this.mobDeck = new DrawingBag();
|
||||
this.roamingMobDeck = new DrawingBag();
|
||||
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Gargoyles', hp: 2, level: 1, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Demons', hp: 3, level: 1, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Undead', hp: 4, level: 1, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Fire Entities', hp: 3, level: 1, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Fallen Angels', hp: 2, level: 1, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Infernal Imps', hp: 3, level: 1, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Skeletons', hp: 2, level: 1, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Satyrs', hp: 3, level: 1, rewardTokens: 1 }));
|
||||
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Gargoyles', hp: 3, level: 3, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Demons', hp: 4, level: 3, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Undead', hp: 5, level: 3, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Fire Entities', hp: 4, level: 3, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Fallen Angels', hp: 3, level: 3, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Infernal Imps', hp: 4, level: 3, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Skeletons', hp: 3, level: 3, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Satyrs', hp: 4, level: 3, rewardTokens: 1 }));
|
||||
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Gargoyles', hp: 6, level: 5, rewardTokens: 2 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Demons', hp: 6, level: 5, rewardTokens: 2 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Undead', hp: 8, level: 5, rewardTokens: 2 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Fire Entities', hp: 7, level: 5, rewardTokens: 2 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Fallen Angels', hp: 5, level: 5, rewardTokens: 2 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Infernal Imps', hp: 5, level: 5, rewardTokens: 2 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Skeletons', hp: 5, level: 5, rewardTokens: 2 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Satyrs', hp: 6, level: 5, rewardTokens: 2 }));
|
||||
this.initCoreGameRoamingMonsters();
|
||||
}
|
||||
|
||||
public initTreasureBag() {
|
||||
this.treasureBag.ClearAllItems();
|
||||
this.addTreasure(TreasureType.Common, 15);
|
||||
this.addTreasure(TreasureType.Rare, 5);
|
||||
}
|
||||
|
||||
public levelUpPhase() {
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
this.runNextPhase();
|
||||
}
|
||||
|
||||
public mobImage(name: string, level: number, isRoamingMonsters: boolean) {
|
||||
name = StringUtils.replaceAll(name, ' ', '').replace(',', '');
|
||||
if (level < 3) {
|
||||
level = 1;
|
||||
} else if (level < 5) {
|
||||
level = 2;
|
||||
} else {
|
||||
level = 3;
|
||||
}
|
||||
if (isRoamingMonsters) {
|
||||
return this.imgUrl(`Mobs/CoreGame/RoamingMonsters/${name}/${level}.png`);
|
||||
} else {
|
||||
return this.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;
|
||||
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 rollBlackDice(times: number) {
|
||||
let wounds = 0;
|
||||
let claws = 0;
|
||||
//miss 33%
|
||||
//1 claw 33%
|
||||
//1 wound 17%
|
||||
//1 claw, 1 wound 17%
|
||||
|
||||
for (let i = 0; i < times; i++) {
|
||||
let result = Math.random() * 100;
|
||||
if (result <= 33) {
|
||||
} else if (result <= 67) {
|
||||
claws += 1;
|
||||
} else if (result <= 83) {
|
||||
wounds += 1;
|
||||
} else {
|
||||
claws += 1;
|
||||
wounds += 1;
|
||||
}
|
||||
}
|
||||
return { claws, wounds };
|
||||
}
|
||||
|
||||
public runNextPhase() {
|
||||
this.info.roundPhase++;
|
||||
switch (this.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.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.info.roundPhase;
|
||||
this.broadcastMessage('roundPhase', '', parameters);
|
||||
}
|
||||
|
||||
public sendMsgboxMsg(playerSessionId: string, msg: Partial<MessageBoxConfig>) {
|
||||
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 treasureImage(type: TreasureType) {
|
||||
return this.imgUrl(`TreasureToken/${TreasureType[type]}.png`);
|
||||
}
|
||||
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 <b>Lowest HP</b> 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 <b>Highest HP</b> 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 <b>Highest Mp</b> 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 <b>Lowest Level</b> 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;
|
||||
}
|
||||
|
||||
public getTargetHerosHtml(beenAttackedHero: MD2HeroInfo[]) {
|
||||
return `<b>${StringUtils.makeCommaSeparatedString(beenAttackedHero.map(h => this.heroFullName(h)), false, true)}</b>`;
|
||||
}
|
||||
// #endregion Public Methods (27)
|
||||
|
||||
}
|
||||
|
||||
export class MD2GameInfo {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor(
|
||||
config: Partial<MD2GameInfo> = 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.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[] = [];
|
||||
public heros: MD2HeroInfo[] = [];
|
||||
public round = 1;
|
||||
public roundPhase: RoundPhase = RoundPhase.HeroPhase;
|
||||
public showAttackBtn: boolean = false;
|
||||
public boss: IBossFight;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
<div class='form-group'>
|
||||
<div class='form-group adjustContainer'>
|
||||
<label for='playerAmount' class='label' *ngIf="title" [innerHtml]="titleHtml"></label>
|
||||
<div>
|
||||
<button nbButton outline type="button" status="primary" size="{{size}}" (click)="adjustNumber(false)">
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
.adjustContainer {
|
||||
white-space: nowrap;
|
||||
}
|
||||
@ -57,17 +57,21 @@ export class AdjacentNumberInputComponent implements ControlValueAccessor, OnIni
|
||||
@Output() hitMaximum = new EventEmitter<number>();
|
||||
@Output() hitIncreasing = new EventEmitter<number>();
|
||||
@Output() hitDecreasing = new EventEmitter<number>();
|
||||
@Output() hitChange = new EventEmitter<number>();
|
||||
|
||||
onChange = (value: number) => {
|
||||
|
||||
};
|
||||
onTouched = () => { };
|
||||
constructor(
|
||||
private cdRef: ChangeDetectorRef
|
||||
) { }
|
||||
writeValue(obj: number): void {
|
||||
this.currentNumber = obj;
|
||||
this.onChange(obj);
|
||||
if (this.currentNumber != obj) {
|
||||
|
||||
this.currentNumber = obj;
|
||||
this.onChange(obj);
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: number) => void): void {
|
||||
@ -99,6 +103,8 @@ export class AdjacentNumberInputComponent implements ControlValueAccessor, OnIni
|
||||
if (null == this.maximum || this.currentNumber < this.maximum) {
|
||||
this.currentNumber++;
|
||||
this.hitIncreasing.next(1);
|
||||
this.hitChange.next(1);
|
||||
|
||||
} else {
|
||||
this.hitMaximum.next(this.currentNumber);
|
||||
}
|
||||
@ -106,6 +112,7 @@ export class AdjacentNumberInputComponent implements ControlValueAccessor, OnIni
|
||||
if (null == this.minimum || this.currentNumber > this.minimum) {
|
||||
this.currentNumber--;
|
||||
this.hitDecreasing.next(1);
|
||||
this.hitChange.next(-1);
|
||||
} else {
|
||||
this.hitMinimum.next(this.currentNumber);
|
||||
}
|
||||
|
||||
@ -40,4 +40,22 @@ export class ArrayUtils {
|
||||
}
|
||||
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
|
||||
};
|
||||
public static Shuffle(array) {
|
||||
let currentIndex = array.length, randomIndex;
|
||||
|
||||
// While there remain elements to shuffle.
|
||||
while (currentIndex > 0) {
|
||||
|
||||
// Pick a remaining element.
|
||||
randomIndex = Math.floor(Math.random() * currentIndex);
|
||||
currentIndex--;
|
||||
|
||||
// And swap it with the current element.
|
||||
[array[currentIndex], array[randomIndex]] = [
|
||||
array[randomIndex], array[currentIndex]];
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,26 +1,26 @@
|
||||
// Typography
|
||||
// @import "globals/typo-font-sizes";
|
||||
// @import "globals/typo-font-weights";
|
||||
// @import "globals/typo-text-transforms";
|
||||
// @import "globals/typo-text-decorations";
|
||||
// @import "globals/typo-letter-spacings";
|
||||
// @import "globals/typo-line-heights";
|
||||
// @import "globals/typo-font-styles";
|
||||
// @import "globals/typo-list-styles";
|
||||
// @import "globals/typo-text-styles";
|
||||
@import "globals/typo-font-sizes";
|
||||
@import "globals/typo-font-weights";
|
||||
@import "globals/typo-text-transforms";
|
||||
@import "globals/typo-text-decorations";
|
||||
@import "globals/typo-letter-spacings";
|
||||
@import "globals/typo-line-heights";
|
||||
@import "globals/typo-font-styles";
|
||||
@import "globals/typo-list-styles";
|
||||
@import "globals/typo-text-styles";
|
||||
|
||||
// Border styles
|
||||
// @import "globals/borders";
|
||||
// @import "globals/border-none";
|
||||
// @import "globals/border-styles";
|
||||
// @import "globals/border-radiuses";
|
||||
// @import "globals/border-dashed";
|
||||
// @import "globals/border-dotted";
|
||||
// @import "globals/border-widths";
|
||||
@import "globals/borders";
|
||||
@import "globals/border-none";
|
||||
@import "globals/border-styles";
|
||||
@import "globals/border-radiuses";
|
||||
@import "globals/border-dashed";
|
||||
@import "globals/border-dotted";
|
||||
@import "globals/border-widths";
|
||||
|
||||
//Common
|
||||
|
||||
@import "globals/heights";
|
||||
@import "globals/widths";
|
||||
// @import "globals/z-index";
|
||||
// @import "globals/cursor";
|
||||
@import "globals/z-index";
|
||||
@import "globals/cursor";
|
||||
|
||||
@ -2,6 +2,30 @@
|
||||
Heights
|
||||
------------------------------------*/
|
||||
|
||||
$i: 5;
|
||||
@while $i < 100 {
|
||||
.g-height-#{$i}vh {
|
||||
height: #{$i}vh;
|
||||
}
|
||||
$i: $i + 5;
|
||||
}
|
||||
|
||||
$i: 10;
|
||||
@while $i < 200 {
|
||||
.g-height-#{$i}px {
|
||||
height: #{$i}px;
|
||||
}
|
||||
$i: $i + 2;
|
||||
}
|
||||
|
||||
$i: 200;
|
||||
@while $i < 800 {
|
||||
.g-height-#{$i}px {
|
||||
height: #{$i}px;
|
||||
}
|
||||
$i: $i + 20;
|
||||
}
|
||||
|
||||
@each $breakpoint in map-keys($grid-breakpoints) {
|
||||
@include media-breakpoint-up($breakpoint) {
|
||||
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
|
||||
@ -37,6 +61,39 @@
|
||||
}
|
||||
$i: $i + 5;
|
||||
}
|
||||
|
||||
$i: 5;
|
||||
@while $i < 100 {
|
||||
.g-height-#{$i}vh {
|
||||
height: #{$i}vh;
|
||||
}
|
||||
|
||||
.g-max-height-#{$i}vh#{$infix} {
|
||||
max-height: #{$i}vh !important;
|
||||
}
|
||||
|
||||
.g-min-height-#{$i}vh#{$infix} {
|
||||
min-height: #{$i}vh !important;
|
||||
}
|
||||
$i: $i + 5;
|
||||
}
|
||||
|
||||
$i: 10;
|
||||
@while $i < 200 {
|
||||
.g-height-#{$i}px#{$infix} {
|
||||
height: #{$i}px;
|
||||
}
|
||||
$i: $i + 2;
|
||||
}
|
||||
|
||||
$i: 200;
|
||||
@while $i < 800 {
|
||||
.g-height-#{$i}px#{$infix} {
|
||||
height: #{$i}px;
|
||||
}
|
||||
$i: $i + 20;
|
||||
}
|
||||
|
||||
/* Auto Height */
|
||||
.g-height-auto#{$infix} {
|
||||
height: auto;
|
||||
|
||||
@ -4,3 +4,7 @@
|
||||
.g-text-break-word {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.g-text-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@ -15,14 +15,38 @@
|
||||
.MD2text {
|
||||
font-family: "DwarvenAxeBBW00-Regular", sans-serif !important;
|
||||
}
|
||||
.MD2IconContainer-sm {
|
||||
.MD2Icon {
|
||||
font-size: 18px;
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
.MD2IconContainer-md {
|
||||
.MD2Icon {
|
||||
font-size: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
.MD2IconContainer-lg {
|
||||
.MD2Icon {
|
||||
font-size: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
}
|
||||
.MD2IconContainer-xl {
|
||||
.MD2Icon {
|
||||
font-size: 50px;
|
||||
line-height: 50px;
|
||||
}
|
||||
}
|
||||
.MD2Icon {
|
||||
font-family: "Massive Darkness 2", sans-serif !important;
|
||||
//font-size: 50px;
|
||||
|
||||
&.atk::before {
|
||||
&.attack::before {
|
||||
content: "A";
|
||||
}
|
||||
&.def::before {
|
||||
&.defense::before {
|
||||
content: "B";
|
||||
}
|
||||
&.mana::before {
|
||||
@ -34,7 +58,7 @@
|
||||
&.enemySkill::before {
|
||||
content: "E";
|
||||
}
|
||||
&.enemyAtk::before {
|
||||
&.enemyAttack::before {
|
||||
content: "F";
|
||||
}
|
||||
&.reroll::before {
|
||||
@ -107,13 +131,13 @@
|
||||
&::before {
|
||||
content: "U";
|
||||
}
|
||||
color: orangered !important;
|
||||
color: #ffab6a !important;
|
||||
}
|
||||
&.diceBlack {
|
||||
&::before {
|
||||
content: "U";
|
||||
}
|
||||
color: black !important;
|
||||
color: #5b5d64 !important;
|
||||
}
|
||||
&.diceRed {
|
||||
&::before {
|
||||
@ -124,16 +148,16 @@
|
||||
&.dice {
|
||||
font-family: "Massive Darkness 2", sans-serif !important;
|
||||
&.Blue {
|
||||
color: aqua !important;
|
||||
color: #58a1ff !important;
|
||||
}
|
||||
&.Yellow {
|
||||
color: #ffc107 !important;
|
||||
}
|
||||
&.Orange {
|
||||
color: orangered !important;
|
||||
color: #ffab6a !important;
|
||||
}
|
||||
&.Black {
|
||||
color: black !important;
|
||||
color: #5b5d64 !important;
|
||||
}
|
||||
&.Red {
|
||||
color: crimson !important;
|
||||
|
||||
@ -17,6 +17,6 @@
|
||||
@import "form-controls";
|
||||
@import "md2";
|
||||
nb-card {
|
||||
max-width: 90vw;
|
||||
max-width: 96vw;
|
||||
max-height: 96vh;
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ const urls = [
|
||||
'https://api.happiness.tours'
|
||||
];
|
||||
const LINE_CLIENT_ID = '1657422139';
|
||||
const dockerDebug = urls[0];
|
||||
const dockerDebug = urls[2];
|
||||
export const environment = {
|
||||
production: false,
|
||||
apiUrl: dockerDebug,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user