Update boss fight

This commit is contained in:
Chris Chen 2024-03-29 08:04:07 -07:00
parent 6a031ca478
commit d486fe9594
55 changed files with 1809 additions and 799 deletions

View File

@ -70,6 +70,10 @@ const socialLinks = [
NbChatModule.forRoot({ NbChatModule.forRoot({
messageGoogleMapKey: 'AIzaSyA_wNuCzia92MAmdLRemailRGvCF7wCZPY', messageGoogleMapKey: 'AIzaSyA_wNuCzia92MAmdLRemailRGvCF7wCZPY',
}), }),
NbDialogModule.forRoot({
closeOnBackdropClick: false,
closeOnEsc: false
}),
NgxMaskModule.forRoot(maskConfig), NgxMaskModule.forRoot(maskConfig),
NbDateFnsDateModule.forRoot({ format: 'MM/dd/yyyy' }), NbDateFnsDateModule.forRoot({ format: 'MM/dd/yyyy' }),
CoreModule.forRoot(), CoreModule.forRoot(),

View File

@ -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 { 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 { 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 { 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({ @NgModule({
@ -58,7 +59,8 @@ import { MobCombatInfoComponent } from './massive-darkness2/mobs/mob-detail-info
BossActivationComponent, BossActivationComponent,
MobAttackInfoComponent, MobAttackInfoComponent,
MobDefInfoComponent, MobDefInfoComponent,
MobCombatInfoComponent MobCombatInfoComponent,
MobStandInfoComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,

View File

@ -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 => { this.stateService.loginUserService.signalRInitialized.pipe(first()).subscribe(result => {
console.log('signalRInitialized'); console.log('signalRInitialized');
@ -78,9 +81,15 @@ export abstract class MD2Base {
} }
abstract refreshUI(); abstract refreshUI();
handleSignalRCallback(message: SignalRMessage): void { 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) { switch (message.actionType) {
case 'hero': case 'hero':
let heroInfo = JSON.parse(message.parameters['hero']) as MD2HeroInfo; let heroInfo = new MD2HeroInfo(JSON.parse(message.parameters['hero']));
switch (message.actionName) { switch (message.actionName) {
case 'join': case 'join':
this.md2Service.heros.push(heroInfo); this.md2Service.heros.push(heroInfo);
@ -88,13 +97,22 @@ export abstract class MD2Base {
case 'update': case 'update':
let exitingHero = this.md2Service.heros.find(h => h.playerInfo.signalRClientId == heroInfo.playerInfo.signalRClientId); let exitingHero = this.md2Service.heros.find(h => h.playerInfo.signalRClientId == heroInfo.playerInfo.signalRClientId);
if (exitingHero) { 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 { } else {
this.md2Service.heros.push(heroInfo); this.md2Service.heros.push(heroInfo);
} }
if (!this.isHeroDashboard) { if (!this.isHeroDashboard) {
if (this.gameInfo.roundPhase == RoundPhase.HeroPhase) { if (this.gameInfo.roundPhase == RoundPhase.HeroPhase) {
if (!this.md2Service.heros.some(h => h.remainActions > 0)) { 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) { 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.msgBoxService.show('Enemy Phase', { icon: ADIcon.WARNING }).pipe(first()).subscribe(result => {
this.md2Service.runNextPhase(); this.md2Service.runNextPhase();
@ -102,12 +120,20 @@ export abstract class MD2Base {
} else { } else {
this.md2Service.runNextPhase(); this.md2Service.runNextPhase();
} }
}
} }
} }
} }
//Object.assign(heroInfo, exitingHero); //Object.assign(heroInfo, exitingHero);
break;
case 'updateMyHero':
if (this.isHeroDashboard) {
this.md2Service.stateService.playerHero = heroInfo;
}
break; break;
default: default:
break; break;
@ -126,6 +152,12 @@ export abstract class MD2Base {
this.detectChanges(); this.detectChanges();
} }
break; break;
case 'phase':
if (this.isHeroDashboard) {
this.md2Service.info.roundPhase = JSON.parse(message.parameters['phase']);
this.detectChanges();
}
break;
default: default:
break; break;
} }
@ -160,17 +192,21 @@ export abstract class MD2Base {
break; break;
case 'heroAction': case 'heroAction':
if (!this.isHeroDashboard) { 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) { switch (message.actionName) {
case 'attackAction': case 'attackAction':
if (this.gameInfo.isBossFight) {
this.md2Service.heroAttackingSubject.next(this.md2Service.currentActivateHero);
} else {
this.gameInfo.showAttackBtn = true; this.gameInfo.showAttackBtn = true;
}
break; break;
case 'openDoor': case 'openDoor':
//Door component listen for it //Door component listen for it
break; break;
case 'tradeAction': case 'tradeAction':
this.md2Service.msgBoxService.show('Trade and Equip', { 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!`, equip items!`,
icon: ADIcon.INFO icon: ADIcon.INFO
}); });
@ -179,7 +215,7 @@ export abstract class MD2Base {
//this.md2Service.roundPhase = Number.parseInt(message.parameters['phase']); //this.md2Service.roundPhase = Number.parseInt(message.parameters['phase']);
break; break;
} }
this.heroAction(this.gameInfo.currentActivateHero, message.actionName); this.heroAction(this.md2Service.currentActivateHero, message.actionName);
this.detectChanges(); this.detectChanges();
} }
break; break;
@ -227,4 +263,15 @@ export abstract class MD2ComponentBase {
iconHtml(icon: MD2Icon, cssClass = '') { iconHtml(icon: MD2Icon, cssClass = '') {
return this.md2Service.stateService.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() {
}
} }

View File

@ -1,26 +1,32 @@
<nb-card> <nb-card>
<nb-card-body> <nb-card-body class="g-overflow-hidden">
<div class="row form-group" style=" <div class="row form-group">
height: 53vh; <div class="col-md-5 g-height-700px">
overflow: auto; <md2-mob-stand-info [mob]="boss.info" [mode]="mode"></md2-mob-stand-info>
">
<div class="col-md-5">
<img src="{{boss.standUrl}}" class="img-fluid">
</div> </div>
<div class="col-md-7"> <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>
<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> </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-attack-info [mob]="boss.info">
</md2-mob-attack-info>
<md2-mob-combat-info [mob]="boss.info"></md2-mob-combat-info> <md2-mob-combat-info [mob]="boss.info"></md2-mob-combat-info>
</div> </div>
</div>
</div>
</div> </div>
</nb-card-body> </nb-card-body>
<nb-card-footer> <nb-card-footer>

View File

@ -1,10 +1,12 @@
import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { NbDialogRef } from '@nebular/theme'; import { NbDialogRef } from '@nebular/theme';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { MD2Service } from '../../../../services/MD2/md2.service'; import { MD2Service } from '../../../../services/MD2/md2.service';
import { MsgBoxService } from '../../../../services/msg-box.service'; import { MsgBoxService } from '../../../../services/msg-box.service';
import { StateService } from '../../../../services/state.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 { MobSkill, IBossFight } from '../../massive-darkness2.model.boss';
import { MD2ComponentBase } from '../../MD2Base'; import { MD2ComponentBase } from '../../MD2Base';
import { SpawnMobDlgComponent } from '../../mobs/spawn-mob-dlg/spawn-mob-dlg.component'; 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', templateUrl: './boss-activation.component.html',
styleUrls: ['./boss-activation.component.scss'] styleUrls: ['./boss-activation.component.scss']
}) })
export class BossActivationComponent extends MD2ComponentBase implements OnInit { export class BossActivationComponent implements OnInit {
boss: IBossFight; boss: IBossFight;
bossAction: MobSkill; bossAction: MobSkill;
currentAction: number;
allActions: number;
MobDlgType = MobDlgType; MobDlgType = MobDlgType;
mode: MobDlgType; mode: MobDlgType = MobDlgType.Activating;
title: string; title: string;
titleHtml: string; titleHtml: string;
@ -31,16 +34,29 @@ export class BossActivationComponent extends MD2ComponentBase implements OnInit
otherAttackTarget: string; otherAttackTarget: string;
constructor( constructor(
private dlgRef: NbDialogRef<SpawnMobDlgComponent>, private dlgRef: NbDialogRef<SpawnMobDlgComponent>,
private msgBoxService: MsgBoxService,
public md2Service: MD2Service, public md2Service: MD2Service,
protected stateService: StateService, protected stateService: StateService,
protected route: ActivatedRoute, protected route: ActivatedRoute,
protected cdRef: ChangeDetectorRef, 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() { close() {
this.boss.standUrl //this.boss.standUrl
this.md2Service.info.roundPhase = RoundPhase.HeroPhase;
this.dlgRef.close(); this.dlgRef.close();
} }

View File

@ -1,5 +1,5 @@
<nb-card> <nb-card>
<nb-card-header> <nb-card-header class="MD2text g-font-size-28">
{{boss.name}} {{boss.name}}
<button nbButton hero status="primary" (click)="activate()">Action</button> <button nbButton hero status="primary" (click)="activate()">Action</button>
</nb-card-header> </nb-card-header>
@ -9,13 +9,24 @@
<img src="{{boss.standUrl}}" class="w-100 g-max-height-80vh"> <img src="{{boss.standUrl}}" class="w-100 g-max-height-80vh">
</div> </div>
<div class="col-md-7"> <div class="col-md-7">
<md2-mob-detail-info [mob]="boss.info"> <div class="row">
</md2-mob-detail-info> <div class="col-md">
<adj-number-input name="mob{{boss.info.name}}" [(ngModel)]="boss.info.unitRemainHp" minimum="0" <adj-number-input name="mob{{boss.info.name}}" [(ngModel)]="boss.info.unitRemainHp" minimum="0"
title="Boss HP" (hitMinimum)="WIN()"> class="mb-3" title="Boss HP" (hitMinimum)="WIN()">
</adj-number-input> </adj-number-input>
<button nbButton hero status="danger" size="small" (click)="attack(boss.info)">Attack It</button> <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>
</div>
<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 class="MD2Text mt-3" [innerHtml]="boss.info.combatSkill.skillName">
</label> </label>

View File

@ -2,7 +2,7 @@ import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { NbDialogService } from '@nebular/theme'; import { NbDialogService } from '@nebular/theme';
import { Subject, Observable } from 'rxjs'; import { Subject, Observable } from 'rxjs';
import { first } from 'rxjs/operators'; import { first, takeUntil } from 'rxjs/operators';
import { MD2Service } from '../../../services/MD2/md2.service'; import { MD2Service } from '../../../services/MD2/md2.service';
import { MsgBoxService } from '../../../services/msg-box.service'; import { MsgBoxService } from '../../../services/msg-box.service';
import { StateService } from '../../../services/state.service'; import { StateService } from '../../../services/state.service';
@ -37,7 +37,16 @@ export class BossFightComponent extends MD2ComponentBase {
ngOnInit(): void { ngOnInit(): void {
super.ngOnInit(); 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() { activate() {
this.boss.activating(); this.boss.activating();

View File

@ -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' }) 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 => { .pipe(first()).subscribe(result => {
if (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.drawDoorEvents.push(door);
} }
this.cdRef.detectChanges(); this.cdRef.detectChanges();

View 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);
}
}

View File

@ -37,6 +37,7 @@ const CORE_GAME_MOB_LEVEL = [
{ name: 'Satyrs', level: 5, hp: 6, rewardTokens: 2, defBlue: 4 }] { name: 'Satyrs', level: 5, hp: 6, rewardTokens: 2, defBlue: 4 }]
export abstract class MobFactory implements IMobFactory { export abstract class MobFactory implements IMobFactory {
abstract mobName: string;
abstract generate(level: number): MobInfo abstract generate(level: number): MobInfo
protected mob: MobInfo; protected mob: MobInfo;
protected loadLevelInfo(mobName: string, level: number) { protected loadLevelInfo(mobName: string, level: number) {
@ -46,11 +47,11 @@ export abstract class MobFactory implements IMobFactory {
name: mobName, hp: levelInfo.hp, level: level, rewardTokens: levelInfo.rewardTokens, name: mobName, hp: levelInfo.hp, level: level, rewardTokens: levelInfo.rewardTokens,
defenseInfo: new DefenseInfo(levelInfo.defBlue) defenseInfo: new DefenseInfo(levelInfo.defBlue)
}); });
this.mob.leaderImgUrl = MD2_IMG_URL(`CoreGame/Mobs/${encodeURI(this.mob.name)}/Leader.png`); this.mob.leaderImgUrl = MD2_IMG_URL(`/CoreGame/Mobs/${this.mob.name}/Leader.png`);
this.mob.minionImgUrl = MD2_IMG_URL(`CoreGame/Mobs/${encodeURI(this.mob.name)}/Minion.png`); this.mob.minionImgUrl = MD2_IMG_URL(`/CoreGame/Mobs/${this.mob.name}/Minion.png`);
} }
iconHtml(icon: MD2Icon, cssClass = '') { iconHtml(icon: MD2Icon, cssClass = 'g-font-size-24') {
if (icon == MD2Icon.Fire) { if (icon == MD2Icon.Fire) {
cssClass += ' g-color-google-plus '; cssClass += ' g-color-google-plus ';
} }
@ -68,6 +69,7 @@ export abstract class MobFactory implements IMobFactory {
} }
} }
export class MobDemonsFactory extends MobFactory { export class MobDemonsFactory extends MobFactory {
mobName: string = 'Demons';
generate(level: number): MobInfo { generate(level: number): MobInfo {
this.loadLevelInfo('Demons', level); this.loadLevelInfo('Demons', level);
this.mob.combatSkill = new MobSkill( this.mob.combatSkill = new MobSkill(
@ -80,6 +82,7 @@ export class MobDemonsFactory extends MobFactory {
} }
export class MobFallenAngelFactory extends MobFactory { export class MobFallenAngelFactory extends MobFactory {
mobName: string = 'Fallen Angels';
generate(level: number): MobInfo { generate(level: number): MobInfo {
this.loadLevelInfo('Fallen Angels', level); this.loadLevelInfo('Fallen Angels', level);
this.mob.combatSkill = new MobSkill( this.mob.combatSkill = new MobSkill(
@ -93,6 +96,7 @@ export class MobFallenAngelFactory extends MobFactory {
} }
export class MobFireEntitiesFactory extends MobFactory { export class MobFireEntitiesFactory extends MobFactory {
mobName: string = 'Fire Entities';
generate(level: number): MobInfo { generate(level: number): MobInfo {
this.loadLevelInfo('Fire Entities', level); this.loadLevelInfo('Fire Entities', level);
this.mob.combatSkill = new MobSkill( this.mob.combatSkill = new MobSkill(
@ -106,6 +110,7 @@ export class MobFireEntitiesFactory extends MobFactory {
} }
export class MobGargoylesFactory extends MobFactory { export class MobGargoylesFactory extends MobFactory {
mobName: string = 'Gargoyles';
generate(level: number): MobInfo { generate(level: number): MobInfo {
this.loadLevelInfo('Gargoyles', level); this.loadLevelInfo('Gargoyles', level);
this.mob.combatSkill = new MobSkill( this.mob.combatSkill = new MobSkill(
@ -118,6 +123,7 @@ export class MobGargoylesFactory extends MobFactory {
} }
} }
export class MobInfernalImpsFactory extends MobFactory { export class MobInfernalImpsFactory extends MobFactory {
mobName: string = 'Infernal Imps';
generate(level: number): MobInfo { generate(level: number): MobInfo {
this.loadLevelInfo('Infernal Imps', level); this.loadLevelInfo('Infernal Imps', level);
let damage = 1; let damage = 1;
@ -148,6 +154,7 @@ export class MobInfernalImpsFactory extends MobFactory {
} }
} }
export class MobSatyrsFactory extends MobFactory { export class MobSatyrsFactory extends MobFactory {
mobName: string = 'Satyrs';
generate(level: number): MobInfo { generate(level: number): MobInfo {
this.loadLevelInfo('Satyrs', level); this.loadLevelInfo('Satyrs', level);
this.mob.combatSkill = new MobSkill( this.mob.combatSkill = new MobSkill(
@ -161,6 +168,7 @@ export class MobSatyrsFactory extends MobFactory {
} }
export class MobSkeletonsFactory extends MobFactory { export class MobSkeletonsFactory extends MobFactory {
mobName: string = 'Skeletons';
generate(level: number): MobInfo { generate(level: number): MobInfo {
this.loadLevelInfo('Skeletons', level); this.loadLevelInfo('Skeletons', level);
this.mob.combatSkill = new MobSkill( this.mob.combatSkill = new MobSkill(
@ -175,6 +183,7 @@ export class MobSkeletonsFactory extends MobFactory {
} }
export class MobUndeadFactory extends MobFactory { export class MobUndeadFactory extends MobFactory {
mobName: string = 'Undead';
generate(level: number): MobInfo { generate(level: number): MobInfo {
this.loadLevelInfo('Undead', level); this.loadLevelInfo('Undead', level);
@ -195,6 +204,7 @@ export class MobUndeadFactory extends MobFactory {
type: MobSkillType.Attack type: MobSkillType.Attack
} }
) )
this.mob.drawingWeight = 1;
return this.mob; return this.mob;
} }
} }

View File

@ -1,53 +1,111 @@
import { Subject } from "rxjs";
import { first, map } from "rxjs/operators";
import { environment } from "../../../../../environments/environment"; import { environment } from "../../../../../environments/environment";
import { DefenseInfo, IMobFactory, MD2Icon, MobInfo } from "../../massive-darkness2.model"; 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"; 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 MD2_IMG_URL = (id: string = null) => { return `${environment.apiUrl}/Files/Images/MD2/Mobs${(id ? `${encodeURI(id)}` : '')}` }
const CORE_GAME_MOB_LEVEL = [ 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 }, new MobInfo({
{ name: 'Demons', level: 3, hp: 4, rewardTokens: 1, defBlue: 2 }, name: 'Andra', level: 1, hp: 5,
{ name: 'Demons', level: 5, hp: 6, rewardTokens: 2, defBlue: 4 },
{ name: 'Undead', level: 1, hp: 4, rewardTokens: 1, defBlue: 1 }, attackInfos: [new AttackInfo(MD2Icon.Melee, 1, 0, 0, 1), new AttackInfo(MD2Icon.Range, 1, 0, 0, 1)],
{ name: 'Undead', level: 3, hp: 5, rewardTokens: 1, defBlue: 1 }, defenseInfo: new DefenseInfo(2, 1),
{ name: 'Undead', level: 5, hp: 8, rewardTokens: 2, defBlue: 1 }, }),
new MobInfo({
name: 'Andra', level: 3, hp: 7,
{ name: 'Fire Entities', level: 1, hp: 3, rewardTokens: 1, defBlue: 1 }, attackInfos: [new AttackInfo(MD2Icon.Melee, 1, 1, 0, 1), new AttackInfo(MD2Icon.Range, 1, 1, 0, 1)],
{ name: 'Fire Entities', level: 3, hp: 4, rewardTokens: 1, defBlue: 2 }, defenseInfo: new DefenseInfo(3, 1),
{ name: 'Fire Entities', level: 5, hp: 7, rewardTokens: 2, defBlue: 3 }, }),
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),
}),
{ name: 'Fallen Angels', level: 1, hp: 2, rewardTokens: 1, defBlue: 2 }, new MobInfo({
{ name: 'Fallen Angels', level: 3, hp: 3, rewardTokens: 1, defBlue: 3 }, name: 'Ytheria, Undead Queen', level: 1, hp: 4,
{ name: 'Fallen Angels', level: 5, hp: 5, rewardTokens: 2, defBlue: 5 }, attackInfos: [new AttackInfo(MD2Icon.Melee, 1), new AttackInfo(MD2Icon.Range, 2, 0, 0, 1)],
defenseInfo: new DefenseInfo(1, 1),
{ name: 'Infernal Imps', level: 1, hp: 3, rewardTokens: 1, defBlue: 1 }, }),
{ name: 'Infernal Imps', level: 3, hp: 4, rewardTokens: 1, defBlue: 1 }, new MobInfo({
{ name: 'Infernal Imps', level: 5, hp: 5, rewardTokens: 2, defBlue: 3 }, 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),
{ name: 'Skeletons', level: 1, hp: 2, rewardTokens: 1, defBlue: 1 }, }),
{ name: 'Skeletons', level: 3, hp: 3, rewardTokens: 1, defBlue: 2 }, new MobInfo({
{ name: 'Skeletons', level: 5, hp: 5, rewardTokens: 2, defBlue: 4 }, 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),
}),
{ name: 'Satyrs', level: 1, hp: 3, rewardTokens: 1, defBlue: 1 }, new MobInfo({
{ name: 'Satyrs', level: 3, hp: 4, rewardTokens: 1, defBlue: 2 }, name: 'Lyidan, Incubus Lord', level: 1, hp: 7,
{ name: 'Satyrs', level: 5, hp: 6, rewardTokens: 2, defBlue: 4 }] 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),
}),
export abstract class MobFactory implements IMobFactory { 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 abstract generate(level: number): MobInfo
protected mob: MobInfo; protected mob: MobInfo;
protected loadLevelInfo(mobName: string, level: number) { protected loadLevelInfo(mobName: string, level: number) {
let levelInfo = CORE_GAME_MOB_LEVEL.find(m => m.name == mobName && level >= m.level); let levelInfo = CORE_GAME_MOB_LEVEL.find(m => m.name == mobName && level >= m.level);
this.mob = new MobInfo({ this.mob = new MobInfo({
name: mobName, hp: levelInfo.hp, level: level, rewardTokens: levelInfo.rewardTokens, type: MobType.RoamingMonster,
defenseInfo: new DefenseInfo(levelInfo.defBlue) 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/Mobs/${encodeURI(this.mob.name)}/Leader.png`); 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`); //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 = '') { iconHtml(icon: MD2Icon, cssClass = '') {
@ -67,146 +125,213 @@ export abstract class MobFactory implements IMobFactory {
} }
} }
} }
export class MobDemonsFactory extends MobFactory { export class RMUndeadQueenFactory extends CoreGameRMFactory {
mobName: string = 'Ytheria, Undead Queen';
generate(level: number): MobInfo { generate(level: number): MobInfo {
this.loadLevelInfo('Demons', level); this.loadLevelInfo('Ytheria, Undead Queen', level);
this.mob.combatSkill = new MobSkill(
{ this.mob.activateFunction = (mob, msgBoxService, heros) => {
description: `Attacking or defending Hero discards 1 ${this.iconHtml(MD2Icon.Mana)}` 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;
return this.mob; mob.actionSubject.next(
actionResult
);
} else {
mob.actions = 2;
mob.actionSubject.next(
`Undead Queen Gains 2 Actions`
);
}
});
}
});;
} }
}
export class MobFallenAngelFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('FallenAngels', level);
this.mob.combatSkill = new MobSkill( this.mob.combatSkill = new MobSkill(
{ {
description: `Defender -${level == 1 ? 1 : 2} ${this.iconHtml(MD2Icon.Defense)}`, description: `Add 1 Minion to each Mob in the Dungeon, if possible.`,
type: MobSkillType.Attack type: MobSkillType.Attack
} }
) )
return this.mob; return this.mob;
} }
} }
export class RMAndraFactory extends CoreGameRMFactory {
export class MobFireEntitiesFactory extends MobFactory { mobName: string = 'Andra';
generate(level: number): MobInfo { generate(level: number): MobInfo {
this.loadLevelInfo('Fire Entities', level); 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( this.mob.combatSkill = new MobSkill(
{ {
description: `Add 1 ${this.iconHtml(MD2Icon.Fire)} to the attacking or defending Hero.`, description: `Deal ${damage} wound to another Hero with the lowest HP in LoS`,
type: MobSkillType.Combat type: MobSkillType.Combat
} }
) )
return this.mob; return this.mob;
} }
} }
export class RMTheGhoulFactory extends CoreGameRMFactory {
export class MobGargoylesFactory extends MobFactory { mobName: string = 'The Ghoul';
generate(level: number): MobInfo { generate(level: number): MobInfo {
this.loadLevelInfo('Gargoyles', level); this.loadLevelInfo('The Ghoul', level);
this.mob.combatSkill = new MobSkill( let health = 2;
{
description: `+ ${level < 5 ? 1 : 2} ${this.iconHtml(MD2Icon.Defense)}`,
type: MobSkillType.Defense
}
)
return this.mob;
}
}
export class MobInfernalImpsFactory extends MobFactory {
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 {
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 {
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 {
generate(level: number): MobInfo {
this.loadLevelInfo('Undead', level);
let skillDesc = '';
if (level < 3) { if (level < 3) {
skillDesc = `+1 ${this.iconHtml(MD2Icon.YellowDice)}`; health = 5;
} else if (level < 5) { } else if (level < 5) {
skillDesc = `+2 ${this.iconHtml(MD2Icon.YellowDice)}`; health = 8;
} else { } else {
skillDesc = `+1 ${this.iconHtml(MD2Icon.YellowDice)} 1 ${this.iconHtml(MD2Icon.OrangeDice)}`; health = 10;
} }
skillDesc += ' and this Mob takes 2 wounds';
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( this.mob.combatSkill = new MobSkill(
{ {
description: skillDesc, 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 type: MobSkillType.Attack
} }
) )
return this.mob; return this.mob;
} }
} }
export const CoreGameRMFactories = [
new RMUndeadQueenFactory(),
export const CoreGameMobFactories = [ new RMAndraFactory(),
new MobDemonsFactory(), new RMTheGhoulFactory(),
new MobFallenAngelFactory(), new RMLyidanIncubusLordFactory(),
new MobFireEntitiesFactory(),
new MobGargoylesFactory(),
new MobInfernalImpsFactory(),
new MobSatyrsFactory(),
new MobSkeletonsFactory(),
new MobUndeadFactory(),
]; ];

View File

@ -1,9 +1,9 @@
<nb-card *ngIf="!md2Service.playerHero"> <nb-card *ngIf="!hero">
<nb-card-body> <nb-card-body>
<button nbButton hero status="primary" fullWidth (click)="initHero()">Choose Hero</button> <button nbButton hero status="primary" fullWidth (click)="initHero()">Choose Hero</button>
</nb-card-body> </nb-card-body>
</nb-card> </nb-card>
<div *ngIf="md2Service.playerHero"> <div *ngIf="hero">
<div class="row no-gutters"> <div class="row no-gutters">
<div class="col-12 col-sm-7"> <div class="col-12 col-sm-7">
<div class="tp-wrapper mb-2"> <div class="tp-wrapper mb-2">
@ -11,24 +11,26 @@
[@flipState]="flip"> [@flipState]="flip">
<div class="tp-box__side tp-box__front "> <div class="tp-box__side tp-box__front ">
<img class="MD2HeroCard " src="{{md2Service.playerHero.imgUrl}}"> <img class="MD2HeroCard " src="{{hero.imgUrl}}">
</div> </div>
<div class="tp-box__side tp-box__back"> <div class="tp-box__side tp-box__back">
<img class="MD2HeroCard " src="{{imgUrl('Heros/Guide/'+className+'.jpg')}}"> <img class="MD2HeroCard " src="{{imgUrl('Heros/Guide/'+className+'.jpg')}}">
<img class="MD2HeroCard " src="{{imgUrl('Sets/Shadowbane/'+className+'.png')}}">
</div> </div>
</div> </div>
</div> </div>
<!-- <div class="g-max-height-80vh mb-2"> <!-- <div class="g-max-height-80vh mb-2">
<img class="MD2HeroCard" src="{{md2Service.playerHero.imgUrl}}"> <img class="MD2HeroCard" src="{{hero.imgUrl}}">
<div class="MD2HeroCard"> <div class="MD2HeroCard">
<span class="MD2text MD2Name">{{md2Service.playerHero.name}}</span> <span class="MD2text MD2Name">{{hero.name}}</span>
<span class="MD2text MD2Hp">{{md2Service.playerHero.hpMaximum}}</span> <span class="MD2text MD2Hp">{{hero.hpMaximum}}</span>
<span class="MD2text MD2Mp">{{md2Service.playerHero.mpMaximum}}</span> <span class="MD2text MD2Mp">{{hero.mpMaximum}}</span>
</div> </div>
<img class="MD2HeroCard" src="{{md2Service.playerHero.imgUrl}}"> <img class="MD2HeroCard" src="{{hero.imgUrl}}">
<img class="MD2HeroCard HpMpBar" src="{{imgUrl('/Heros/Template/Border.png')}}"> <img class="MD2HeroCard HpMpBar" src="{{imgUrl('/Heros/Template/Border.png')}}">
</div> --> </div> -->
</div> </div>
@ -39,63 +41,57 @@
<div class="row no-gutters"> <div class="row no-gutters">
<div class="col-6"> <div class="col-6">
<!-- <adj-number-input name="heroHP" [(ngModel)]="md2Service.playerHero.hp" <!-- <adj-number-input name="heroHP" [(ngModel)]="hero.hp"
[maximum]="md2Service.playerHero.hpMaximum" minimum="0" [maximum]="hero.hpMaximum" minimum="0"
title="{{iconHtml(MD2Icon.HP,'g-color-google-plus mr-1 g-font-size-18')}}HP" showMaximum title="{{iconHtml(MD2Icon.HP,'g-color-google-plus mr-1 g-font-size-18')}}HP" showMaximum
(blur)="heroUpdateDebounceTimer.resetTimer()" (hitDecreasing)="increaseRage()"> (blur)="heroUpdateDebounceTimer.resetTimer()" (hitDecreasing)="increaseRage()">
</adj-number-input> --> </adj-number-input> -->
<adj-number-input name="heroHP" [(ngModel)]="md2Service.playerHero.hp" <adj-number-input name="heroHP" [(ngModel)]="hero.hp" [maximum]="hero.hpMaximum" minimum="0"
[maximum]="md2Service.playerHero.hpMaximum" minimum="0"
title="{{imgHtml('HpIcon.png','g-height-25 mr-1')}}HP" showMaximum title="{{imgHtml('HpIcon.png','g-height-25 mr-1')}}HP" showMaximum
(blur)="heroUpdateDebounceTimer.resetTimer()" (hitDecreasing)="increaseRage()"> (blur)="heroUpdateDebounceTimer.resetTimer()" (hitDecreasing)="increaseRage()">
</adj-number-input> </adj-number-input>
<adj-number-input name="heroMana" [(ngModel)]="md2Service.playerHero.mp" <adj-number-input name="heroMana" [(ngModel)]="hero.mp" [maximum]="hero.mpMaximum"
[maximum]="md2Service.playerHero.mpMaximum" minimum="0" minimum="0" title="{{imgHtml('HeroIcon.png','g-height-25 mr-1')}}Mana" showMaximum
title="{{imgHtml('HeroIcon.png','g-height-25 mr-1')}}Mana" showMaximum
(blur)="heroUpdateDebounceTimer.resetTimer()"> (blur)="heroUpdateDebounceTimer.resetTimer()">
</adj-number-input> </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" title="{{iconHtml(MD2Icon.Fire,'g-color-google-plus mr-1 g-font-size-18')}}Fire Token"
(blur)="heroUpdateDebounceTimer.resetTimer()"> (blur)="heroUpdateDebounceTimer.resetTimer()">
</adj-number-input> </adj-number-input>
<adj-number-input name="heroFire" [(ngModel)]="md2Service.playerHero.frozenToken" <adj-number-input name="heroFire" [(ngModel)]="hero.frozenToken" minimum="0"
minimum="0"
title="{{iconHtml(MD2Icon.Frost,'g-color-aqua mr-1 g-font-size-18')}}Frozen Token" title="{{iconHtml(MD2Icon.Frost,'g-color-aqua mr-1 g-font-size-18')}}Frozen Token"
(blur)="heroUpdateDebounceTimer.resetTimer()"> (blur)="heroUpdateDebounceTimer.resetTimer()">
</adj-number-input> </adj-number-input>
</div> </div>
<div class="col-6"> <div class="col-6">
<adj-number-input name="remainActions" [(ngModel)]="md2Service.playerHero.remainActions" <adj-number-input name="remainActions" [(ngModel)]="hero.remainActions" minimum="0"
minimum="0" title="Remain Actions" (blur)="heroUpdateDebounceTimer.resetTimer()" title="Remain Actions" (blur)="heroUpdateDebounceTimer.resetTimer()" hideIncreaseBtn
hideIncreaseBtn> *ngIf="hero.uiActivating">
</adj-number-input> </adj-number-input>
<adj-number-input name="heroLevel" [(ngModel)]="md2Service.playerHero.level" minimum="1" <adj-number-input name="heroLevel" [(ngModel)]="hero.level" minimum="1" maximum="5"
maximum="5" title="Level" (blur)="heroUpdateDebounceTimer.resetTimer()"> title="Level" (blur)="heroUpdateDebounceTimer.resetTimer()">
</adj-number-input> </adj-number-input>
<adj-number-input name="heroExp" [(ngModel)]="md2Service.playerHero.exp" minimum="0" <adj-number-input name="heroExp" [(ngModel)]="hero.exp" minimum="0" title="Exp"
title="Exp" (blur)="heroUpdateDebounceTimer.resetTimer()"> (blur)="heroUpdateDebounceTimer.resetTimer()">
</adj-number-input> </adj-number-input>
<adj-number-input name="heroRage" [(ngModel)]="md2Service.playerHero.rage" minimum="0" <adj-number-input name="heroRage" [(ngModel)]="hero.rage" minimum="0" maximum="7"
maximum="7"
title="{{iconHtml(MD2Icon.Rage,'g-color-google-plus mr-1 g-font-size-18')}}Rage" title="{{iconHtml(MD2Icon.Rage,'g-color-google-plus mr-1 g-font-size-18')}}Rage"
(blur)="heroUpdateDebounceTimer.resetTimer()" (blur)="heroUpdateDebounceTimer.resetTimer()" *ngIf="hero.class==HeroClass.Berserker">
*ngIf="md2Service.playerHero.class==HeroClass.Berserker">
</adj-number-input> </adj-number-input>
<adj-number-input name="heroCorruption" [(ngModel)]="md2Service.playerHero.corruptionToken" <adj-number-input name="heroCorruption" [(ngModel)]="hero.corruptionToken" minimum="0"
minimum="0" title="{{imgHtml('Tokens/CorruptToken.png','g-height-18')}} Corruption" title="{{imgHtml('Tokens/CorruptToken.png','g-height-18')}} Corruption"
(blur)="heroUpdateDebounceTimer.resetTimer()" (blur)="heroUpdateDebounceTimer.resetTimer()" *ngIf="hero.uiShowCorruptionToken">
*ngIf="md2Service.playerHero.corruptionToken>0">
</adj-number-input> </adj-number-input>
</div> </div>
</div> </div>
<div *ngIf="md2Service.info.isBossFight"></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()" <button nbButton hero class="mr-2" status="info" (click)="moveAction()"
*ngIf="!showMoveAction">Move</button> *ngIf="!showMoveAction">Move</button>
<button nbButton hero class="mr-2" status="info" (click)="moveActionEnd()" <button nbButton hero class="mr-2" status="info" (click)="moveActionEnd()"
@ -109,8 +105,14 @@
</div> </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 <button nbButton hero status="info" class="mt-2" (click)="openDoor()" *ngIf="showMoveAction">Open
Door</button> Door</button>
<button nbButton hero fullWidth status="warning" class="mt-3" *ngIf="hero.uiActivating"
(click)="endActivation()">End
Activation</button>
</nb-card-body> </nb-card-body>
</nb-card> </nb-card>
@ -121,7 +123,7 @@
</div> </div>
</div> </div>
<!-- <nb-flip-card *ngIf="md2Service.playerHero"> <!-- <nb-flip-card *ngIf="hero">
<nb-card-front> <nb-card-front>
<nb-card> <nb-card>

View File

@ -8,6 +8,7 @@ import { MD2Service } from '../../../services/MD2/md2.service';
import { MsgBoxService } from '../../../services/msg-box.service'; import { MsgBoxService } from '../../../services/msg-box.service';
import { StateService } from '../../../services/state.service'; import { StateService } from '../../../services/state.service';
import { ADButtonColor, ADButtons } from '../../../ui/alert-dlg/alert-dlg.model'; import { ADButtonColor, ADButtons } from '../../../ui/alert-dlg/alert-dlg.model';
import { ArrayUtils } from '../../../utilities/array-utils';
import { StringUtils } from '../../../utilities/string-utils'; import { StringUtils } from '../../../utilities/string-utils';
import { DebounceTimer } from '../../../utilities/timer-utils'; import { DebounceTimer } from '../../../utilities/timer-utils';
import { HeroClass, MD2HeroInfo } from '../massive-darkness2.model'; 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.Rogue, 'Rogue'),
new DropDownOption(HeroClass.Wizard, 'Wizard'), new DropDownOption(HeroClass.Wizard, 'Wizard'),
new DropDownOption(HeroClass.Shaman, 'Shaman'), new DropDownOption(HeroClass.Shaman, 'Shaman'),
new DropDownOption(HeroClass.Druid, 'Druid'),
]; ];
heros = [] as MD2HeroInfo[]; heros = [] as MD2HeroInfo[];
wizards: MD2HeroInfo[] = [ wizards: MD2HeroInfo[] = [
@ -76,7 +78,7 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
} }
public get allowAttack(): boolean { 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 { ngOnInit(): void {
@ -130,7 +132,7 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
class: heroClass 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); this.showHeroList(heroClass, 0);
}); });
} }
@ -157,16 +159,16 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
} }
broadcastHeroInfo() { broadcastHeroInfo() {
this.md2Service.broadcastMyHeroInfo(); this.md2Service.broadcastService.broadcastMyHeroInfo();
this.heroUpdateDebounceTimer.clearOut(); this.heroUpdateDebounceTimer.clearOut();
} }
increaseRage() { increaseRage() {
if (this.md2Service.playerHero.rage < 7) { if (this.hero.rage < 7) {
this.md2Service.playerHero.rage++; this.hero.rage++;
} }
} }
openDoor() { openDoor() {
this.md2Service.broadcastHeroAction('openDoor'); this.md2Service.broadcastService.broadcastHeroAction('openDoor');
this.showMoveAction = false; this.showMoveAction = false;
this.detectChanges(); this.detectChanges();
} }
@ -183,17 +185,19 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
this.showMoveAction = false; this.showMoveAction = false;
switch (action) { switch (action) {
case 'recoveryAction': 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; break;
default: default:
break; break;
} }
this.md2Service.broadcastHeroAction(action); this.md2Service.broadcastService.broadcastHeroAction(action);
this.reduceAction(); this.reduceAction();
} }
reduceAction() { reduceAction() {
this.md2Service.playerHero.remainActions -= 1; this.hero.remainActions -= 1;
this.detectChanges(); this.detectChanges();
this.broadcastHeroInfo(); this.broadcastHeroInfo();
} }
@ -202,4 +206,30 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
toggleFlip() { toggleFlip() {
this.flip = (this.flip == 'inactive') ? 'active' : 'inactive'; 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();
}
}
} }

View File

@ -24,7 +24,7 @@
</nb-card-header> </nb-card-header>
<nb-card-body> <nb-card-body>
<div class="row" *ngIf="md2Service.heros.length==0"> <div class="row" *ngIf="md2Service.heros.length==0&& false">
<div class="col-6"> <div class="col-6">
<adj-number-input name="heroLevel" [(ngModel)]="md2Service.playerAmount" [maximum]="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-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-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-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 <span class="badge badge-success mr-1" *ngIf="hero.remainActions>0">Remain
Actions: {{hero.remainActions}}</span> 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> --> <!-- <span class="badge badge-success mr-1">{{hero.playerInfo.signalRClientId}}</span> -->
</div> </div>

View File

@ -15,6 +15,7 @@ import { QRCodeService } from '../../services/qrcode.service';
import { StringUtils } from '../../utilities/string-utils'; import { StringUtils } from '../../utilities/string-utils';
import { SpawnMobDlgComponent } from './mobs/spawn-mob-dlg/spawn-mob-dlg.component'; import { SpawnMobDlgComponent } from './mobs/spawn-mob-dlg/spawn-mob-dlg.component';
import { BossMicheal } from './massive-darkness2.model.boss'; import { BossMicheal } from './massive-darkness2.model.boss';
import { MD2InitService } from '../../services/MD2/md2-init.service';
@Component({ @Component({
selector: 'ngx-massive-darkness2', selector: 'ngx-massive-darkness2',
@ -26,6 +27,7 @@ export class MassiveDarkness2Component extends MD2Base implements OnInit {
HeroClass: HeroClass HeroClass: HeroClass
constructor( constructor(
private fileService: FileService, private fileService: FileService,
private initService: MD2InitService,
private msgBoxService: MsgBoxService, private msgBoxService: MsgBoxService,
private qrCodeService: QRCodeService, private qrCodeService: QRCodeService,
public gameRoomService: GameRoomService, public gameRoomService: GameRoomService,
@ -42,7 +44,8 @@ export class MassiveDarkness2Component extends MD2Base implements OnInit {
this.md2Service.enemyPhaseSubject.pipe(takeUntil(this.destroy$)).subscribe(result => { this.md2Service.enemyPhaseSubject.pipe(takeUntil(this.destroy$)).subscribe(result => {
this.showEnemyPhaseAction(0); this.showEnemyPhaseAction(0);
}); });
this.initService.initMobDecks();
this.initService.initTreasureBag();
} }
override signalRInitialized() { override signalRInitialized() {
@ -64,7 +67,7 @@ export class MassiveDarkness2Component extends MD2Base implements OnInit {
} }
showEnemyPhaseAction(index: number) { 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 enemyInfo = `<img src="${mob.imageUrl}" class='g-height-70vh'><br>`;
let extraRule = ''; let extraRule = '';
@ -85,7 +88,13 @@ export class MassiveDarkness2Component extends MD2Base implements OnInit {
// break; // 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 => { .onClose.pipe(first()).subscribe(result => {
index++; index++;
if (index < this.md2Service.enemyPhaseMobs.length) { if (index < this.md2Service.enemyPhaseMobs.length) {
@ -125,10 +134,7 @@ export class MassiveDarkness2Component extends MD2Base implements OnInit {
} }
enterBossFight() { enterBossFight() {
this.msgBoxService.showInputbox('Boss Fight', 'Choose the boss').pipe(first()).subscribe(result => { this.md2Service.enterBossFight();
this.md2Service.info.isBossFight = true;
this.md2Service.info.boss = new BossMicheal(this.md2Service);
this.detectChanges();
});
} }
} }

View 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})`
}
}

View File

@ -1,5 +1,5 @@
import { stringify } from "querystring" import { stringify } from "querystring"
import { Subject } from "rxjs" import { Observable, Subject, Subscription } from "rxjs"
import { first } from "rxjs/operators" import { first } from "rxjs/operators"
import { MD2Service } from "../../services/MD2/md2.service" import { MD2Service } from "../../services/MD2/md2.service"
import { StringUtils } from "../../utilities/string-utils" import { StringUtils } from "../../utilities/string-utils"
@ -8,121 +8,6 @@ import { TreasureType, AttackInfo, DefenseInfo, AttackType, MD2Icon, MD2HeroInfo
import { RollingBlackDice } from "./massive-darkness2.model.dice" import { RollingBlackDice } from "./massive-darkness2.model.dice"
export interface IBossFight {
name: string
addTreasureToken: Subject<TreasureType>
spawnMob: Subject<void>
spawnRoamingMonster: Subject<void>
rounds: number
actions: number
hpPerHero: number
info: MobInfo
actionBlackDice: number
imgUrl: string
standUrl: string
activating(): boolean
prepareForBossFight(): void
nextRound(): void
}
export class BossMicheal implements IBossFight {
constructor(private md2Service: 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.hpPerHero = 15;
this.info = new MobInfo({
description: this.name,
type: MobType.Boss,
hp: this.md2Service.heros.length * this.hpPerHero,
unitRemainHp: this.md2Service.heros.length * this.hpPerHero,
level: 10,
combatSkill: new MobSkill(`Combat 1 ${this.md2Service.stateService.iconHtml(MD2Icon.EnemySkill)}`,
`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.rounds = 0;
this.actionBlackDice = 2;
}
name: string
addTreasureToken: Subject<TreasureType>
spawnMob: Subject<void>
spawnRoamingMonster: Subject<void>
rounds: number
actions: number
hpPerHero: number
info: MobInfo
actionBlackDice: number
imgUrl: string
standUrl: string
corruptionTokenHtml: string
activating(): boolean {
let actionResult = new RollingBlackDice().roll(this.actionBlackDice);
let actionHtml = '';
let beenAttackedHero = [] as MD2HeroInfo[];
let bossAction: MobSkill;
switch (actionResult.claws) {
case 0:
//Justice From Above
beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.MostCorruption, true);
bossAction = new MobSkill('Justice From Above',
`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.`);
break;
case 2:
//Dark Blessing
bossAction = new MobSkill('Dark Blessing',
`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 true;
}
prepareForBossFight(): void {
}
darkBlessingCorruptionAmt: number = 1;
nextRound(): void {
this.rounds++;
switch (this.rounds) {
case 3:
case 5:
this.darkBlessingCorruptionAmt++;
break;
case 2:
case 4:
this.info.defenseInfo[0].black += 1;
this.info.attackInfos[0].black += 1;
break;
// case 4:
// this.defInfo.black += 2;
// this.atkInfos[0].black += 2;
// break;
default:
break;
}
}
}
export enum MobSkillType { export enum MobSkillType {
Attack, Attack,
Defense, Defense,
@ -147,3 +32,286 @@ export class MobSkill {
description: string description: string
targetHeros: MD2HeroInfo[] targetHeros: MD2HeroInfo[]
} }
export interface IBossFight {
name: string
addTreasureToken: Subject<TreasureType>
spawnMob: Subject<void>
spawnRoamingMonster: Subject<void>
rounds: number
actions: number
activatedTimes: number
info: MobInfo
actionBlackDice: number
imgUrl: string
standUrl: string
extraRules: string
activating(): boolean
prepareForBossFight(): void
darknessPhase(): void
}
export abstract class BossFight implements IBossFight {
name: string
addTreasureToken: Subject<TreasureType>
spawnMob: Subject<void>
spawnRoamingMonster: Subject<void>
rounds: number
actions: number
activatedTimes: number
info: MobInfo
actionBlackDice: number
imgUrl: string
standUrl: string
extraRules: string
protected subscription: Subscription
constructor(protected md2Service: MD2Service) {
this.rounds = 1;
}
activating(): boolean {
this.activatedTimes = this.actions;
this.runAction();
return true;
}
runAction() {
this.bossAction().pipe(first()).subscribe(result => {
this.activatedTimes--;
if (this.activatedTimes) {
this.runAction();
} else {
if (false == this.md2Service.heros.some(h => h.remainActions > 0)) {
this.md2Service.darknessPhase();
}
}
});
}
abstract bossAction(): Observable<boolean>;
protected actionEnd
prepareForBossFight(): void {
throw new Error("Method not implemented.")
}
darknessPhase(): void {
throw new Error("Method not implemented.")
}
}
export class BossMicheal extends BossFight {
constructor(protected md2Service: MD2Service) {
super(md2Service);
this.corruptionTokenHtml = this.md2Service.stateService.imgHtml('Tokens/CorruptToken.png');
this.name = 'Michael - The Corrupted Archangel';
this.imgUrl = md2Service.stateService.imgUrl('/Boss/Michael - The Corrupted Archangel.jpg');
this.standUrl = md2Service.stateService.imgUrl('/Boss/Michael.png');
this.info = new MobInfo({
description: this.name,
type: MobType.Boss,
hpPerHero: 15,
level: 10,
combatSkill: new MobSkill(
{
name: `Combat 1 ${this.md2Service.stateService.iconHtml(MD2Icon.EnemySkill)}`,
description: `Deal 1 Wound for each ${this.corruptionTokenHtml} on the attacking or defending Hero. Discard the tokens afterwards(once per combat).`
}),
imageUrl: md2Service.stateService.imgUrl('/Boss/Michael.png')
});
this.info.defenseInfo = new DefenseInfo(5, 1);
this.info.attackInfos = [new AttackInfo(MD2Icon.Melee, 2, 2, 0, 1)];
this.actions = 1;
this.actionBlackDice = 2;
this.extraRules = `Archangel Michael cant be the target of any attack, skill, ability or take Wounds until there are no Corruption tokens in the whole Tile.<br><br>` +
`Any Hero on a Zone with a ${this.corruptionTokenHtml} may spend 1 action to remove it. Each time a Hero removes a ${this.corruptionTokenHtml} from a Zone they must roll 1 ${this.md2Service.stateService.iconHtml(MD2Icon.BlackDice)}.` +
`If ${this.md2Service.stateService.iconHtml(MD2Icon.EnemyClaw)} the Hero takes 1 Wound.<br>If ${this.md2Service.stateService.iconHtml(MD2Icon.EnemySkill)} place 1 ${this.corruptionTokenHtml} on their Dashboard.<br>` +
`If ${this.md2Service.stateService.iconHtml(MD2Icon.EnemyClaw)}/${this.md2Service.stateService.iconHtml(MD2Icon.EnemySkill)} the Hero takes 1 Wound and places 1 ${this.corruptionTokenHtml} on their Dashboard.`
}
activatedTimes: number
acted: number
name: string
addTreasureToken: Subject<TreasureType>
spawnMob: Subject<void>
spawnRoamingMonster: Subject<void>
rounds: number
actions: number
info: MobInfo
actionBlackDice: number
imgUrl: string
standUrl: string
corruptionTokenHtml: string
bossAction(): Observable<boolean> {
let actionResult = new RollingBlackDice().roll(this.actionBlackDice);
let actionHtml = '';
let beenAttackedHero = [] as MD2HeroInfo[];
let bossAction: MobSkill;
switch (actionResult.claws) {
case 0:
//Justice From Above
beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.MostCorruption, true);
bossAction = new MobSkill(
{
name: 'Justice From Above',
description: `Place Michael in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.`
});
break;
case 1:
//Lance Dash
beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.LeastCorruption, true);
bossAction = new MobSkill({
name: 'Lance Dash',
description:
`Move Michael and Place 1 ${this.corruptionTokenHtml} in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.`
});
break;
case 2:
//Dark Blessing
bossAction = new MobSkill({
name: 'Dark Blessing',
description:
`Place Michael on the central Zone and add 1 ${this.corruptionTokenHtml} to the Corruption Stone Zone with the least amount of ${this.corruptionTokenHtml}.<br>` +
`Deal <b>${this.darkBlessingCorruptionAmt}</b> Wounds per ${this.corruptionTokenHtml} to all Heros in each Tiles <b>distributed as they wish</b>.`
});
break;
default:
break;
}
return this.md2Service.dlgService.open(BossActivationComponent, { context: { boss: this, bossAction: bossAction, currentAction: this.activatedTimes, allActions: this.actions } }).onClose;
}
prepareForBossFight(): void {
this.md2Service.heros.forEach(hero => {
hero.uiShowCorruptionToken = true;
});
this.md2Service.msgBoxService.show('Prepare Boss Fight', {
text: `<h6>Place ${this.md2Service.heros.length * 2} ${this.corruptionTokenHtml} on the Corruption Stone Zones (Shadow
Zones).<br>Players choose the Zones, but must distribute
the tokens as equally as possible among them.</h6>`
});
}
darkBlessingCorruptionAmt: number = 1;
darknessPhase(): void {
this.rounds++;
switch (this.rounds) {
case 3:
case 5:
this.darkBlessingCorruptionAmt++;
break;
case 2:
case 4:
this.info.defenseInfo.black += 1;
this.info.attackInfos[0].black += 1;
break;
// case 4:
// this.defInfo.black += 2;
// this.atkInfos[0].black += 2;
// break;
default:
break;
}
}
}
export class BossReaper extends BossFight {
constructor(protected md2Service: MD2Service) {
super(md2Service);
this.timeTokenHtml = this.md2Service.stateService.imgHtml('Tokens/TimeToken.png');
this.name = 'The Reaper';
this.imgUrl = md2Service.stateService.imgUrl('/Boss/The Reaper.jpg');
this.standUrl = md2Service.stateService.imgUrl('/Boss/The Reaper-Stand.png');
this.info = new MobInfo({
description: this.name,
type: MobType.Boss,
hpPerHero: 25,
level: 10,
combatSkill: new MobSkill(
{
description: `If the Hero has no ${this.md2Service.stateService.iconHtml(MD2Icon.Mana)}, they take 1 ${this.md2Service.stateService.iconHtml(MD2Icon.Frost)}`,
type: MobSkillType.Attack,
}),
imageUrl: md2Service.stateService.imgUrl('/Boss/The Reaper-Stand.png')
});
this.info.defenseInfo = new DefenseInfo(4, 3);
this.info.attackInfos = [new AttackInfo(MD2Icon.Melee, 1, 2, 0, 3)];
this.actions = 1;
this.actionBlackDice = 2;
this.extraRules = `A Hero standing in the Hourglass Zone may spend 1 action to add 1 ${this.timeTokenHtml} in each Hourglass Zone.`;
}
name: string
addTreasureToken: Subject<TreasureType>
spawnMob: Subject<void>
spawnRoamingMonster: Subject<void>
rounds: number
actions: number
info: MobInfo
actionBlackDice: number
imgUrl: string
standUrl: string
timeTokenHtml: string
bossAction() {
let actionResult = new RollingBlackDice().roll(this.actionBlackDice);
let actionHtml = '';
let beenAttackedHero = [] as MD2HeroInfo[];
let bossAction: MobSkill;
switch (actionResult.claws) {
case 0:
//Justice From Above
beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.LeastMp, true);
bossAction = new MobSkill(
{
name: 'Soul Drain',
description: `Place The Reaper in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.`
});
break;
case 1:
//Lance Dash
beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.LeastCorruption, true);
bossAction = new MobSkill({
name: 'Time Ticking',
description:
`Place The Reaper in the <b>Hourglass Zone</b> withe the least ${this.timeTokenHtml} and remove 1 ${this.timeTokenHtml} from <b>The OtherHourglass Zone</b>.`
});
break;
case 2:
//Dark Blessing
bossAction = new MobSkill({
name: 'Death Is Coming',
description:
`Place The Reaper in the central Zone.<br>` +
`Roll 1 ${this.md2Service.stateService.iconHtml(MD2Icon.YellowDice)}. Remove ${this.timeTokenHtml} equal to ${this.md2Service.stateService.iconHtml(MD2Icon.Melee)} rolled from both <b>Hourglass Zone</b>.<br>` +
`Each Hero discards ${this.md2Service.stateService.iconHtml(MD2Icon.MP)} equal to ${this.md2Service.stateService.iconHtml(MD2Icon.Melee)} rolled.`
});
break;
default:
break;
}
return this.md2Service.dlgService.open(BossActivationComponent, { context: { boss: this, bossAction: bossAction, currentAction: this.activatedTimes, allActions: this.actions } }).onClose;
}
prepareForBossFight(): void {
this.md2Service.msgBoxService.show('Prepare Boss Fight', {
text: `<h6>Place 2 ${this.timeTokenHtml} in each Hourglass Zone.</h6>`
})
}
darkBlessingCorruptionAmt: number = 1;
darknessPhase(): void {
this.rounds++;
if (this.rounds > 4) {
this.actions = 3;
} else if (this.rounds > 1) {
this.actions = 2;
}
}
}

View File

@ -1,7 +1,9 @@
import { Subject } from "rxjs"; import { Observable, Subject } from "rxjs";
import { environment } from "../../../environments/environment"; import { environment } from "../../../environments/environment";
import { MsgBoxService } from "../../services/msg-box.service";
import { ObjectUtils } from "../../utilities/object-utils"; import { ObjectUtils } from "../../utilities/object-utils";
import { GamePlayer } from "../games.model"; import { GamePlayer } from "../games.model";
import { MD2Clone } from "./factorys/md2-clone";
import { MobSkill } from "./massive-darkness2.model.boss"; import { MobSkill } from "./massive-darkness2.model.boss";
const MD2_IMG_URL = (id: string = null) => { return `${environment.apiUrl}/Files/Images/MD2/${(id ? `${encodeURI(id)}` : '')}` } const MD2_IMG_URL = (id: string = null) => { return `${environment.apiUrl}/Files/Images/MD2/${(id ? `${encodeURI(id)}` : '')}` }
@ -9,7 +11,8 @@ export enum MobDlgType {
Spawn, Spawn,
Activating, Activating,
BeenAttacked, BeenAttacked,
PreView PreView,
Dashboard
} }
export enum RoundPhase { export enum RoundPhase {
HeroPhase, HeroPhase,
@ -31,6 +34,7 @@ export enum HeroClass {
Ranger, Ranger,
Shaman, Shaman,
Paladin, Paladin,
Druid,
} }
export enum MobType { export enum MobType {
Mob, Mob,
@ -68,14 +72,16 @@ export enum MD2Icon {
RedDice, RedDice,
BlueDice, BlueDice,
YellowDice, YellowDice,
OrangeDice OrangeDice,
BlackDice
} }
export enum AttackTarget { export enum AttackTarget {
Random = 40, Random = 40,
LowestHp = 50, LeastHp = 50,
HighestHp = 60, LeastMp = 60,
HighestMp = 70, HighestHp = 70,
LowestLevel = 80, HighestMp = 80,
LowestLevel = 90,
MostCorruption = 200, MostCorruption = 200,
LeastCorruption = 201 LeastCorruption = 201
} }
@ -132,9 +138,13 @@ export class DrawingBag<T extends IDrawingItem> {
} }
drawingItems: IDrawingItem[] drawingItems: IDrawingItem[]
removedItems: IDrawingItem[] removedItems: IDrawingItem[]
public bagIsEmpty() { 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; return this.drawingItems.reduce((sum, current) => sum + current.drawingWeight, 0) == 0;
} }
}
public Draw(amount: number): T[] { public Draw(amount: number): T[] {
let drawItems: T[] = this.DrawAndRemove(amount); let drawItems: T[] = this.DrawAndRemove(amount);
@ -144,7 +154,7 @@ export class DrawingBag<T extends IDrawingItem> {
public DrawAndRemove(amount: number = 1, predicate: (value: T) => boolean = undefined): T[] { public DrawAndRemove(amount: number = 1, predicate: (value: T) => boolean = undefined): T[] {
let drawItems: T[] = []; let drawItems: T[] = [];
for (let i = 0; i < amount; i++) { for (let i = 0; i < amount; i++) {
if (!this.bagIsEmpty()) { if (!this.bagIsEmpty(predicate)) {
let drawItem = null as T; let drawItem = null as T;
let drawingPool = [] as T[]; let drawingPool = [] as T[];
if (predicate) { if (predicate) {
@ -159,7 +169,7 @@ export class DrawingBag<T extends IDrawingItem> {
const item = drawingPool[i]; const item = drawingPool[i];
drawCalc += item.drawingWeight; drawCalc += item.drawingWeight;
if (drawCalc >= drawIndex) { if (drawCalc >= drawIndex) {
drawItem = ObjectUtils.CloneValue(item); drawItem = MD2Clone.CloneDrawingItem(item);
drawItem.drawingWeight = 1; drawItem.drawingWeight = 1;
break; break;
} }
@ -183,7 +193,7 @@ export class DrawingBag<T extends IDrawingItem> {
this.removedItems = []; this.removedItems = [];
} }
public AddItem(item: IDrawingItem) { 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) { if (existingItem) {
existingItem.drawingWeight += item.drawingWeight; existingItem.drawingWeight += item.drawingWeight;
} else { } else {
@ -193,6 +203,20 @@ export class DrawingBag<T extends IDrawingItem> {
public RemoveItem(item: IDrawingItem) { public RemoveItem(item: IDrawingItem) {
if (item) { if (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); let existingItem = this.drawingItems.find(i => i.name == item.name);
if (existingItem) { if (existingItem) {
existingItem.drawingWeight -= item.drawingWeight; existingItem.drawingWeight -= item.drawingWeight;
@ -204,6 +228,9 @@ export class DrawingBag<T extends IDrawingItem> {
this.removedItems.push(item); this.removedItems.push(item);
} }
} }
}
} }
} }
@ -214,11 +241,13 @@ export class DrawingBag<T extends IDrawingItem> {
} }
} }
export interface IMobFactory { export interface IMobFactory {
mobName: string;
generate(level: number): MobInfo; generate(level: number): MobInfo;
} }
export interface IDrawingItem { export interface IDrawingItem {
imageUrl: string imageUrl: string
name: string name: string
get identifyName(): string
description: string description: string
drawingWeight: number drawingWeight: number
} }
@ -234,6 +263,9 @@ export class DrawingItem implements IDrawingItem {
this.description = description this.description = description
this.drawingWeight = drawingWeight this.drawingWeight = drawingWeight
} }
get identifyName(): string {
return this.name;
}
imageUrl: string imageUrl: string
name: string name: string
description: string description: string
@ -249,6 +281,9 @@ export class TreasureItem extends DrawingItem {
} }
type: TreasureType; type: TreasureType;
itemAmount: number; itemAmount: number;
get identifyName(): string {
return this.name;
}
} }
export class MobInfo implements IDrawingItem { export class MobInfo implements IDrawingItem {
constructor( constructor(
@ -270,13 +305,15 @@ export class MobInfo implements IDrawingItem {
level: number; level: number;
rewardTokens: number; rewardTokens: number;
hp: number; hp: number;
hpPerHero: number;
mobAmount: number; mobAmount: number;
carriedTreasure: TreasureItem[]; carriedTreasure: TreasureItem[];
fixedCarriedTreasure: TreasureItem[]; fixedCarriedTreasure: TreasureItem[];
unitRemainHp: number; unitRemainHp: number;
attackInfos: AttackInfo[]; attackInfos: AttackInfo[];
defenseInfo: DefenseInfo; defenseInfo: DefenseInfo;
actions: number = 0;
activateDescription: string;
combatSkill: MobSkill combatSkill: MobSkill
fireToken: number = 0; fireToken: number = 0;
@ -288,6 +325,9 @@ export class MobInfo implements IDrawingItem {
uiCorruptionTokens: number; uiCorruptionTokens: number;
uiAttackedBy: string; uiAttackedBy: string;
get identifyName(): string {
return `${this.name}_${this.level}`;
}
public get carriedTreasureHtml(): string { public get carriedTreasureHtml(): string {
if (!this.carriedTreasure) return ''; if (!this.carriedTreasure) return '';
return this.carriedTreasure.map(i => `<img src="${i.imageUrl}" class='mr-1' width="40px">`) return this.carriedTreasure.map(i => `<img src="${i.imageUrl}" class='mr-1' width="40px">`)
@ -327,7 +367,8 @@ export class MobInfo implements IDrawingItem {
return 0; return 0;
} }
} }
public actionSubject: Subject<string>;
public activateFunction?: (mob: MobInfo, msgBoxService: MsgBoxService, heros: MD2HeroInfo[]) => void;
} }
export class MD2HeroInfo { export class MD2HeroInfo {
@ -354,10 +395,12 @@ export class MD2HeroInfo {
shadowSkillHtml: string; shadowSkillHtml: string;
remainActions: number = 3; remainActions: number = 3;
rage: number = 0; rage: number = 0;
uiActivating = false;
uiShowCorruptionToken = false;
uiBossFight = false;
public get heroFullName(): string { public get heroFullName(): string {
return `${this.playerInfo.name} (${HeroClass[this.class]} - ${this.name}` return `${this.playerInfo.name} (${HeroClass[this.class]} - ${this.name})`
} }
} }
@ -403,6 +446,9 @@ export class MD2EnemyPhaseSpecialRule implements IDrawingItem {
this.title = title this.title = title
this.description = description this.description = description
} }
get identifyName(): string {
return this.name;
}
imageUrl: string; imageUrl: string;
name: string; name: string;
drawingWeight: number; drawingWeight: number;

View File

@ -1 +1 @@
<span class="MD2Icon {{icon}} {{iconClass}} {{sizeClass}}"></span> <span class="MD2Icon {{iconName}} {{iconClass}} {{sizeClass}}"></span>

View File

@ -10,10 +10,26 @@ export class MD2IconComponent implements OnInit {
@Input() iconClass: string = 'mr-1'; @Input() iconClass: string = 'mr-1';
@Input("icon") icon: string;
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'; @Input() size: string = 'sm';
iconName: string;
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {

View File

@ -1,7 +1,8 @@
<label class='label'>Weapon Info</label> <!-- <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"> <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"> <div class="col-md-4">
<md2-icon icon="attack" size="lg"></md2-icon> <md2-icon [icon]="info.type" size="lg"></md2-icon>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<div *ngIf="info.yellow" class="g-height-45"> <div *ngIf="info.yellow" class="g-height-45">
@ -26,3 +27,4 @@
</div> </div>
</div> </div>
</div> </div>
</ng-container>

View File

@ -1,5 +1,5 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { MD2Icon, MobDlgType, MobInfo } from '../../../massive-darkness2.model'; import { MD2Icon, MobDlgType, MobInfo, MobType } from '../../../massive-darkness2.model';
@Component({ @Component({
selector: 'md2-mob-attack-info', selector: 'md2-mob-attack-info',
@ -22,9 +22,23 @@ export class MobAttackInfoComponent implements OnInit {
} }
@Input() mode: MobDlgType = MobDlgType.PreView; @Input() mode: MobDlgType = MobDlgType.PreView;
display: boolean = false;
constructor() { } constructor() { }
ngOnInit(): void { 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;
}
} }
} }

View File

@ -1,5 +1,24 @@
<div class='form-group row' *ngIf="showSkill"> <!-- <div class='form-group row' *ngIf="showSkill">
<label class='label col-sm-3 form-control-label MD2text g-font-size-30' <label class='label col-sm-3 form-control-label MD2text g-font-size-30'>
[innerHtml]="mob.combatSkill.skillName"></label> {{mob.combatSkill.skillRoll}} <md2-icon icon="enemySkill" size="md"></md2-icon>
<div class='col-sm' [innerHtml]="mob.combatSkill.skillDescription"></div> </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> </div>

View File

@ -17,3 +17,12 @@
left: 26px; left: 26px;
font-size: 30px; font-size: 30px;
} }
.skillDesc {
padding-left: 8px;
.MD2Icon {
font-size: 45px;
}
}
.skillDesc .MD2Icon {
font-size: 45px;
}

View File

@ -1,5 +1,5 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { MD2Icon, MobDlgType, MobInfo } from '../../../massive-darkness2.model'; import { MD2Icon, MobDlgType, MobInfo, MobType } from '../../../massive-darkness2.model';
import { MobSkillType } from '../../../massive-darkness2.model.boss'; import { MobSkillType } from '../../../massive-darkness2.model.boss';
@Component({ @Component({
@ -22,6 +22,9 @@ export class MobCombatInfoComponent implements OnInit {
} }
} }
@Input() mode: MobDlgType = MobDlgType.PreView; @Input() mode: MobDlgType = MobDlgType.PreView;
showSkill: boolean = false;
showBlackDice: boolean
skillTriggerHtml: string = '';
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {
@ -34,14 +37,17 @@ export class MobCombatInfoComponent implements OnInit {
this.showSkill = [MobSkillType.Combat, MobSkillType.Defense].includes(this.mob.combatSkill.type); this.showSkill = [MobSkillType.Combat, MobSkillType.Defense].includes(this.mob.combatSkill.type);
break; break;
case MobDlgType.PreView: case MobDlgType.PreView:
case MobDlgType.Spawn:
default:
this.showSkill = true; this.showSkill = true;
break; 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;;
}
} }
showSkill: boolean = false;;
}

View File

@ -1,4 +1,5 @@
<label class='label'>Defense Info</label> <!-- <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="g-brd-3 g-brd-bottom--dashed g-brd-gray-light-v2 mb-3 mt-2 row">
<div class="col-md-4"> <div class="col-md-4">
<md2-icon icon="defense" size="lg"></md2-icon> <md2-icon icon="defense" size="lg"></md2-icon>
@ -16,3 +17,4 @@
</div> </div>
</div> </div>
</div> </div>
</ng-container>

View File

@ -1,5 +1,6 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { MD2Icon, MobInfo, MobDlgType } from '../../../massive-darkness2.model'; import { MD2Icon, MobInfo, MobDlgType } from '../../../massive-darkness2.model';
import { MobSkillType } from '../../../massive-darkness2.model.boss';
@Component({ @Component({
selector: 'md2-mob-def-info', selector: 'md2-mob-def-info',
@ -21,8 +22,22 @@ export class MobDefInfoComponent implements OnInit {
} }
} }
@Input() mode: MobDlgType = MobDlgType.PreView; @Input() mode: MobDlgType = MobDlgType.PreView;
display: boolean = false;
constructor() { } constructor() { }
ngOnInit(): void { 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;
}
} }
} }

View File

@ -1,27 +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 g-text-nowrap'>Level <b class="MD2text g-font-size-18">{{mob.level}}</b></label><br>
<label class='label'>Alive Units <b class="MD2text g-font-size-18">{{mob.mobAmount}}</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> </ng-container>
<label class='label'>Total HP <b class="MD2text g-font-size-18">{{mob.totalHp}}</b></label><br>
<ng-container *ngIf="mob.carriedTreasureHtml"> <ng-container *ngIf="mob.carriedTreasureHtml">
<label class='label'>Carried Treasure</label><br> <label class='label'>Carried Treasure</label><br>
<div [innerHtml]="mob.carriedTreasureHtml"></div> <div [innerHtml]="mob.carriedTreasureHtml"></div>
</ng-container> </ng-container>
<md2-mob-attack-info [mob]="mob" [mode]="mode" *ngIf="mob.attackInfos&&mob.attackInfos.length>0&& !hideWeaponInfo"> <div class="g-height-20 mb-1"></div>
<md2-mob-attack-info [mob]="mob" [mode]="mode">
</md2-mob-attack-info> </md2-mob-attack-info>
<md2-mob-def-info [mob]="mob" [mode]="mode" *ngIf="mob.defenseInfo&& !hideWeaponInfo"></md2-mob-def-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> <md2-mob-combat-info [mob]="mob" [mode]="mode"></md2-mob-combat-info>
<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>

View File

@ -1,5 +1,6 @@
import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { MD2MobService } from '../../../../services/MD2/md2-mob.service';
import { MD2Service } from '../../../../services/MD2/md2.service'; import { MD2Service } from '../../../../services/MD2/md2.service';
import { StateService } from '../../../../services/state.service'; import { StateService } from '../../../../services/state.service';
import { MD2Icon, MobDlgType, MobInfo } from '../../massive-darkness2.model'; import { MD2Icon, MobDlgType, MobInfo } from '../../massive-darkness2.model';
@ -39,11 +40,9 @@ export class MobDetailInfoComponent extends MD2ComponentBase implements OnInit {
this.showAttackingInfo = typeof value !== "undefined" && value !== false; 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( constructor(
private mobService: MD2MobService,
public md2Service: MD2Service, public md2Service: MD2Service,
protected stateService: StateService, protected stateService: StateService,
protected route: ActivatedRoute, 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();
}
} }

View File

@ -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>

View File

@ -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;
}

View File

@ -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();
});
});

View File

@ -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;
}
}

View File

@ -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="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="row no-gutters">
<div class="col-12 col-md-8"> <div class="col-12 col-md-8">
<md2-mob-stand-info [mob]="mob"></md2-mob-stand-info>
<img class="mobImg g-width-95x" src="{{mob.imageUrl}}" (click)="showMobImage(mob)" />
</div> </div>
<div class=" col-12 col-md-4"> <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> </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)" <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'> <!-- <div class='form-group'>
<label for='playerAmount' class='label'>Current Unit HP</label> <label for='playerAmount' class='label'>Current Unit HP</label>
<div> <div>

View File

@ -18,83 +18,6 @@ $mob-card-height: 556px;
/* right: auto; */ /* right: auto; */
z-index: 99; 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 { #clip {
position: absolute; position: absolute;

View File

@ -1,13 +1,16 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { NbDialogService } from '@nebular/theme'; import { NbDialogService } from '@nebular/theme';
import { stringify } from 'querystring';
import { first } from 'rxjs/operators'; import { first } from 'rxjs/operators';
import { FileService } from '../../../services/file.service'; import { FileService } from '../../../services/file.service';
import { MD2MobService } from '../../../services/MD2/md2-mob.service';
import { MD2Service } from '../../../services/MD2/md2.service'; import { MD2Service } from '../../../services/MD2/md2.service';
import { MsgBoxService } from '../../../services/msg-box.service'; import { MsgBoxService } from '../../../services/msg-box.service';
import { StateService } from '../../../services/state.service'; import { StateService } from '../../../services/state.service';
import { ADIcon } from '../../../ui/alert-dlg/alert-dlg.model'; import { ADIcon } from '../../../ui/alert-dlg/alert-dlg.model';
import { NumberUtils } from '../../../utilities/number-utils'; import { NumberUtils } from '../../../utilities/number-utils';
import { StringUtils } from '../../../utilities/string-utils';
import { DrawingBag, DrawingItem, MD2Icon, MobDlgType, MobInfo, TreasureType } from '../massive-darkness2.model'; import { DrawingBag, DrawingItem, MD2Icon, MobDlgType, MobInfo, TreasureType } from '../massive-darkness2.model';
import { MD2Base, MD2ComponentBase } from '../MD2Base'; import { MD2Base, MD2ComponentBase } from '../MD2Base';
import { SpawnMobDlgComponent } from './spawn-mob-dlg/spawn-mob-dlg.component'; 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 fileService: FileService,
private msgBoxService: MsgBoxService, private msgBoxService: MsgBoxService,
private dlgService: NbDialogService, private dlgService: NbDialogService,
private mobService: MD2MobService,
public md2Service: MD2Service, public md2Service: MD2Service,
protected stateService: StateService, protected stateService: StateService,
protected route: ActivatedRoute, protected route: ActivatedRoute,
@ -44,6 +48,7 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.initMobDecks(); this.initMobDecks();
super.ngOnInit();
} }
@ -61,7 +66,6 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
initMobDecks() { initMobDecks() {
this.md2Service.initMobDecks();
this.mobs = []; this.mobs = [];
this.cdRef.detectChanges(); this.cdRef.detectChanges();
let spawn$ = this.isRoamingMonster ? this.md2Service.darknessPhaseRule.spawnRoamingMonster : this.md2Service.darknessPhaseRule.spawnMob; let spawn$ = this.isRoamingMonster ? this.md2Service.darknessPhaseRule.spawnRoamingMonster : this.md2Service.darknessPhaseRule.spawnMob;
@ -74,8 +78,9 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
let result = this.md2Service.spawnMob(this.isRoamingMonster); 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 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 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: result.mob } }) this.dlgService.open(SpawnMobDlgComponent, { context: { title: titleText, mode: actType, mob: mob } })
.onClose.pipe(first()).subscribe(result => { .onClose.pipe(first()).subscribe(result => {
this.afterSpawn(); this.afterSpawn();
}); });
@ -83,7 +88,7 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
} }
afterSpawn() { afterSpawn() {
this.cdRef.detectChanges(); this.cdRef.detectChanges();
this.md2Service.broadcastMobsInfo(); this.md2Service.broadcastService.broadcastMobsInfo();
if (this.showRoundMessage) { if (this.showRoundMessage) {
this.msgBoxService.show(`${NumberUtils.Ordinal(this.md2Service.info.round)} Hero Phase`, { icon: ADIcon.INFO }); this.msgBoxService.show(`${NumberUtils.Ordinal(this.md2Service.info.round)} Hero Phase`, { icon: ADIcon.INFO });
} }
@ -103,16 +108,23 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
this.cdRef.detectChanges(); this.cdRef.detectChanges();
} }
killOneUnit(mob: MobInfo) { killOneUnit(mob: MobInfo) {
let attacker = this.md2Service.currentActivateHero;
if (mob.mobAmount > 1) { if (mob.mobAmount > 1) {
mob.mobAmount--; mob.mobAmount--;
mob.unitRemainHp = mob.hp; mob.unitRemainHp = mob.hp;
if (this.attacking) { if (this.attacking) {
this.attackingAttackerExp += 1; this.attackingAttackerExp += 1;
} else { } 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 { } else {
this.mobs.splice(this.mobs.indexOf(mob), 1); this.mobs.splice(this.mobs.indexOf(mob), 1);
mob.mobAmount = 0; mob.mobAmount = 0;
@ -122,7 +134,11 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
this.attackingAttackerExp += 1; this.attackingAttackerExp += 1;
this.attackingAttackerReward = `<br> and gains ${mob.carriedTreasureHtml}`; this.attackingAttackerReward = `<br> and gains ${mob.carriedTreasureHtml}`;
} else { } 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 => {
}); });
} }
@ -148,49 +164,7 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
.onClose.pipe(first()).subscribe(mobResult => { .onClose.pipe(first()).subscribe(mobResult => {
if (mobResult) { if (mobResult) {
let attackDamage = mobResult.uiWounds; let attackDamage = mobResult.uiWounds;
if (attackDamage) { this.mobService.attackMob(mobResult, 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();
} }
}); });
@ -203,6 +177,16 @@ 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) { showMobImage(mob: MobInfo) {
this.dlgService.open(SpawnMobDlgComponent, { context: { title: `${mob.description}`, mode: MobDlgType.PreView, mob: mob } }) this.dlgService.open(SpawnMobDlgComponent, { context: { title: `${mob.description}`, mode: MobDlgType.PreView, mob: mob } })
.onClose.pipe(first()).subscribe(result => { .onClose.pipe(first()).subscribe(result => {

View File

@ -5,13 +5,21 @@
</nb-card-header> </nb-card-header>
<nb-card-body> <nb-card-body>
<div class="row no-gutters"> <div class="row no-gutters">
<div class="col-md-7"> <div class="col-md-7 g-height-90vh">
<img src="{{mob.imageUrl}}" class="g-width-90x"> <!-- <img src="{{mob.imageUrl}}" class="g-width-90x"> -->
<md2-mob-stand-info [mob]="mob" [mode]="mode"></md2-mob-stand-info>
</div> </div>
<div class="col-md-5"> <div class="col-md-5">
<md2-mob-detail-info [mob]="mob" [mode]="mode"> <md2-mob-detail-info [mob]="mob" [mode]="mode">
</md2-mob-detail-info> </md2-mob-detail-info>
<div *ngIf="actionInfoHtml">
<div class="alert alert-warning" role="alert" [innerHtml]="actionInfoHtml">
</div>
</div>
<ng-container *ngIf="mode==MobDlgType.Spawn&&mob.type==MobType.Mob"> <ng-container *ngIf="mode==MobDlgType.Spawn&&mob.type==MobType.Mob">
<div class="row form-group mt-2"> <div class="row form-group mt-2">
<div class="col-md-2"> <div class="col-md-2">
@ -71,7 +79,7 @@
<label for='damages' class='label'>Cause How Many Damages?</label> <label for='damages' class='label'>Cause How Many Damages?</label>
<input type='number' nbInput fullWidth id='damages' name='damages' [(ngModel)]='mob.uiWounds'> <input type='number' nbInput fullWidth id='damages' name='damages' [(ngModel)]='mob.uiWounds'>
</div> </div>
<div class='form-group'> <!-- <div class='form-group'>
<label for='damages' class='label'>Cause How Many <span <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> [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' <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> [innerHtml]="iconHtml(MD2Icon.Frost,'g-color-aqua mr-1 g-font-size-18')"></span>?</label>
<input type='number' nbInput fullWidth id='uiFrozenTokens' name='uiFrozenTokens' <input type='number' nbInput fullWidth id='uiFrozenTokens' name='uiFrozenTokens'
[(ngModel)]='mob.uiFrozenTokens'> [(ngModel)]='mob.uiFrozenTokens'>
</div> </div> -->
</ng-container> </ng-container>
</div> </div>

View File

@ -1,6 +1,7 @@
import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { NbDialogRef } from '@nebular/theme'; import { NbDialogRef } from '@nebular/theme';
import { first } from 'rxjs/operators';
import { FileService } from '../../../../services/file.service'; import { FileService } from '../../../../services/file.service';
import { MD2Service } from '../../../../services/MD2/md2.service'; import { MD2Service } from '../../../../services/MD2/md2.service';
import { MsgBoxService } from '../../../../services/msg-box.service'; import { MsgBoxService } from '../../../../services/msg-box.service';
@ -23,7 +24,7 @@ export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit {
titleHtml: string; titleHtml: string;
MD2Icon = MD2Icon; MD2Icon = MD2Icon;
mob: MobInfo; mob: MobInfo;
actionInfoHtml: string;
beenAttackedHero = [] as MD2HeroInfo[]; beenAttackedHero = [] as MD2HeroInfo[];
attackTarget: string; attackTarget: string;
otherAttackTarget: string; otherAttackTarget: string;
@ -41,6 +42,13 @@ export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit {
//this.mob = new MobInfo(this.mob); //this.mob = new MobInfo(this.mob);
if (this.mode == MobDlgType.Spawn && this.mob.type == MobType.Mob) { 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)]; 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.uiWounds = 0;
this.mob.uiFrozenTokens = 0; this.mob.uiFrozenTokens = 0;
@ -52,22 +60,20 @@ export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit {
this.mob.attackInfos = this.mob.attackInfos.filter(a => a.yellow > 0 || a.orange > 0 || a.red > 0); this.mob.attackInfos = this.mob.attackInfos.filter(a => a.yellow > 0 || a.orange > 0 || a.red > 0);
} }
// switch (this.mode) { switch (this.mode) {
// case MobDlgType.Spawn: case MobDlgType.Spawn:
// return 'warning'; this.mob.attackInfos = this.mob.attackInfos.filter(a => a.yellow > 0 || a.orange > 0 || a.red > 0);
// break; break;
// case MobDlgType.Activating: case MobDlgType.Activating:
// return 'danger'; break;
// break; case MobDlgType.BeenAttacked:
// case MobDlgType.BeenAttacked: break;
// return 'info'; case MobDlgType.PreView:
// break; default:
// case MobDlgType.PreView: break;
// default: }
// return '';
// break;
// }
this.md2Service.info.showAttackBtn = false; this.md2Service.info.showAttackBtn = false;
this.md2Service.refreshUI$.next();
this.dlgRef.close(this.mob); this.dlgRef.close(this.mob);
} }
cancel() { cancel() {
@ -97,7 +103,7 @@ export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit {
} }
} }
switch (targetType) { switch (targetType) {
case AttackTarget.LowestHp: case AttackTarget.LeastHp:
let lowestHp = Math.min(...this.md2Service.heros.map(h => h.hp)); let lowestHp = Math.min(...this.md2Service.heros.map(h => h.hp));
this.beenAttackedHero = this.md2Service.heros.filter(h => h.hp == lowestHp); this.beenAttackedHero = this.md2Service.heros.filter(h => h.hp == lowestHp);
//this.otherAttackTarget = 'attacking the other <b>Lowest HP</b> hero.'; //this.otherAttackTarget = 'attacking the other <b>Lowest HP</b> hero.';

View File

@ -47,7 +47,7 @@ export class TreasureBagComponent extends MD2ComponentBase implements OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
this.md2Service.initTreasureBag(); //this.md2Service.initTreasureBag();
this.detectChanges(); this.detectChanges();
} }
detectChanges() { detectChanges() {
@ -59,7 +59,7 @@ export class TreasureBagComponent extends MD2ComponentBase implements OnInit {
resetTreasureBag(showConfirmMsg = true) { resetTreasureBag(showConfirmMsg = true) {
this.msgBoxService.show("Restore Treasure Bag", { text: 'Are you sure?', icon: ADIcon.QUESTION, buttons: ADButtons.YesNo }).pipe(first()).subscribe(result => { this.msgBoxService.show("Restore Treasure Bag", { text: 'Are you sure?', icon: ADIcon.QUESTION, buttons: ADButtons.YesNo }).pipe(first()).subscribe(result => {
if (result) { if (result) {
this.md2Service.initTreasureBag(); //this.md2Service.initTreasureBag();
} }
}); });

View File

@ -23,11 +23,11 @@ export class MD2BroadcastService {
) { } ) { }
public get playerHero(): MD2HeroInfo { public get playerHero(): MD2HeroInfo {
return this.stateService.info.heros.find(h => h.playerInfo.signalRClientId == this.loginUserService.userAccess.signalRSessionId); return this.stateService.playerHero;
} }
public broadcastAllHeroInfoToAll() { public broadcastAllHeroInfoToAll() {
this.stateService.info.heros.forEach(element => { this.stateService.info.heros.forEach(element => {
this.broadcastHeroInfoToAll(element); this.broadcastHeroInfoToOwner(element);
}); });
} }
@ -55,7 +55,7 @@ export class MD2BroadcastService {
receiver: { isGroup: false, sessionId: hero.playerInfo.signalRClientId } as SignalRSession, receiver: { isGroup: false, sessionId: hero.playerInfo.signalRClientId } as SignalRSession,
from: { isGroup: true, sessionId: this.gameRoomService.gameRoomId }, from: { isGroup: true, sessionId: this.gameRoomService.gameRoomId },
actionType: 'hero', actionType: 'hero',
actionName: 'update', actionName: 'updateMyHero',
} as SignalRMessage; } as SignalRMessage;
message.parameters = { hero: JSON.stringify(hero) }; message.parameters = { hero: JSON.stringify(hero) };
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => { this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
@ -90,6 +90,12 @@ export class MD2BroadcastService {
parameters['gameInfo'] = JSON.stringify(this.stateService.info); parameters['gameInfo'] = JSON.stringify(this.stateService.info);
this.broadcastMessage('GameRoom', 'update', parameters); this.broadcastMessage('GameRoom', 'update', parameters);
} }
public broadcastRoundPhase() {
let parameters = {};
parameters['phase'] = JSON.stringify(this.stateService.info.roundPhase);
this.broadcastMessage('GameRoom', 'phase', parameters);
}
public broadcastMobsInfo() { public broadcastMobsInfo() {
let parameters = {}; let parameters = {};
parameters['roamingMonsters'] = JSON.stringify(this.stateService.info.roamingMonsters); parameters['roamingMonsters'] = JSON.stringify(this.stateService.info.roamingMonsters);

View 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));
}
}

View 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();
});
});

View 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();
}
}
}

View File

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { MD2Icon, TreasureType } from '../../games/massive-darkness2/massive-darkness2.model'; import { DrawingBag, MD2HeroInfo, MD2Icon, MobInfo, TreasureItem, TreasureType } from '../../games/massive-darkness2/massive-darkness2.model';
import { FileService } from '../file.service'; import { FileService } from '../file.service';
import { MD2GameInfo } from './md2.service'; import { MD2GameInfo } from './md2.service';
@ -12,6 +12,10 @@ export class MD2StateService {
private _playerAmount: number = 2; private _playerAmount: number = 2;
public info: MD2GameInfo; public info: MD2GameInfo;
public playerHero: MD2HeroInfo;
public mobDeck: DrawingBag<MobInfo>;
public roamingMobDeck: DrawingBag<MobInfo>;
public treasureBag: DrawingBag<TreasureItem> = new DrawingBag<TreasureItem>();
constructor( constructor(
public fileService: FileService, public fileService: FileService,

View File

@ -3,7 +3,7 @@ import { AttackInfo, AttackTarget, CoreGameDarknessPhaseRule, DefenseInfo, Drawi
import { first, map, reduce } from "rxjs/operators"; import { first, map, reduce } from "rxjs/operators";
import { NbDialogService } from '@nebular/theme'; import { NbDialogService } from '@nebular/theme';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { IBossFight } from '../../games/massive-darkness2/massive-darkness2.model.boss'; import { BossMicheal, BossReaper, IBossFight } from '../../games/massive-darkness2/massive-darkness2.model.boss';
import { ADIcon, MessageBoxConfig } from '../../ui/alert-dlg/alert-dlg.model'; import { ADIcon, MessageBoxConfig } from '../../ui/alert-dlg/alert-dlg.model';
import { NumberUtils } from '../../utilities/number-utils'; import { NumberUtils } from '../../utilities/number-utils';
import { StringUtils } from '../../utilities/string-utils'; import { StringUtils } from '../../utilities/string-utils';
@ -15,6 +15,11 @@ import { SignalRService, SignalRSession, SignalRMessage } from '../signal-r.serv
import { MD2StateService } from './md2-state.service'; import { MD2StateService } from './md2-state.service';
import { MD2BroadcastService } from './md2-broadcast.service'; import { MD2BroadcastService } from './md2-broadcast.service';
import { CoreGameMobFactories } from '../../games/massive-darkness2/factorys/mobs/CoreGame'; 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({ @Injectable({
@ -28,13 +33,12 @@ export class MD2Service {
public enemyPhaseSubject = new Subject<MobInfo>(); public enemyPhaseSubject = new Subject<MobInfo>();
public initialized = false; public initialized = false;
public mobBeenKilledSubject = new Subject<MobInfo>(); public mobBeenKilledSubject = new Subject<MobInfo>();
public mobDeck: DrawingBag<MobInfo>;
public roamingMobDeck: DrawingBag<MobInfo>;
public treasureBag: DrawingBag<TreasureItem> = new DrawingBag<TreasureItem>();
public refreshUI$: Subject<void> = new Subject<void>(); public refreshUI$: Subject<void> = new Subject<void>();
public refreshTreasureBagSubject = new Subject<DrawingBag<TreasureItem>>(); public refreshTreasureBagSubject = new Subject<DrawingBag<TreasureItem>>();
public heroAttackingSubject = new Subject<MD2HeroInfo>();
public get heros() { public get heros() {
return this.stateService.info.heros; return this.stateService.info.heros;
@ -76,67 +80,6 @@ export class MD2Service {
} }
// #endregion Constructors (1) // #endregion Constructors (1)
private initCoreGameRoamingMonsters() {
let mobs = [];
this.roamingMobDeck.AddItem(new MobInfo({ name: 'Andra', hp: 5, level: 1, rewardTokens: 2, type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Rare)] }));
this.roamingMobDeck.AddItem(new MobInfo({ name: 'Andra', hp: 7, level: 3, rewardTokens: 2, type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic)] }));
this.roamingMobDeck.AddItem(new MobInfo({ name: 'Andra', hp: 5, level: 5, rewardTokens: 0, type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic, 3)] }));
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)],
defenseInfo: new DefenseInfo(1, 1),
type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(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)],
defenseInfo: new DefenseInfo(2, 1),
type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(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)],
defenseInfo: new DefenseInfo(4, 1),
type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic, 3)]
}));
this.roamingMobDeck.AddItem(new MobInfo({
name: 'Lyidan, Incubus Lord', hp: 7, level: 1, rewardTokens: 2,
type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Rare)]
}));
this.roamingMobDeck.AddItem(new MobInfo({
name: 'Lyidan, Incubus Lord', hp: 10, level: 3, rewardTokens: 2,
type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic)]
}));
this.roamingMobDeck.AddItem(new MobInfo({
name: 'Lyidan, Incubus Lord', hp: 7, level: 5, rewardTokens: 0,
type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic, 3)]
}));
this.roamingMobDeck.AddItem(new MobInfo({
name: 'The Ghoul', hp: 5, level: 1, rewardTokens: 2,
type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Rare)]
}));
this.roamingMobDeck.AddItem(new MobInfo({
name: 'The Ghoul', hp: 8, level: 3, rewardTokens: 2,
type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic)]
}));
this.roamingMobDeck.AddItem(new MobInfo({
name: 'The Ghoul', hp: 5, level: 5, rewardTokens: 0,
type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic, 3)]
}));
}
private initCoreGameMobs() {
CoreGameMobFactories.forEach(factory => {
for (let i = 1; i <= 5; i++) {
this.mobDeck.AddItem(factory.generate(i));
i++;
}
});
}
// #region Public Getters And Setters (5) // #region Public Getters And Setters (5)
public get highestPlayerLevel(): number { public get highestPlayerLevel(): number {
@ -156,13 +99,23 @@ export class MD2Service {
} }
public get playerHero(): MD2HeroInfo { public get playerHero(): MD2HeroInfo {
return this.heros.find(h => h.playerInfo.signalRClientId == this.loginUserService.userAccess.signalRSessionId); return this.stateService.playerHero;
} }
public get currentActivateHero(): MD2HeroInfo {
return this.stateService.info.heros.find(h => h.uiActivating);
}
// #endregion Public Getters And Setters (5) // #endregion Public Getters And Setters (5)
// #region Public Methods (27) // #region Public Methods (27)
public get treasureBag() {
return this.stateService.treasureBag
}
public addTreasure(type: TreasureType, amount: number = 1) { public addTreasure(type: TreasureType, amount: number = 1) {
this.treasureBag.AddItem(new TreasureItem(type, amount)); this.treasureBag.AddItem(new TreasureItem(type, amount));
this.refreshTreasureBagSubject.next(this.treasureBag); this.refreshTreasureBagSubject.next(this.treasureBag);
@ -174,13 +127,19 @@ export class MD2Service {
let remainFrozenToken = Math.max(0, hero.frozenToken - hero.remainActions); let remainFrozenToken = Math.max(0, hero.frozenToken - hero.remainActions);
hero.remainActions = Math.max(0, hero.remainActions - hero.frozenToken); hero.remainActions = Math.max(0, hero.remainActions - hero.frozenToken);
hero.frozenToken = remainFrozenToken; hero.frozenToken = remainFrozenToken;
this.broadcastHeroInfoToOwner(hero); this.broadcastService.broadcastHeroInfoToOwner(hero);
}); });
this.stateService.info.roundPhase = RoundPhase.HeroPhase; this.stateService.info.roundPhase = RoundPhase.HeroPhase;
if (!this.stateService.info.isBossFight) {
this.stateService.info.round++; this.stateService.info.round++;
if (this.darknessPhaseRule.runDarknessPhase()) { if (this.darknessPhaseRule.runDarknessPhase()) {
this.msgBoxService.show(`${NumberUtils.Ordinal(this.stateService.info.round)} Hero Phase`, { icon: ADIcon.INFO }); 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(); //this.runNextPhase();
} }
@ -194,9 +153,9 @@ export class MD2Service {
level = 5; level = 5;
} }
if (isRoamingMonster) { if (isRoamingMonster) {
mobDeck = this.roamingMobDeck; mobDeck = this.stateService.roamingMobDeck;
} else { } else {
mobDeck = this.mobDeck; mobDeck = this.stateService.mobDeck;
} }
if (mobDeck.drawingItems.filter(m => (m as MobInfo).level == level) if (mobDeck.drawingItems.filter(m => (m as MobInfo).level == level)
@ -205,40 +164,44 @@ export class MD2Service {
} }
let newSpawnMob = new MobInfo(mobDeck.DrawAndRemove(1, m => m.level == level)[0]); let newSpawnMob = new MobInfo(mobDeck.DrawAndRemove(1, m => m.level == level)[0]);
if (isRoamingMonster) {
newSpawnMob.unitRemainHp = newSpawnMob.hp * this.playerAmount;
newSpawnMob.mobAmount = 0;
} 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);
let exitingMob = isRoamingMonster ? this.roamingMonsters.find(m => m.name == newSpawnMob.name) : this.mobs.find(m => m.name == newSpawnMob.name);
if (exitingMob) { if (exitingMob) {
exitingMob.level = newSpawnMob.level; exitingMob.level = newSpawnMob.level;
exitingMob.hp = newSpawnMob.hp; exitingMob.hp = newSpawnMob.hp;
exitingMob.imageUrl = newSpawnMob.imageUrl; exitingMob.imageUrl = newSpawnMob.imageUrl;
exitingMob.attackInfos = newSpawnMob.attackInfos;
exitingMob.defenseInfo = newSpawnMob.defenseInfo;
exitingMob.combatSkill = newSpawnMob.combatSkill;
} else { } 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); this.mobs.push(newSpawnMob);
}
newSpawnMob.carriedTreasure = this.treasureBag.DrawAndRemove(newSpawnMob.rewardTokens); newSpawnMob.carriedTreasure = this.treasureBag.DrawAndRemove(newSpawnMob.rewardTokens);
this.refreshTreasureBagSubject.next(this.treasureBag); this.refreshTreasureBagSubject.next(this.treasureBag);
} }
newSpawnMob.imageUrl = this.mobImage(newSpawnMob.name, newSpawnMob.level, isRoamingMonster);
return { exitingMob, mob: newSpawnMob }; return { exitingMob, mob: newSpawnMob };
} }
public enemyPhase() { public enemyPhase() {
//this.msgBoxService //this.msgBoxService
let monsterBag = new DrawingBag<MobInfo>(); this.enemyPhaseMobs = this.roamingMonsters.concat(this.mobs);
monsterBag.drawingItems = this.roamingMonsters.concat(this.mobs);
for (let i = 0; i < monsterBag.drawingItems.length; i++) { if (this.enemyPhaseMobs.length > 0) {
const element = monsterBag.drawingItems[i]; this.enemyPhaseMobs = ArrayUtils.Shuffle(this.enemyPhaseMobs);
element.drawingWeight = 1;
}
if (monsterBag.drawingItems.length > 0) {
this.enemyPhaseMobs = monsterBag.DrawAndRemove(monsterBag.drawingItems.length);
//this.showEnemyPhaseAction(); //this.showEnemyPhaseAction();
this.enemyPhaseSubject.next(this.enemyPhaseMobs[0]); this.enemyPhaseSubject.next(this.enemyPhaseMobs[0]);
} else { } else {
@ -247,33 +210,53 @@ export class MD2Service {
} }
public enterBossFight() { public enterBossFight() {
//this.sendMsgboxMsg 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) { public fileList(folderPath: string) {
return this.fileService.FileList('Images/MD2/' + folderPath); return this.fileService.FileList('Images/MD2/' + folderPath);
} }
public heroFullName(hero: MD2HeroInfo) { public heroFullName(hero: MD2HeroInfo) {
if (!hero) return ''; return MD2Logic.heroFullName(hero);
return `${hero.playerInfo.name} (${HeroClass[hero.class]} - ${hero.name})`
} }
public initMobDecks() { public levelUpPhase(runNextPhase: boolean = true) {
this.mobDeck = new DrawingBag();
this.roamingMobDeck = new DrawingBag();
this.initCoreGameMobs();
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++) { for (let i = 0; i < this.heros.length; i++) {
const hero = this.heros[i]; const hero = this.heros[i];
let levelUpInfo = MD2Rules.checkCoreGameLevelup(hero.level, hero.exp); let levelUpInfo = MD2Rules.checkCoreGameLevelup(hero.level, hero.exp);
@ -289,11 +272,12 @@ export class MD2Service {
levelUpInfo = MD2Rules.checkCoreGameLevelup(hero.level, hero.exp); levelUpInfo = MD2Rules.checkCoreGameLevelup(hero.level, hero.exp);
} }
} }
if (runNextPhase) {
this.runNextPhase(); this.runNextPhase();
} }
}
public mobImage(name: string, level: number, isRoamingMonsters: boolean) { public mobImage(name: string, level: number, isRoamingMonsters: boolean) {
name = StringUtils.replaceAll(name, ' ', '').replace(',', '');
if (level < 3) { if (level < 3) {
level = 1; level = 1;
} else if (level < 5) { } else if (level < 5) {
@ -314,6 +298,7 @@ export class MD2Service {
hero.hp = hero.hpMaximum; hero.hp = hero.hpMaximum;
hero.mp = hero.mpMaximum; hero.mp = hero.mpMaximum;
hero.exp = 0; hero.exp = 0;
this.stateService.playerHero = hero;
let message = { let message = {
receiver: { isGroup: true, sessionId: this.gameRoomService.gameRoomId } as SignalRSession, receiver: { isGroup: true, sessionId: this.gameRoomService.gameRoomId } as SignalRSession,
from: { isGroup: false, sessionId: this.loginUserService.userAccess.signalRSessionId }, from: { isGroup: false, sessionId: this.loginUserService.userAccess.signalRSessionId },
@ -368,51 +353,11 @@ export class MD2Service {
} }
public getTargetHerosByFilter(targetType: AttackTarget, onlyOne: boolean = false) { public getTargetHerosByFilter(targetType: AttackTarget, onlyOne: boolean = false) {
let beenAttackedHero = [] as MD2HeroInfo[]; return MD2Logic.getTargetHerosByFilter(this.stateService.info.heros, targetType, onlyOne);
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[]) { public getTargetHerosHtml(beenAttackedHero: MD2HeroInfo[]) {
return `<b>${StringUtils.makeCommaSeparatedString(beenAttackedHero.map(h => this.heroFullName(h)), false, true)}</b>`; return MD2Logic.getTargetHerosHtml(beenAttackedHero);
} }
// #endregion Public Methods (27) // #endregion Public Methods (27)
@ -433,12 +378,10 @@ export class MD2GameInfo {
if (this.boss.info) { if (this.boss.info) {
this.boss.info = new MobInfo(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)); this.heros = this.heros.map(h => new MD2HeroInfo(h));
} }
} }
public currentActivateHero: MD2HeroInfo;
public isBossFight: boolean = false; public isBossFight: boolean = false;
public mobs: MobInfo[] = []; public mobs: MobInfo[] = [];
public roamingMonsters: MobInfo[] = []; public roamingMonsters: MobInfo[] = [];

View File

@ -1,4 +1,4 @@
<div class='form-group'> <div class='form-group adjustContainer'>
<label for='playerAmount' class='label' *ngIf="title" [innerHtml]="titleHtml"></label> <label for='playerAmount' class='label' *ngIf="title" [innerHtml]="titleHtml"></label>
<div> <div>
<button nbButton outline type="button" status="primary" size="{{size}}" (click)="adjustNumber(false)"> <button nbButton outline type="button" status="primary" size="{{size}}" (click)="adjustNumber(false)">

View File

@ -0,0 +1,3 @@
.adjustContainer {
white-space: nowrap;
}

View File

@ -57,17 +57,21 @@ export class AdjacentNumberInputComponent implements ControlValueAccessor, OnIni
@Output() hitMaximum = new EventEmitter<number>(); @Output() hitMaximum = new EventEmitter<number>();
@Output() hitIncreasing = new EventEmitter<number>(); @Output() hitIncreasing = new EventEmitter<number>();
@Output() hitDecreasing = new EventEmitter<number>(); @Output() hitDecreasing = new EventEmitter<number>();
@Output() hitChange = new EventEmitter<number>();
onChange = (value: number) => { onChange = (value: number) => {
}; };
onTouched = () => { }; onTouched = () => { };
constructor( constructor(
private cdRef: ChangeDetectorRef private cdRef: ChangeDetectorRef
) { } ) { }
writeValue(obj: number): void { writeValue(obj: number): void {
if (this.currentNumber != obj) {
this.currentNumber = obj; this.currentNumber = obj;
this.onChange(obj); this.onChange(obj);
this.cdRef.detectChanges();
}
} }
registerOnChange(fn: (value: number) => void): void { registerOnChange(fn: (value: number) => void): void {
@ -99,6 +103,8 @@ export class AdjacentNumberInputComponent implements ControlValueAccessor, OnIni
if (null == this.maximum || this.currentNumber < this.maximum) { if (null == this.maximum || this.currentNumber < this.maximum) {
this.currentNumber++; this.currentNumber++;
this.hitIncreasing.next(1); this.hitIncreasing.next(1);
this.hitChange.next(1);
} else { } else {
this.hitMaximum.next(this.currentNumber); this.hitMaximum.next(this.currentNumber);
} }
@ -106,6 +112,7 @@ export class AdjacentNumberInputComponent implements ControlValueAccessor, OnIni
if (null == this.minimum || this.currentNumber > this.minimum) { if (null == this.minimum || this.currentNumber > this.minimum) {
this.currentNumber--; this.currentNumber--;
this.hitDecreasing.next(1); this.hitDecreasing.next(1);
this.hitChange.next(-1);
} else { } else {
this.hitMinimum.next(this.currentNumber); this.hitMinimum.next(this.currentNumber);
} }

View File

@ -40,4 +40,22 @@ export class ArrayUtils {
} }
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]); 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;
}
} }

View File

@ -1,26 +1,26 @@
// Typography // Typography
// @import "globals/typo-font-sizes"; @import "globals/typo-font-sizes";
// @import "globals/typo-font-weights"; @import "globals/typo-font-weights";
// @import "globals/typo-text-transforms"; @import "globals/typo-text-transforms";
// @import "globals/typo-text-decorations"; @import "globals/typo-text-decorations";
// @import "globals/typo-letter-spacings"; @import "globals/typo-letter-spacings";
// @import "globals/typo-line-heights"; @import "globals/typo-line-heights";
// @import "globals/typo-font-styles"; @import "globals/typo-font-styles";
// @import "globals/typo-list-styles"; @import "globals/typo-list-styles";
// @import "globals/typo-text-styles"; @import "globals/typo-text-styles";
// Border styles // Border styles
// @import "globals/borders"; @import "globals/borders";
// @import "globals/border-none"; @import "globals/border-none";
// @import "globals/border-styles"; @import "globals/border-styles";
// @import "globals/border-radiuses"; @import "globals/border-radiuses";
// @import "globals/border-dashed"; @import "globals/border-dashed";
// @import "globals/border-dotted"; @import "globals/border-dotted";
// @import "globals/border-widths"; @import "globals/border-widths";
//Common //Common
@import "globals/heights"; @import "globals/heights";
@import "globals/widths"; @import "globals/widths";
// @import "globals/z-index"; @import "globals/z-index";
// @import "globals/cursor"; @import "globals/cursor";

View File

@ -2,6 +2,30 @@
Heights 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) { @each $breakpoint in map-keys($grid-breakpoints) {
@include media-breakpoint-up($breakpoint) { @include media-breakpoint-up($breakpoint) {
$infix: breakpoint-infix($breakpoint, $grid-breakpoints); $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
@ -37,6 +61,39 @@
} }
$i: $i + 5; $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 */ /* Auto Height */
.g-height-auto#{$infix} { .g-height-auto#{$infix} {
height: auto; height: auto;

View File

@ -4,3 +4,7 @@
.g-text-break-word { .g-text-break-word {
word-wrap: break-word; word-wrap: break-word;
} }
.g-text-nowrap {
white-space: nowrap;
}

View File

@ -15,6 +15,30 @@
.MD2text { .MD2text {
font-family: "DwarvenAxeBBW00-Regular", sans-serif !important; 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 { .MD2Icon {
font-family: "Massive Darkness 2", sans-serif !important; font-family: "Massive Darkness 2", sans-serif !important;
//font-size: 50px; //font-size: 50px;
@ -107,13 +131,13 @@
&::before { &::before {
content: "U"; content: "U";
} }
color: orangered !important; color: #ffab6a !important;
} }
&.diceBlack { &.diceBlack {
&::before { &::before {
content: "U"; content: "U";
} }
color: black !important; color: #5b5d64 !important;
} }
&.diceRed { &.diceRed {
&::before { &::before {
@ -124,16 +148,16 @@
&.dice { &.dice {
font-family: "Massive Darkness 2", sans-serif !important; font-family: "Massive Darkness 2", sans-serif !important;
&.Blue { &.Blue {
color: aqua !important; color: #58a1ff !important;
} }
&.Yellow { &.Yellow {
color: #ffc107 !important; color: #ffc107 !important;
} }
&.Orange { &.Orange {
color: orangered !important; color: #ffab6a !important;
} }
&.Black { &.Black {
color: black !important; color: #5b5d64 !important;
} }
&.Red { &.Red {
color: crimson !important; color: crimson !important;

View File

@ -17,6 +17,6 @@
@import "form-controls"; @import "form-controls";
@import "md2"; @import "md2";
nb-card { nb-card {
max-width: 90vw; max-width: 96vw;
max-height: 96vh; max-height: 96vh;
} }

View File

@ -13,7 +13,7 @@ const urls = [
'https://api.happiness.tours' 'https://api.happiness.tours'
]; ];
const LINE_CLIENT_ID = '1657422139'; const LINE_CLIENT_ID = '1657422139';
const dockerDebug = urls[0]; const dockerDebug = urls[2];
export const environment = { export const environment = {
production: false, production: false,
apiUrl: dockerDebug, apiUrl: dockerDebug,