This commit is contained in:
Chris Chen 2025-11-13 15:44:39 -08:00
parent 2ef9968920
commit f30c41afba
32 changed files with 480 additions and 424 deletions

View File

@ -20,7 +20,7 @@ export interface LoginTokenViewModel {
avatarImage: string; avatarImage: string;
role: Role; role: Role;
cellGroup: PastoralDomain; cellGroup: PastoralDomain;
signalRSessionId; signalRConnectionId;
sessionTabId: string; sessionTabId: string;
} }

View File

@ -5,6 +5,7 @@ export interface IGamePlayer {
isPlayer: boolean; isPlayer: boolean;
signalRClientId: string; signalRClientId: string;
tabId: string; tabId: string;
isDisconnected: boolean;
} }
export class GamePlayer implements IGamePlayer { export class GamePlayer implements IGamePlayer {
@ -14,4 +15,5 @@ export class GamePlayer implements IGamePlayer {
isPlayer: boolean; isPlayer: boolean;
signalRClientId: string; signalRClientId: string;
tabId: string; tabId: string;
isDisconnected: boolean;
} }

View File

@ -8,6 +8,7 @@ import { StateService } from "../../services/state.service";
import { ADIcon, MessageBoxConfig } from "../../ui/alert-dlg/alert-dlg.model"; import { ADIcon, MessageBoxConfig } from "../../ui/alert-dlg/alert-dlg.model";
import { MD2HeroInfo, MD2Icon, MobInfo, RoundPhase } from "./massive-darkness2.model"; import { MD2HeroInfo, MD2Icon, MobInfo, RoundPhase } from "./massive-darkness2.model";
import { LoginUserService } from "../../services/login-user.service"; import { LoginUserService } from "../../services/login-user.service";
import { GamePlayer } from "../games.model";
@Injectable() @Injectable()
export abstract class MD2Base { export abstract class MD2Base {
@ -59,14 +60,14 @@ export abstract class MD2Base {
} }
imgUrl(imgPath: string) { imgUrl(imgPath: string) {
return this.md2Service.stateService.imgUrl(imgPath); return this.md2Service.imgUrl(imgPath);
} }
fileList(folderPath: string) { fileList(folderPath: string) {
return this.md2Service.fileList(folderPath); return this.md2Service.fileList(folderPath);
} }
iconHtml(icon: MD2Icon, cssClass = '') { iconHtml(icon: MD2Icon, cssClass = '') {
return this.md2Service.stateService.iconHtml(icon, cssClass); return this.md2Service.iconHtml(icon, cssClass);
} }
imgHtml(imgFile: string, cssClass = '') { imgHtml(imgFile: string, cssClass = '') {
@ -82,12 +83,17 @@ export abstract class MD2Base {
} }
abstract refreshUI(); abstract refreshUI();
handleSignalRCallback(message: SignalRMessage): void { handleSignalRCallback(message: SignalRMessage): void {
// if (message.from.isGroup) { if (message.from) {
// if (!this.isHeroDashboard) return; if (message.from.isGroup) {
// } else { if (!this.isHeroDashboard) return;
// if (this.isHeroDashboard && this.md2Service.playerHero.playerInfo.signalRClientId == message.from.sessionId) return; } else {
// } if (this.isHeroDashboard && this.md2Service.playerHero?.playerInfo?.signalRClientId == message.from.connectionId) return;
}
}
if (!this.isHeroDashboard) {
}
switch (message.actionType) { switch (message.actionType) {
case 'hero': case 'hero':
let heroInfo = new MD2HeroInfo(JSON.parse(message.parameters['hero'])); let heroInfo = new MD2HeroInfo(JSON.parse(message.parameters['hero']));
@ -101,7 +107,7 @@ export abstract class MD2Base {
break; break;
case 'updateMyHero': case 'updateMyHero':
if (this.isHeroDashboard) { if (this.isHeroDashboard) {
this.md2Service.stateService.playerHero = heroInfo; this.md2Service.playerHero = heroInfo;
} }
break; break;
@ -113,24 +119,42 @@ export abstract class MD2Base {
case 'heroes': case 'heroes':
switch (message.actionName) { switch (message.actionName) {
case 'updateAll': case 'updateAll':
let allHeroes = (JSON.parse(message.parameters['heros']) as MD2HeroInfo[]).map(h => new MD2HeroInfo(h)); if (this.isHeroDashboard) {
//Remove heroes that are not in the list let allHeroes = (JSON.parse(message.parameters['heros']) as MD2HeroInfo[]).map(h => new MD2HeroInfo(h));
this.md2Service.info.heros = this.md2Service.heros.filter(h => !allHeroes.some(h2 => h2.playerInfo.tabId == h.playerInfo.tabId)); //Remove heroes that are not in the list
allHeroes.forEach(heroInfo => { this.md2Service.info.heros = this.md2Service.heros.filter(h => allHeroes.some(h2 => h2.playerInfo.tabId == h.playerInfo.tabId));
this.updateHeroInfo(heroInfo); allHeroes.forEach(heroInfo => {
}); this.updateHeroInfo(heroInfo);
});
this.detectChanges();
}
break; break;
} }
break; break;
case 'GameRoom': case 'GameRoom':
switch (message.actionName) { switch (message.actionName) {
case 'Leaving': case 'Leaving':
this.md2Service.heros.splice(this.md2Service.heros.findIndex(h => h.playerInfo.tabId == message.from.sessionId)); let leavingPlayerInfo = message.value as GamePlayer;
let leavingHero = this.md2Service.heros.find(h => h.playerInfo.tabId == leavingPlayerInfo.tabId);
if (leavingHero) {
leavingHero.playerInfo.isDisconnected = true;
}
//var disconnectHero = this.md2Service.heros.splice(this.md2Service.heros.findIndex(h => h.playerInfo.signalRClientId == leavingPlayerInfo.signalRClientId));
//this.md2Service.info.disconnectedHeroes.push(...disconnectHero);
this.detectChanges(); this.detectChanges();
break; break;
case 'update': case 'update':
if (this.isHeroDashboard) { if (this.isHeroDashboard) {
this.md2Service.info = new MD2GameInfo(JSON.parse(message.parameters['gameInfo']) as MD2GameInfo); this.md2Service.info = new MD2GameInfo(JSON.parse(message.parameters['gameInfo']) as MD2GameInfo);
let playerHero = this.md2Service.heros.find(h => h.playerInfo.tabId == this.stateService.loginUserService.sessionTabId);
if (playerHero) {
playerHero.playerInfo = this.md2Service.gameRoomService.currentPlayer();
playerHero.playerInfo.isDisconnected = false;
this.md2Service.playerHero = playerHero;
this.md2Service.broadcastMyHeroInfo();
}
this.detectChanges(); this.detectChanges();
} }
break; break;
@ -140,10 +164,17 @@ export abstract class MD2Base {
this.detectChanges(); this.detectChanges();
} }
break; break;
case 'getGameInfo':
if (!this.isHeroDashboard) {
this.md2Service.broadcastGameInfo();
}
break;
case 'sendJoinInfo': case 'sendJoinInfo':
if (this.isHeroDashboard && this.md2Service.playerHero) { if (this.isHeroDashboard && this.md2Service.playerHero) {
this.md2Service.playerHero.playerInfo.signalRClientId = message.parameters['signalrconnid']; this.md2Service.playerHero.playerInfo.signalRClientId = message.parameters['signalrconnid'];
this.md2Service.broadcastService.broadcastMyHeroInfo(); this.md2Service.broadcastMyHeroInfo();
} }
break; break;
default: default:
@ -212,14 +243,15 @@ export abstract class MD2Base {
} }
} }
updateHeroInfo(heroInfo: MD2HeroInfo) { updateHeroInfo(heroInfo: MD2HeroInfo) {
let exitingHero = this.md2Service.heros.find(h => h.playerInfo.signalRClientId == heroInfo.playerInfo.signalRClientId); let exitingHero = this.md2Service.heros.find(h => h.playerInfo.tabId == heroInfo.playerInfo.tabId);
if (exitingHero) { if (exitingHero) {
//For boss fight, if the hero finished activating, activate the boss
let activateBoss = exitingHero.uiActivating && !heroInfo.uiActivating; let activateBoss = exitingHero.uiActivating && !heroInfo.uiActivating;
this.md2Service.heros[this.md2Service.heros.indexOf(exitingHero)] = heroInfo; this.md2Service.heros[this.md2Service.heros.indexOf(exitingHero)] = heroInfo;
//My hero update //My hero update
if (this.isHeroDashboard && this.md2Service.loginUserService.sessionTabId == heroInfo.playerInfo.tabId) { if (this.isHeroDashboard && this.md2Service.loginUserService.sessionTabId == heroInfo.playerInfo.tabId) {
this.md2Service.stateService.playerHero = heroInfo; this.md2Service.playerHero = heroInfo;
} }
if (!this.isHeroDashboard && this.md2Service.info.isBossFight && activateBoss) { if (!this.isHeroDashboard && this.md2Service.info.isBossFight && activateBoss) {
this.md2Service.activateBoss(); this.md2Service.activateBoss();
@ -276,14 +308,14 @@ export abstract class MD2ComponentBase {
this.destroy$.complete(); this.destroy$.complete();
} }
imgUrl(imgPath: string) { imgUrl(imgPath: string) {
return this.md2Service.stateService.imgUrl(imgPath); return this.md2Service.imgUrl(imgPath);
} }
fileList(folderPath: string) { fileList(folderPath: string) {
return this.md2Service.fileList(folderPath); return this.md2Service.fileList(folderPath);
} }
iconHtml(icon: MD2Icon, cssClass = '') { iconHtml(icon: MD2Icon, cssClass = '') {
return this.md2Service.stateService.iconHtml(icon, cssClass); return this.md2Service.iconHtml(icon, cssClass);
} }
detectChanges() { detectChanges() {
if (!this.cdRef['destroyed']) { if (!this.cdRef['destroyed']) {

View File

@ -11,7 +11,7 @@
<div class="col-md-7"> <div class="col-md-7">
<div class="row"> <div class="row">
<div class="col-md"> <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.hp" minimum="0"
class="mb-3" title="Boss HP" (hitMinimum)="WIN()"> class="mb-3" title="Boss HP" (hitMinimum)="WIN()">
</adj-number-input> </adj-number-input>
<md2-mob-attack-info [mob]="boss.info"> <md2-mob-attack-info [mob]="boss.info">

View File

@ -52,7 +52,11 @@ export class BossFightComponent extends MD2ComponentBase {
this.boss.activating(); this.boss.activating();
} }
WIN() { WIN() {
this.msgBoxService.show('Win', { text: 'You Win the Boss Fight', icon: ADIcon.INFO });
this.md2Service.info.isBossFight = false;
this.md2Service.info.boss = undefined;
this.md2Service.heros.forEach(h => h.uiBossFight = false);
this.md2Service.broadcastGameInfo();
} }
attack(mob: MobInfo) { attack(mob: MobInfo) {
@ -62,6 +66,9 @@ export class BossFightComponent extends MD2ComponentBase {
let attackDamage = mobResult.uiWounds; let attackDamage = mobResult.uiWounds;
if (attackDamage) { if (attackDamage) {
this.boss.info.hp -= attackDamage; this.boss.info.hp -= attackDamage;
if (this.boss.info.hp <= 0) {
this.WIN();
}
this.cdRef.detectChanges(); this.cdRef.detectChanges();
} }
} }

View File

@ -139,7 +139,7 @@
<!-- <img class="MD2HeroCard " src="{{imgUrl('Heros/'+className+'.jpg')}}" (click)="toggleFlip()"> --> <!-- <img class="MD2HeroCard " src="{{imgUrl('Heros/'+className+'.jpg')}}" (click)="toggleFlip()"> -->
<!-- Action Buttons (Desktop/Landscape) --> <!-- Action Buttons (Desktop/Landscape) -->
<div class="hero-actions d-none d-sm-block"> <div class="hero-actions d-block">
<div class="action-buttons-group" *ngIf="hero.uiActivating && hero.remainActions > 0"> <div class="action-buttons-group" *ngIf="hero.uiActivating && hero.remainActions > 0">
<button nbButton hero class="action-btn" status="info" (click)="moveAction()" <button nbButton hero class="action-btn" status="info" (click)="moveAction()"
*ngIf="!showMoveAction"> *ngIf="!showMoveAction">

View File

@ -14,6 +14,7 @@ import { DebounceTimer } from '../../../utilities/timer-utils';
import { HeroClass, MD2HeroInfo, MD2HeroProfile, MD2Icon } from '../massive-darkness2.model'; import { HeroClass, MD2HeroInfo, MD2HeroProfile, MD2Icon } from '../massive-darkness2.model';
import { MD2Base } from '../MD2Base'; import { MD2Base } from '../MD2Base';
import { MD2HeroProfileService } from '../service/massive-darkness2.service'; import { MD2HeroProfileService } from '../service/massive-darkness2.service';
import { SignalRService } from '../../../services/signal-r.service';
@Component({ @Component({
selector: 'ngx-hero-dashboard', selector: 'ngx-hero-dashboard',
@ -70,6 +71,15 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
return this.md2Service.playerHero; return this.md2Service.playerHero;
} }
public get className() {
if (this.md2Service.playerHero) {
return HeroClass[this.md2Service.playerHero.class];
}
if (this.selectedHeroClass) {
return HeroClass[this.selectedHeroClass];
}
return '';
}
public get currentSelectingHero(): MD2HeroInfo { public get currentSelectingHero(): MD2HeroInfo {
return this.heros[this.currentHeroIndex]; return this.heros[this.currentHeroIndex];
} }
@ -82,6 +92,7 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
protected route: ActivatedRoute, protected route: ActivatedRoute,
protected cdRef: ChangeDetectorRef, protected cdRef: ChangeDetectorRef,
private msgBoxService: MsgBoxService, private msgBoxService: MsgBoxService,
private signalRService: SignalRService
) { ) {
super(md2Service, stateService, route, cdRef); super(md2Service, stateService, route, cdRef);
this.isHeroDashboard = true; this.isHeroDashboard = true;
@ -93,6 +104,15 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
ngOnInit(): void { ngOnInit(): void {
super.ngOnInit(); super.ngOnInit();
this.gameRoomService.gameRoomId = this.roomId;
this.gameRoomService.joinGameRoom(this.roomId);
//this.fetchGameInfo();
this.signalRService.signalRMessageConnSubject.subscribe(state => {
if (state.status == 'connected') {
this.fetchGameInfo();
}
});
} }
override signalRInitialized() { override signalRInitialized() {
@ -103,9 +123,7 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
// } // }
} }
initHero() { initHero() {
this.gameRoomService.gameRoomId = this.roomId; if (!this.md2Service.heros.some(h => h.playerInfo.signalRClientId == this.stateService.loginUserService.userAccess.signalRConnectionId)) {
this.gameRoomService.joinGameRoom(this.roomId);
if (!this.md2Service.heros.some(h => h.playerInfo.signalRClientId == this.stateService.loginUserService.userAccess.signalRSessionId)) {
this.msgBoxService.showInputbox('Select Hero Class', '', { dropDownOptions: this.classOptions, inputType: 'dropdown' }) this.msgBoxService.showInputbox('Select Hero Class', '', { dropDownOptions: this.classOptions, inputType: 'dropdown' })
.pipe(first()).subscribe(heroClass => { .pipe(first()).subscribe(heroClass => {
@ -127,10 +145,11 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
}); });
} }
} }
className: string;
initClassHeroList(heroClass: HeroClass) { initClassHeroList(heroClass: HeroClass) {
this.heros = []; this.heros = [];
this.className = HeroClass[heroClass];
this.selectedHeroClass = heroClass; this.selectedHeroClass = heroClass;
this.heroProfileService.getAll().pipe(first()).subscribe(result => { this.heroProfileService.getAll().pipe(first()).subscribe(result => {
@ -161,7 +180,7 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
selectCurrentHero() { selectCurrentHero() {
if (this.currentSelectingHero) { if (this.currentSelectingHero) {
this.md2Service.playerJoin(this.currentSelectingHero); this.md2Service.playerJoin(this.currentSelectingHero);
this.md2Service.broadcastService.broadcastMyHeroInfo(); this.md2Service.broadcastMyHeroInfo();
this.isSelectingHero = false; this.isSelectingHero = false;
this.detectChanges(); this.detectChanges();
} }
@ -189,8 +208,12 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
this.detectChanges(); this.detectChanges();
} }
fetchGameInfo() {
this.md2Service.broadcastFetchGameInfo();
}
broadcastHeroInfo() { broadcastHeroInfo() {
this.md2Service.broadcastService.broadcastMyHeroInfo(); this.md2Service.broadcastMyHeroInfo();
this.heroUpdateDebounceTimer.clearOut(); this.heroUpdateDebounceTimer.clearOut();
} }
increaseRage() { increaseRage() {
@ -199,7 +222,7 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
} }
} }
openDoor() { openDoor() {
this.md2Service.broadcastService.broadcastHeroAction('openDoor'); this.md2Service.broadcastHeroAction('openDoor');
//this.showMoveAction = false; //this.showMoveAction = false;
this.detectChanges(); this.detectChanges();
} }
@ -224,7 +247,7 @@ export class HeroDashboardComponent extends MD2Base implements OnInit {
default: default:
break; break;
} }
this.md2Service.broadcastService.broadcastHeroAction(action); this.md2Service.broadcastHeroAction(action);
this.reduceAction(); this.reduceAction();
} }
reduceAction() { reduceAction() {

View File

@ -59,20 +59,28 @@
<div class="col-12" *ngFor="let hero of md2Service.heros"> <div class="col-12" *ngFor="let hero of md2Service.heros">
<label class='label mr-1'>{{hero.playerInfo.name}} ({{heroClassName(hero)}} - <label class='label mr-1'>{{hero.playerInfo.name}} ({{heroClassName(hero)}} -
{{hero.name}})</label> {{hero.name}})</label>
<span class="badge badge-primary mr-1">Lv.:{{hero.level}}</span> <span class="badge badge-primary mr-1"
<span class="badge badge-primary mr-1">HP: {{hero.hp}}/{{hero.hpMaximum}}</span> (click)="adjustHeroValue(hero,'level')">Lv.:{{hero.level}}</span>
<span class="badge badge-primary mr-1">Mana: {{hero.mp}}/{{hero.mpMaximum}}</span> <span class="badge badge-primary mr-1" (click)="adjustHeroValue(hero,'hp')">HP:
<span class="badge badge-success mr-1">Exp: {{hero.exp}}</span> {{hero.hp}}/{{hero.hpMaximum}}</span>
<span class="badge badge-primary mr-1" (click)="adjustHeroValue(hero,'mp')">Mana:
{{hero.mp}}/{{hero.mpMaximum}}</span>
<span class="badge badge-success mr-1" (click)="adjustHeroValue(hero,'exp')">Exp:
{{hero.exp}}</span>
<span class="badge mr-1" *ngIf="hero.fireToken"> <span class="badge mr-1" *ngIf="hero.fireToken">
<md2-icon [icon]="MD2Icon.FireToken" size="sm"></md2-icon> {{hero.fireToken}} <md2-icon [icon]="MD2Icon.FireToken" size="sm"></md2-icon> {{hero.fireToken}}
</span> </span>
<span class="badge mr-1" *ngIf="hero.frozenToken"> <span class="badge mr-1" *ngIf="hero.frozenToken">
<md2-icon [icon]="MD2Icon.FrozenToken" size="sm"></md2-icon>{{hero.frozenToken}} <md2-icon [icon]="MD2Icon.FrozenToken" size="sm"></md2-icon>{{hero.frozenToken}}
</span> </span>
<span class="badge badge-success mr-1" *ngIf="hero.remainActions>0">Actions: <span class="badge badge-success mr-1" *ngIf="hero.remainActions>0"
(click)="adjustHeroValue(hero,'remainActions')">Actions:
{{hero.remainActions}}</span> {{hero.remainActions}}</span>
<span class="badge badge-light mr-1" *ngIf=" !hero.uiActivating">Inactive</span> <span class="badge badge-light mr-1" *ngIf=" !hero.uiActivating"
(click)="activatingHero(hero)">Inactive</span>
<span class="badge badge-primary mr-1" *ngIf="hero.uiActivating">Activating</span> <span class="badge badge-primary mr-1" *ngIf="hero.uiActivating">Activating</span>
<span class="badge badge-warning mr-1"
*ngIf="hero.playerInfo.isDisconnected">Disconnected</span>
<!-- <span class="badge badge-success mr-1">{{hero.playerInfo.signalRClientId}}</span> --> <!-- <span class="badge badge-success mr-1">{{hero.playerInfo.signalRClientId}}</span> -->
<span class="badge badge-danger mr-1" (click)="removeHero(hero)">X <span class="badge badge-danger mr-1" (click)="removeHero(hero)">X

View File

@ -0,0 +1,3 @@
.badge {
cursor: pointer;
}

View File

@ -51,6 +51,31 @@ export class MassiveDarkness2Component extends MD2Base implements OnInit {
} }
override signalRInitialized() { override signalRInitialized() {
}
adjustHeroValue(hero: MD2HeroInfo, value: string) {
this.msgBoxService.showInputbox(`Adjust ${value} for ${hero.playerInfo.name}`, `Enter the new value for ${value}`, {
inputType: 'number',
inputValue: hero[value].toString()
}).pipe(first()).subscribe(result => {
if (result) {
hero[value] = Number.parseInt(result);
this.md2Service.broadcastHeroInfoToAll(hero, true);
this.detectChanges();
}
});
}
activatingHero(hero: MD2HeroInfo) {
if (hero.remainActions > 0) {
this.msgBoxService.show('Activating', { text: `Are you sure you want to activate ${hero.playerInfo.name}?` }).pipe(first()).subscribe(result => {
if (result) {
this.md2Service.heros.forEach(h => h.uiActivating = false);
hero.uiActivating = true;
this.md2Service.broadcastAllHeroInfoToAll();
this.detectChanges();
}
});
}
} }
showQrCode() { showQrCode() {
if (this.md2Service.initialized == false) { if (this.md2Service.initialized == false) {
@ -146,7 +171,7 @@ export class MassiveDarkness2Component extends MD2Base implements OnInit {
this.md2Service.heros.forEach(hero => { this.md2Service.heros.forEach(hero => {
hero.uiShowAttackBtn = this.md2Service.mobs.length > 0 || this.md2Service.roamingMonsters.length > 0 || this.md2Service.info.isBossFight; hero.uiShowAttackBtn = this.md2Service.mobs.length > 0 || this.md2Service.roamingMonsters.length > 0 || this.md2Service.info.isBossFight;
}); });
this.md2Service.broadcastService.broadcastAllHeroInfoToAll(); this.md2Service.broadcastAllHeroInfoToAll();
} }
removeHero(hero) { removeHero(hero) {
this.msgBoxService.showConfirmDeleteBox().pipe(first()).subscribe(result => { this.msgBoxService.showConfirmDeleteBox().pipe(first()).subscribe(result => {

View File

@ -53,6 +53,7 @@ export interface IBossFight {
imgUrl: string imgUrl: string
standUrl: string standUrl: string
extraRules: string extraRules: string
md2Service: MD2Service
activating(): boolean activating(): boolean
prepareForBossFight(): void prepareForBossFight(): void
darknessPhase(): void darknessPhase(): void
@ -73,7 +74,7 @@ export abstract class BossFight implements IBossFight {
extraRules: string extraRules: string
protected subscription: Subscription protected subscription: Subscription
constructor(protected md2Service: MD2Service) { constructor(public md2Service: MD2Service) {
this.rounds = 1; this.rounds = 1;
} }
activating(): boolean { activating(): boolean {
@ -105,26 +106,26 @@ export abstract class BossFight implements IBossFight {
} }
export class BossMicheal extends BossFight { export class BossMicheal extends BossFight {
constructor(protected md2Service: MD2Service) { constructor(public md2Service: MD2Service) {
super(md2Service); super(md2Service);
this.corruptionTokenHtml = this.md2Service.stateService.imgHtml('Tokens/CorruptToken.png'); this.corruptionTokenHtml = this.md2Service.imgHtml('Tokens/CorruptToken.png');
this.name = 'Michael - The Corrupted Archangel'; this.name = 'Michael - The Corrupted Archangel';
this.imgUrl = md2Service.stateService.imgUrl('/Boss/Michael - The Corrupted Archangel.jpg'); this.imgUrl = md2Service.imgUrl('/Boss/Michael - The Corrupted Archangel.jpg');
this.standUrl = md2Service.stateService.imgUrl('/Boss/Michael.png'); this.standUrl = md2Service.imgUrl('/Boss/Michael.png');
this.info = new MobInfo({ this.info = new MobInfo({
description: this.name, description: this.name,
type: MobType.Boss, type: MobType.Boss,
hpPerHero: 15, hpPerHero: 15,
level: 10, level: 10,
imageUrl: md2Service.stateService.imgUrl('/Boss/Michael.png') imageUrl: md2Service.imgUrl('/Boss/Michael.png')
}); });
if (!this.info.skills) { if (!this.info.skills) {
this.info.skills = []; this.info.skills = [];
} }
this.info.skills.push({ this.info.skills.push({
name: `Combat 1 ${this.md2Service.stateService.iconHtml(MD2Icon.EnemySkill)}`, name: `Combat 1 ${this.md2Service.iconHtml(MD2Icon.EnemySkill)}`,
description: `Deal 1 Wound for each ${this.corruptionTokenHtml} on the attacking or defending Hero. Discard the tokens afterwards(once per combat).`, description: `Deal 1 Wound for each ${this.corruptionTokenHtml} on the attacking or defending Hero. Discard the tokens afterwards(once per combat).`,
type: MobSkillType.Combat, type: MobSkillType.Combat,
skillRoll: 1 skillRoll: 1
@ -135,9 +136,9 @@ export class BossMicheal extends BossFight {
this.actionBlackDice = 2; 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>` + 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)}.` + `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.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.iconHtml(MD2Icon.EnemyClaw)} the Hero takes 1 Wound.<br>If ${this.md2Service.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.` `If ${this.md2Service.iconHtml(MD2Icon.EnemyClaw)}/${this.md2Service.iconHtml(MD2Icon.EnemySkill)} the Hero takes 1 Wound and places 1 ${this.corruptionTokenHtml} on their Dashboard.`
} }
activatedTimes: number activatedTimes: number
acted: number acted: number
@ -232,26 +233,26 @@ export class BossMicheal extends BossFight {
export class BossReaper extends BossFight { export class BossReaper extends BossFight {
constructor(protected md2Service: MD2Service) { constructor(public md2Service: MD2Service) {
super(md2Service); super(md2Service);
this.timeTokenHtml = this.md2Service.stateService.imgHtml('Tokens/TimeToken.png'); this.timeTokenHtml = this.md2Service.imgHtml('Tokens/TimeToken.png');
this.name = 'The Reaper'; this.name = 'The Reaper';
this.imgUrl = md2Service.stateService.imgUrl('/Boss/The Reaper.jpg'); this.imgUrl = md2Service.imgUrl('/Boss/The Reaper.jpg');
this.standUrl = md2Service.stateService.imgUrl('/Boss/The Reaper-Stand.png'); this.standUrl = md2Service.imgUrl('/Boss/The Reaper-Stand.png');
this.info = new MobInfo({ this.info = new MobInfo({
description: this.name, description: this.name,
type: MobType.Boss, type: MobType.Boss,
hpPerHero: 25, hpPerHero: 25,
level: 10, level: 10,
imageUrl: md2Service.stateService.imgUrl('/Boss/The Reaper-Stand.png') imageUrl: md2Service.imgUrl('/Boss/The Reaper-Stand.png')
}); });
if (!this.info.skills) { if (!this.info.skills) {
this.info.skills = []; this.info.skills = [];
} }
this.info.skills.push({ this.info.skills.push({
description: `If the Hero has no ${this.md2Service.stateService.iconHtml(MD2Icon.Mana_Color)}, they take 1 ${this.md2Service.stateService.iconHtml(MD2Icon.FrozenToken)}`, description: `If the Hero has no ${this.md2Service.iconHtml(MD2Icon.Mana_Color)}, they take 1 ${this.md2Service.iconHtml(MD2Icon.FrozenToken)}`,
type: MobSkillType.Attack, type: MobSkillType.Attack,
skillRoll: 1 skillRoll: 1
} as MD2MobSkill); } as MD2MobSkill);
@ -304,8 +305,8 @@ export class BossReaper extends BossFight {
name: 'Death Is Coming', name: 'Death Is Coming',
description: description:
`Place The Reaper in the central Zone.<br>` + `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>` + `Roll 1 ${this.md2Service.iconHtml(MD2Icon.YellowDice)}. Remove ${this.timeTokenHtml} equal to ${this.md2Service.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.` `Each Hero discards ${this.md2Service.iconHtml(MD2Icon.MP)} equal to ${this.md2Service.iconHtml(MD2Icon.Melee)} rolled.`
}); });
break; break;

View File

@ -6,7 +6,6 @@ import { MsgBoxService } from '../../../services/msg-box.service';
import { DropDownOption } from '../../../entity/dropDownOption'; import { DropDownOption } from '../../../entity/dropDownOption';
import { MD2Icon } from '../massive-darkness2.model'; import { MD2Icon } from '../massive-darkness2.model';
import { first } from 'rxjs/operators'; import { first } from 'rxjs/operators';
import { MD2StateService } from '../../../services/MD2/md2-state.service';
import { MD2IconPickerDlgComponent } from './md2-icon-picker-dlg.component'; import { MD2IconPickerDlgComponent } from './md2-icon-picker-dlg.component';
import { NbDialogService } from '@nebular/theme'; import { NbDialogService } from '@nebular/theme';
import { DOMParser as ProseMirrorDOMParser } from 'prosemirror-model'; import { DOMParser as ProseMirrorDOMParser } from 'prosemirror-model';
@ -59,7 +58,6 @@ export class MD2HtmlEditorComponent implements ControlValueAccessor, AfterViewIn
constructor( constructor(
private msgBoxService: MsgBoxService, private msgBoxService: MsgBoxService,
private md2StateService: MD2StateService,
private dialogService: DialogService, private dialogService: DialogService,
private cdr: ChangeDetectorRef, private cdr: ChangeDetectorRef,
elementRef: ElementRef, ngZone: NgZone, @Inject(PLATFORM_ID) platformId: Object) { elementRef: ElementRef, ngZone: NgZone, @Inject(PLATFORM_ID) platformId: Object) {

View File

@ -1,8 +1,8 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { NbDialogRef } from '@nebular/theme'; import { NbDialogRef } from '@nebular/theme';
import { MD2Icon } from '../massive-darkness2.model'; import { MD2Icon } from '../massive-darkness2.model';
import { MD2StateService } from '../../../services/MD2/md2-state.service';
import { DialogRef } from '@progress/kendo-angular-dialog'; import { DialogRef } from '@progress/kendo-angular-dialog';
import { MD2Service } from '../../../services/MD2/md2.service';
@Component({ @Component({
selector: 'md2-icon-picker-dlg', selector: 'md2-icon-picker-dlg',
@ -63,7 +63,7 @@ export class MD2IconPickerDlgComponent implements OnInit {
constructor( constructor(
private dlgRef: DialogRef, private dlgRef: DialogRef,
private md2StateService: MD2StateService private md2Service: MD2Service
) { } ) { }
ngOnInit(): void { ngOnInit(): void {
@ -72,7 +72,7 @@ export class MD2IconPickerDlgComponent implements OnInit {
this.iconList.push({ this.iconList.push({
icon: icon, icon: icon,
name: MD2Icon[icon], name: MD2Icon[icon],
html: this.md2StateService.iconHtml(icon) html: this.md2Service.iconHtml(icon)
}); });
} }
} }

View File

@ -1,6 +1,6 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { MD2Icon, TreasureType } from '../massive-darkness2.model'; import { MD2Icon, TreasureType } from '../massive-darkness2.model';
import { MD2StateService } from '../../../services/MD2/md2-state.service'; import { MD2Service } from '../../../services/MD2/md2.service';
@Component({ @Component({
selector: 'md2-icon', selector: 'md2-icon',
@ -44,7 +44,7 @@ export class MD2IconComponent implements OnInit {
} }
@Input() size: string = 'sm'; @Input() size: string = 'sm';
iconName: string; iconName: string;
constructor(private md2StateService: MD2StateService) { } constructor(private md2Service: MD2Service) { }
ngOnInit(): void { ngOnInit(): void {
} }
@ -52,10 +52,10 @@ export class MD2IconComponent implements OnInit {
private initIcon(icon: MD2Icon): void { private initIcon(icon: MD2Icon): void {
if (icon < MD2Icon.TreasureToken) { if (icon < MD2Icon.TreasureToken) {
this.isImageIcon = false; this.isImageIcon = false;
this.iconHtml = this.md2StateService.iconHtml(icon); this.iconHtml = this.md2Service.iconHtml(icon);
} else { } else {
this.isImageIcon = true; this.isImageIcon = true;
this.imgUrl = this.md2StateService.iconHtml(icon); this.imgUrl = this.md2Service.iconHtml(icon);
} }
} }

View File

@ -5,7 +5,7 @@ import { MD2MobLevelInfo } from '../../massive-darkness2.db.model';
import { MobSkillType } from '../../massive-darkness2.model.boss'; import { MobSkillType } from '../../massive-darkness2.model.boss';
import { MD2MobLevelInfoService } from '../../service/massive-darkness2.service'; import { MD2MobLevelInfoService } from '../../service/massive-darkness2.service';
import { MD2Icon, MobType } from '../../massive-darkness2.model'; import { MD2Icon, MobType } from '../../massive-darkness2.model';
import { MD2StateService } from '../../../../services/MD2/md2-state.service'; import { MD2Service } from '../../../../services/MD2/md2.service';
@Component({ @Component({
selector: 'ngx-md2-mob-level-editor', selector: 'ngx-md2-mob-level-editor',
@ -30,7 +30,7 @@ export class MD2MobLevelEditorComponent extends DialogContentBase implements OnI
public dialog: DialogRef, public dialog: DialogRef,
private mobLevelInfoService: MD2MobLevelInfoService, private mobLevelInfoService: MD2MobLevelInfoService,
private cdr: ChangeDetectorRef, private cdr: ChangeDetectorRef,
private md2StateService: MD2StateService private md2Service: MD2Service
) { ) {
super(dialog); super(dialog);
} }
@ -43,9 +43,9 @@ export class MD2MobLevelEditorComponent extends DialogContentBase implements OnI
public initializeEnums(): void { public initializeEnums(): void {
this.attackTypes = [ this.attackTypes = [
{ value: MobSkillType.Attack, text: 'None' }, { value: MobSkillType.Attack, text: 'None' },
{ value: MobSkillType.MeleeAttack, text: this.md2StateService.iconHtml(MD2Icon.Melee) + ' Melee Attack' }, { value: MobSkillType.MeleeAttack, text: this.md2Service.iconHtml(MD2Icon.Melee) + ' Melee Attack' },
{ value: MobSkillType.RangeAttack, text: this.md2StateService.iconHtml(MD2Icon.Range) + ' Range Attack' }, { value: MobSkillType.RangeAttack, text: this.md2Service.iconHtml(MD2Icon.Range) + ' Range Attack' },
{ value: MobSkillType.MagicAttack, text: this.md2StateService.iconHtml(MD2Icon.Magic) + ' Magic Attack' }, { value: MobSkillType.MagicAttack, text: this.md2Service.iconHtml(MD2Icon.Magic) + ' Magic Attack' },
]; ];
this.selectedAttackType = this.attackTypes.find(t => t.value === this.model.attackInfo.type) || this.attackTypes[0] || null; this.selectedAttackType = this.attackTypes.find(t => t.value === this.model.attackInfo.type) || this.attackTypes[0] || null;
this.selectedAlterAttackType = this.attackTypes.find(t => t.value === this.model.alterAttackInfo?.type) || this.attackTypes[0] || null; this.selectedAlterAttackType = this.attackTypes.find(t => t.value === this.model.alterAttackInfo?.type) || this.attackTypes[0] || null;

View File

@ -8,8 +8,8 @@
<div class="pl-2 col-md-6" *ngIf="mob.mobAmount"> <div class="pl-2 col-md-6" *ngIf="mob.mobAmount">
<ng-container> <ng-container>
<label class='label g-text-nowrap'>Alive Units <b <label class='label g-text-nowrap'>Minions <b
class="MD2text g-font-size-18">{{mob.mobAmount}}</b></label><br> class="MD2text g-font-size-18">{{mob.mobAmount-1}}</b></label><br>
</ng-container> </ng-container>
</div> </div>

View File

@ -1,7 +1,8 @@
<img class="g-width-95x img-thumbnail mobBg" src="{{imgUrl('/Mobs/BG.png')}}" /> <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" /> <img class="mobImg roamingMonster" src="{{getMobImageUrl(mob)}}" (click)="showMobImage(mob)" *ngIf="!isMob" />
<div *ngIf="isMob"> <div *ngIf="isMob">
<img class="mobImg mobLeader" src="{{mob.leaderImgUrl}}" (click)="showMobImage(mob)" /> <img class="mobImg mobLeader" src="{{mob.leaderImgUrl}}" (click)="showMobImage(mob)"
<img class="mobImg mobMinion" src="{{mob.minionImgUrl}}" (click)="showMobImage(mob)" /> [ngClass]="{'noMinions': mob.mobAmount==1}" />
<img class="mobImg mobMinion" src="{{mob.minionImgUrl}}" (click)="showMobImage(mob)" *ngIf="mob.mobAmount>1" />
</div> </div>

View File

@ -1,6 +1,7 @@
.mobImg { .mobImg {
position: absolute; position: absolute;
z-index: 2; z-index: 2;
object-fit: contain;
&.roamingMonster { &.roamingMonster {
width: 95%; width: 95%;
max-height: 80%; max-height: 80%;
@ -12,6 +13,10 @@
max-height: 80%; max-height: 80%;
top: 40px; top: 40px;
left: 0; left: 0;
&.noMinions {
width: 95%;
max-height: 90%;
}
} }
&.mobMinion { &.mobMinion {
width: 60%; width: 60%;

View File

@ -119,7 +119,7 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
afterSpawn() { afterSpawn() {
this.cdRef.detectChanges(); this.cdRef.detectChanges();
this.md2Service.broadcastService.broadcastMobsInfo(); this.md2Service.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 });
} }
@ -150,7 +150,7 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
if (attacker) { if (attacker) {
attacker.exp += 1; attacker.exp += 1;
this.msgBoxService.show(`${attacker.heroFullName} Gain 1 Exp`, { icon: ADIcon.INFO }).pipe(first()).subscribe(result => { this.msgBoxService.show(`${attacker.heroFullName} Gain 1 Exp`, { icon: ADIcon.INFO }).pipe(first()).subscribe(result => {
this.md2Service.broadcastService.broadcastHeroInfoToOwner(attacker); this.md2Service.broadcastHeroInfoToOwner(attacker);
}); });
} }

View File

@ -1,16 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { MD2BroadcastService } from './md2-broadcast.service';
describe('MD2BroadcastService', () => {
let service: MD2BroadcastService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(MD2BroadcastService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@ -1,116 +0,0 @@
import { Injectable } from '@angular/core';
import { NbDialogService } from '@nebular/theme';
import { first } from 'rxjs/operators';
import { MD2HeroInfo } from '../../games/massive-darkness2/massive-darkness2.model';
import { FileService } from '../file.service';
import { GameRoomService } from '../game-room.service';
import { LoginUserService } from '../login-user.service';
import { SignalRService, SignalRSession, SignalRMessage } from '../signal-r.service';
import { MD2StateService } from './md2-state.service';
@Injectable({
providedIn: 'root'
})
export class MD2BroadcastService {
constructor(
public fileService: FileService,
private gameRoomService: GameRoomService,
private loginUserService: LoginUserService,
public stateService: MD2StateService,
public signalRService: SignalRService,
public dlgService: NbDialogService
) { }
public get playerHero(): MD2HeroInfo {
return this.stateService.playerHero;
}
public broadcastAllHeroInfoToAll() {
let message = {
receiver: { isGroup: true, sessionId: this.gameRoomService.gameRoomId } as SignalRSession,
from: { isGroup: false, sessionId: this.loginUserService.userAccess.signalRSessionId },
actionType: 'heroes',
actionName: 'updateAll',
} as SignalRMessage;
message.parameters = { heros: JSON.stringify(this.stateService.info.heros) };
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
});
this.broadcastMobsInfo();
}
public broadcastHeroAction(action: string, extraValue: any = null) {
let parameters = {};
parameters['tabId'] = this.loginUserService.sessionTabId;
parameters['extraValue'] = JSON.stringify(extraValue);
this.broadcastMessage('heroAction', action, parameters);
}
public broadcastHeroInfoToAll(hero: MD2HeroInfo) {
let message = {
receiver: { isGroup: true, sessionId: this.gameRoomService.gameRoomId } as SignalRSession,
from: { isGroup: false, sessionId: hero.playerInfo.signalRClientId },
actionType: 'hero',
actionName: 'update',
} as SignalRMessage;
message.parameters = { hero: JSON.stringify(hero) };
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
});
}
public broadcastHeroInfoToOwner(hero: MD2HeroInfo) {
let message = {
receiver: { isGroup: false, sessionId: hero.playerInfo.signalRClientId } as SignalRSession,
from: { isGroup: true, sessionId: this.gameRoomService.gameRoomId },
actionType: 'hero',
actionName: 'updateMyHero',
} as SignalRMessage;
message.parameters = { hero: JSON.stringify(hero) };
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
});
}
/**
* `sessionId` = null means broadcast to all
*/
public broadcastMessage(actionType: string,
actionName: string,
parameters: { [key: string]: string; } = {},
sessionId: string = null) {
let message = {
receiver: { isGroup: !sessionId, sessionId: sessionId } as SignalRSession,
from: { isGroup: false, sessionId: this.loginUserService.userAccess.signalRSessionId },
actionType: actionType,
actionName: actionName,
parameters: parameters
} as SignalRMessage;
if (sessionId == null) {
message.receiver = { isGroup: true, sessionId: this.gameRoomService.gameRoomId } as SignalRSession
} else {
message.receiver = { isGroup: false, sessionId: this.gameRoomService.gameRoomId } as SignalRSession
}
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
});
}
public broadcastGameInfo() {
let parameters = {};
parameters['gameInfo'] = JSON.stringify(this.stateService.info);
this.broadcastMessage('GameRoom', 'update', parameters);
}
public broadcastRoundPhase() {
let parameters = {};
parameters['phase'] = JSON.stringify(this.stateService.info.roundPhase);
this.broadcastMessage('GameRoom', 'phase', parameters);
}
public broadcastMobsInfo() {
let parameters = {};
parameters['roamingMonsters'] = JSON.stringify(this.stateService.info.roamingMonsters);
parameters['mobs'] = JSON.stringify(this.stateService.info.mobs);
this.broadcastMessage('mobs', 'update', parameters);
}
public broadcastMyHeroInfo() {
this.broadcastHeroInfoToAll(this.playerHero);
}
}

View File

@ -1,8 +1,8 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { DrawingBag, MobInfo, MobType, TreasureItem, TreasureType } from '../../games/massive-darkness2/massive-darkness2.model'; import { DrawingBag, MobInfo, MobType, TreasureItem, TreasureType } from '../../games/massive-darkness2/massive-darkness2.model';
import { MD2StateService } from './md2-state.service';
import { MD2MobInfoService } from '../../games/massive-darkness2/service/massive-darkness2.service'; import { MD2MobInfoService } from '../../games/massive-darkness2/service/massive-darkness2.service';
import { first } from 'rxjs/operators'; import { first } from 'rxjs/operators';
import { MD2Service } from './md2.service';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -10,13 +10,13 @@ import { first } from 'rxjs/operators';
export class MD2InitService { export class MD2InitService {
constructor( constructor(
private stateService: MD2StateService, private md2Service: MD2Service,
private mobInfoService: MD2MobInfoService, private mobInfoService: MD2MobInfoService,
) { } ) { }
public initMobDecks() { public initMobDecks() {
this.stateService.mobDeck = new DrawingBag(); this.md2Service.mobDeck = new DrawingBag();
this.stateService.roamingMobDeck = new DrawingBag(); this.md2Service.roamingMobDeck = new DrawingBag();
this.initCoreGameMobAndRoamingMonsters(); this.initCoreGameMobAndRoamingMonsters();
} }
@ -29,18 +29,18 @@ export class MD2InitService {
// } // }
// }); // });
this.mobInfoService.getAll().pipe(first()).subscribe(result => { this.mobInfoService.getAll().pipe(first()).subscribe(result => {
this.stateService.mobInfos = result.filter(m => m.type == MobType.Mob); this.md2Service.mobInfos = result.filter(m => m.type == MobType.Mob);
this.stateService.roamingMobInfos = result.filter(m => m.type == MobType.RoamingMonster); this.md2Service.roamingMobInfos = result.filter(m => m.type == MobType.RoamingMonster);
for (let i = 0; i < result.length; i++) { for (let i = 0; i < result.length; i++) {
const mobInfo = result[i]; const mobInfo = result[i];
for (let j = 0; j < mobInfo.mobLevelInfos.length; j++) { for (let j = 0; j < mobInfo.mobLevelInfos.length; j++) {
const levelInfo = mobInfo.mobLevelInfos[j]; const levelInfo = mobInfo.mobLevelInfos[j];
switch (mobInfo.type) { switch (mobInfo.type) {
case MobType.Mob: case MobType.Mob:
this.stateService.mobDeck.AddItem(new MobInfo({ name: mobInfo.name, level: levelInfo.level, drawingWeight: 1 })); this.md2Service.mobDeck.AddItem(new MobInfo({ name: mobInfo.name, level: levelInfo.level, drawingWeight: 1 }));
break; break;
case MobType.RoamingMonster: case MobType.RoamingMonster:
this.stateService.roamingMobDeck.AddItem(new MobInfo({ name: mobInfo.name, level: levelInfo.level, drawingWeight: 1 })); this.md2Service.roamingMobDeck.AddItem(new MobInfo({ name: mobInfo.name, level: levelInfo.level, drawingWeight: 1 }));
break; break;
} }
} }
@ -53,12 +53,12 @@ export class MD2InitService {
} }
public initTreasureBag() { public initTreasureBag() {
this.stateService.treasureBag.ClearAllItems(); this.md2Service.treasureBag.ClearAllItems();
this.addTreasure(TreasureType.Common, 15); this.addTreasure(TreasureType.Common, 15);
this.addTreasure(TreasureType.Rare, 5); this.addTreasure(TreasureType.Rare, 5);
} }
public addTreasure(type: TreasureType, amount: number = 1) { public addTreasure(type: TreasureType, amount: number = 1) {
this.stateService.treasureBag.AddItem(new TreasureItem(type, amount)); this.md2Service.treasureBag.AddItem(new TreasureItem(type, amount));
} }
} }

View File

@ -3,7 +3,6 @@ import { first } from 'rxjs/operators';
import { MobInfo, MobType } from '../../games/massive-darkness2/massive-darkness2.model'; import { MobInfo, MobType } from '../../games/massive-darkness2/massive-darkness2.model';
import { ADIcon } from '../../ui/alert-dlg/alert-dlg.model'; import { ADIcon } from '../../ui/alert-dlg/alert-dlg.model';
import { MsgBoxService } from '../msg-box.service'; import { MsgBoxService } from '../msg-box.service';
import { MD2StateService } from './md2-state.service';
import { MD2Service } from './md2.service'; import { MD2Service } from './md2.service';
@Injectable({ @Injectable({
@ -16,14 +15,13 @@ export class MD2MobService {
attackingAttackerReward: string = ''; attackingAttackerReward: string = '';
constructor( constructor(
private md2Service: MD2Service, private md2Service: MD2Service,
private stateService: MD2StateService,
private msgBoxService: MsgBoxService private msgBoxService: MsgBoxService
) { } ) { }
public get roamingMonsters() { public get roamingMonsters() {
return this.stateService.info.roamingMonsters; return this.md2Service.info.roamingMonsters;
} }
public get mobs() { public get mobs() {
return this.stateService.info.mobs; return this.md2Service.info.mobs;
} }
addOneUnit(mob: MobInfo) { addOneUnit(mob: MobInfo) {
@ -120,14 +118,14 @@ export class MD2MobService {
} }
this.attackingAllExp = 0; this.attackingAllExp = 0;
this.attackingAttackerExp = 0; this.attackingAttackerExp = 0;
this.md2Service.broadcastService.broadcastAllHeroInfoToAll(); this.md2Service.broadcastAllHeroInfoToAll();
this.md2Service.refreshUI$.next(); this.md2Service.refreshUI$.next();
}); });
} else if (this.attackingAttackerExp > 0 && attacker) { } else if (this.attackingAttackerExp > 0 && attacker) {
this.msgBoxService.show(`<b>${attackerTitle}</b> Gains ${this.attackingAttackerExp} Exp`, { icon: ADIcon.INFO }).pipe(first()).subscribe(result => { this.msgBoxService.show(`<b>${attackerTitle}</b> Gains ${this.attackingAttackerExp} Exp`, { icon: ADIcon.INFO }).pipe(first()).subscribe(result => {
attacker.exp += this.attackingAttackerExp; attacker.exp += this.attackingAttackerExp;
this.attackingAttackerExp = 0; this.attackingAttackerExp = 0;
this.md2Service.broadcastService.broadcastHeroInfoToOwner(attacker); this.md2Service.broadcastHeroInfoToOwner(attacker);
this.md2Service.refreshUI$.next(); this.md2Service.refreshUI$.next();
}); });

View File

@ -1,16 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { MD2StateService } from './md2-state.service';
describe('MD2StateService', () => {
let service: MD2StateService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(MD2StateService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@ -1,87 +0,0 @@
import { Injectable } from '@angular/core';
import { DrawingBag, MD2HeroInfo, MD2Icon, MobInfo, TreasureItem, TreasureType } from '../../games/massive-darkness2/massive-darkness2.model';
import { FileService } from '../file.service';
import { MD2GameInfo } from './md2.service';
import { MD2MobInfo } from '../../games/massive-darkness2/massive-darkness2.db.model';
@Injectable({
providedIn: 'root'
})
export class MD2StateService {
private _highestPlayerLevel: number = 1;
private _playerAmount: number = 2;
public info: MD2GameInfo;
public playerHero: MD2HeroInfo;
public mobInfos: MD2MobInfo[] = [];
public roamingMobInfos: MD2MobInfo[] = [];
public mobDeck: DrawingBag<MobInfo>;
public roamingMobDeck: DrawingBag<MobInfo>;
public treasureBag: DrawingBag<TreasureItem> = new DrawingBag<TreasureItem>();
constructor(
public fileService: FileService,
) { }
public iconHtml(icon: MD2Icon, cssClass = '') {
if (icon == MD2Icon.Fire) {
cssClass += ' g-color-google-plus ';
}
if (icon == MD2Icon.Frost || icon == MD2Icon.Mana) {
cssClass += ' g-color-aqua ';
}
if (icon < MD2Icon.RedDice) {
return `<span md2-icon='${String.fromCharCode(65 + icon)}' class='MD2Icon ${cssClass}'>${String.fromCharCode(65 + icon)}</span>`
} else if (icon < MD2Icon.TreasureToken) {
return `<span md2-icon='${String.fromCharCode(65 + icon)}' class='MD2Icon dice ${MD2Icon[icon].replace('Dice', '')} ${cssClass}'></span>`;
} else {
if (!cssClass) {
cssClass = 'g-height-25 mr-1';
}
//image based icons
switch (icon) {
case MD2Icon.HP_Color:
return this.imgHtml('HeartIcon.png', cssClass);
case MD2Icon.Mana_Color:
return this.imgHtml('ManaIcon.png', cssClass);
case MD2Icon.CorruptToken:
return this.imgHtml('Tokens/CorruptToken.png', cssClass);
case MD2Icon.TimeToken:
return this.imgHtml('Tokens/TimeToken.png', cssClass);
case MD2Icon.FireToken:
return this.imgHtml('Tokens/FireToken.png', cssClass);
case MD2Icon.FrozenToken:
return this.imgHtml('Tokens/FrozenToken.png', cssClass);
case MD2Icon.TreasureToken:
return this.imgHtml('TreasureToken/Cover.png', cssClass);
case MD2Icon.TreasureToken_Common:
return this.imgHtml('TreasureToken/Common.png', cssClass);
case MD2Icon.TreasureToken_Rare:
return this.imgHtml('TreasureToken/Rare.png', cssClass);
case MD2Icon.TreasureToken_Epic:
return this.imgHtml('TreasureToken/Epic.png', cssClass);
case MD2Icon.TreasureToken_Legendary:
return this.imgHtml('TreasureToken/Legendary.png', cssClass);
}
}
}
public imgHtml(imgPath: string, cssClass = 'g-height-25 mr-1') {
return `<img src='${this.imgUrl(imgPath)}' class='${cssClass}'>`;
}
public imgUrl(imgPath: string) {
return this.fileService.ImageUrl('MD2/' + imgPath);
}
public treasureImage(type: TreasureType) {
return this.imgUrl(`TreasureToken/${TreasureType[type]}.png`);
}
public treasureImageHtml(type: TreasureType) {
return this.imgHtml(`TreasureToken/${TreasureType[type]}.png`, 'g-height-40 mr-1');
}
}

View File

@ -11,9 +11,7 @@ import { FileService } from '../file.service';
import { GameRoomService } from '../game-room.service'; import { GameRoomService } from '../game-room.service';
import { LoginUserService } from '../login-user.service'; import { LoginUserService } from '../login-user.service';
import { MsgBoxService } from '../msg-box.service'; import { MsgBoxService } from '../msg-box.service';
import { SignalRService, SignalRSession, SignalRMessage } from '../signal-r.service'; import { SignalRService, SignalRClient, SignalRMessage } from '../signal-r.service';
import { MD2StateService } from './md2-state.service';
import { MD2BroadcastService } from './md2-broadcast.service';
import { MD2Logic } from '../../games/massive-darkness2/massive-darkness2.logic'; import { MD2Logic } from '../../games/massive-darkness2/massive-darkness2.logic';
import { MD2InitService } from './md2-init.service'; import { MD2InitService } from './md2-init.service';
import { ArrayUtils } from '../../utilities/array-utils'; import { ArrayUtils } from '../../utilities/array-utils';
@ -29,6 +27,17 @@ const MD2_IMG_URL = (id: string = null) => { return `${environment.apiUrl}/Files
export class MD2Service { export class MD2Service {
// #region Properties (24) // #region Properties (24)
private _highestPlayerLevel: number = 1;
private _playerAmount: number = 2;
public info: MD2GameInfo;
public playerHero: MD2HeroInfo;
public mobInfos: MD2MobInfo[] = [];
public roamingMobInfos: MD2MobInfo[] = [];
public mobDeck: DrawingBag<MobInfo>;
public roamingMobDeck: DrawingBag<MobInfo>;
public treasureBag: DrawingBag<TreasureItem> = new DrawingBag<TreasureItem>();
public darknessPhaseRule: IDarknessPhaseRule; public darknessPhaseRule: IDarknessPhaseRule;
public enemyPhaseMobs: MobInfo[]; public enemyPhaseMobs: MobInfo[];
public enemyPhaseSubject = new Subject<MobInfo>(); public enemyPhaseSubject = new Subject<MobInfo>();
@ -42,45 +51,34 @@ export class MD2Service {
public get heros() { public get heros() {
return this.stateService.info.heros; return this.info.heros;
} }
public get roamingMonsters() { public get roamingMonsters() {
return this.stateService.info.roamingMonsters; return this.info.roamingMonsters;
} }
public get mobs() { public get mobs() {
return this.stateService.info.mobs; return this.info.mobs;
} }
public get allRoamingMonsterInfos() { public get allRoamingMonsterInfos() {
return this.stateService.roamingMobInfos; return this.roamingMobInfos;
} }
public get allMobInfos() { public get allMobInfos() {
return this.stateService.mobInfos; return this.mobInfos;
}
public get info(): MD2GameInfo {
return this.stateService.info;
}
public set info(v: MD2GameInfo) {
this.stateService.info = v;
} }
// #endregion Properties (24) // #endregion Properties (24)
// #region Constructors (1) // #region Constructors (1)
constructor( constructor(
public fileService: FileService, public fileService: FileService,
public msgBoxService: MsgBoxService, public msgBoxService: MsgBoxService,
private gameRoomService: GameRoomService, public gameRoomService: GameRoomService,
public loginUserService: LoginUserService, public loginUserService: LoginUserService,
public stateService: MD2StateService,
public signalRService: SignalRService, public signalRService: SignalRService,
public dlgService: NbDialogService, public dlgService: NbDialogService
public broadcastService: MD2BroadcastService
) { ) {
this.darknessPhaseRule = new CoreGameDarknessPhaseRule(); this.darknessPhaseRule = new CoreGameDarknessPhaseRule();
this.stateService.info = new MD2GameInfo(); this.info = new MD2GameInfo();
this.darknessPhaseRule.addTreasureToken.subscribe(treasureType => { this.darknessPhaseRule.addTreasureToken.subscribe(treasureType => {
this.addTreasure(treasureType, 1); this.addTreasure(treasureType, 1);
}); });
@ -105,11 +103,8 @@ export class MD2Service {
} }
} }
public get playerHero(): MD2HeroInfo {
return this.stateService.playerHero;
}
public get currentActivateHero(): MD2HeroInfo { public get currentActivateHero(): MD2HeroInfo {
return this.stateService.info.heros.find(h => h.uiActivating); return this.info.heros.find(h => h.uiActivating);
} }
@ -119,9 +114,6 @@ export class MD2Service {
// #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));
@ -131,17 +123,17 @@ export class MD2Service {
public darknessPhase() { public darknessPhase() {
this.heros.forEach(hero => { this.heros.forEach(hero => {
hero.remainActions = 3; hero.remainActions = 3;
this.broadcastService.broadcastHeroInfoToOwner(hero); this.broadcastHeroInfoToOwner(hero);
}); });
this.stateService.info.roundPhase = RoundPhase.HeroPhase; this.info.roundPhase = RoundPhase.HeroPhase;
if (!this.stateService.info.isBossFight) { if (!this.info.isBossFight) {
this.stateService.info.round++; this.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.info.round)} Hero Phase`, { icon: ADIcon.INFO });
} }
} else { } else {
this.stateService.info.boss.darknessPhase(); this.info.boss.darknessPhase();
this.msgBoxService.show(`Boss Fight - ${NumberUtils.Ordinal(this.stateService.info.boss.rounds)} Hero Phase`, { icon: ADIcon.INFO }); this.msgBoxService.show(`Boss Fight - ${NumberUtils.Ordinal(this.info.boss.rounds)} Hero Phase`, { icon: ADIcon.INFO });
} }
//this.runNextPhase(); //this.runNextPhase();
@ -157,9 +149,9 @@ export class MD2Service {
level = 5; level = 5;
} }
if (isRoamingMonster) { if (isRoamingMonster) {
mobDeck = this.stateService.roamingMobDeck; mobDeck = this.roamingMobDeck;
} else { } else {
mobDeck = this.stateService.mobDeck; mobDeck = this.mobDeck;
} }
if (mobDeck.drawingItems.filter(m => (m as MobInfo).level == level) if (mobDeck.drawingItems.filter(m => (m as MobInfo).level == level)
@ -298,9 +290,9 @@ export class MD2Service {
} else { } else {
this.info.boss = new BossMicheal(this); this.info.boss = new BossMicheal(this);
} }
this.stateService.info.roamingMonsters = []; this.info.roamingMonsters = [];
this.stateService.info.mobs = []; this.info.mobs = [];
this.stateService.info.isBossFight = true; this.info.isBossFight = true;
this.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.hp = this.info.boss.info.hpPerHero * this.info.heros.length;
this.info.boss.info.unitRemainHp = this.info.boss.info.hp; this.info.boss.info.unitRemainHp = this.info.boss.info.hp;
@ -314,14 +306,14 @@ export class MD2Service {
hero.uiActivating = false; hero.uiActivating = false;
hero.uiBossFight = true; hero.uiBossFight = true;
}); });
this.broadcastService.broadcastAllHeroInfoToAll(); this.broadcastGameInfo();
} }
}); });
//this.sendMsgboxMsg //this.sendMsgboxMsg
} }
public activateBoss() { public activateBoss() {
this.stateService.info.boss.activating(); this.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);
@ -342,8 +334,8 @@ export class MD2Service {
hero.mp += levelUpInfo.extraMp; hero.mp += levelUpInfo.extraMp;
hero.mpMaximum += levelUpInfo.extraMp; hero.mpMaximum += levelUpInfo.extraMp;
hero.exp = levelUpInfo.currentExp; hero.exp = levelUpInfo.currentExp;
this.broadcastService.broadcastHeroInfoToOwner(hero); this.broadcastHeroInfoToOwner(hero);
this.sendMsgboxMsg(hero.playerInfo.signalRClientId, { title: 'Level Up', text: 'Please do a skill level up!', icon: ADIcon.INFO }); this.sendMsgboxMsg(hero.playerInfo.signalRClientId, { title: `Level Up Lv.${hero.level}`, text: 'Please do a skill level up!', icon: ADIcon.INFO });
levelUpInfo = MD2Rules.checkCoreGameLevelup(hero.level, hero.exp); levelUpInfo = MD2Rules.checkCoreGameLevelup(hero.level, hero.exp);
} }
} }
@ -362,9 +354,9 @@ export class MD2Service {
level = 3; level = 3;
} }
if (isRoamingMonsters) { if (isRoamingMonsters) {
return this.stateService.imgUrl(`Mobs/CoreGame/RoamingMonsters/${name}/${level}.png`); return this.imgUrl(`Mobs/CoreGame/RoamingMonsters/${name}/${level}.png`);
} else { } else {
return this.stateService.imgUrl(`Mobs/CoreGame/Mobs/${name}/${level}.png`); return this.imgUrl(`Mobs/CoreGame/Mobs/${name}/${level}.png`);
} }
} }
@ -374,7 +366,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; this.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 },
@ -389,12 +381,12 @@ export class MD2Service {
public runNextPhase() { public runNextPhase() {
this.stateService.info.roundPhase++; this.info.roundPhase++;
switch (this.stateService.info.roundPhase) { switch (this.info.roundPhase) {
case RoundPhase.HeroPhase: case RoundPhase.HeroPhase:
this.heros.forEach(hero => { this.heros.forEach(hero => {
hero.remainActions = 3; hero.remainActions = 3;
this.broadcastService.broadcastHeroInfoToOwner(hero); this.broadcastHeroInfoToOwner(hero);
}); });
break; break;
case RoundPhase.EnemyPhase: case RoundPhase.EnemyPhase:
@ -409,14 +401,14 @@ export class MD2Service {
default: break; default: break;
} }
let parameters = {}; let parameters = {};
parameters['phase'] = this.stateService.info.roundPhase; parameters['phase'] = this.info.roundPhase;
this.broadcastService.broadcastMessage('roundPhase', '', parameters); this.broadcastMessage('roundPhase', '', parameters);
} }
public sendMsgboxMsg(playerSessionId: string, msg: Partial<MessageBoxConfig>) { public sendMsgboxMsg(playerSessionId: string, msg: Partial<MessageBoxConfig>) {
let message = { let message = {
receiver: { isGroup: false, sessionId: playerSessionId } as SignalRSession, receiver: { isGroup: false, connectionId: playerSessionId } as SignalRClient,
from: { isGroup: false, sessionId: this.loginUserService.userAccess.signalRSessionId }, from: { isGroup: false, connectionId: this.loginUserService.userAccess.signalRConnectionId },
actionType: 'message', actionType: 'message',
actionName: 'popup', actionName: 'popup',
} as SignalRMessage; } as SignalRMessage;
@ -426,7 +418,7 @@ export class MD2Service {
} }
public getTargetHerosByFilter(targetType: AttackTarget, onlyOne: boolean = false) { public getTargetHerosByFilter(targetType: AttackTarget, onlyOne: boolean = false) {
return MD2Logic.getTargetHerosByFilter(this.stateService.info.heros, targetType, onlyOne); return MD2Logic.getTargetHerosByFilter(this.info.heros, targetType, onlyOne);
} }
public getTargetHerosHtml(beenAttackedHero: MD2HeroInfo[]) { public getTargetHerosHtml(beenAttackedHero: MD2HeroInfo[]) {
@ -449,32 +441,193 @@ export class MD2Service {
default: default:
break; break;
} }
this.broadcastService.broadcastHeroInfoToAll(this.currentActivateHero); this.broadcastHeroInfoToAll(this.currentActivateHero, true);
this.broadcastService.broadcastHeroInfoToOwner(this.currentActivateHero); //this.broadcastHeroInfoToOwner(this.currentActivateHero);
this.msgBoxService.show( this.msgBoxService.show(
this.stateService.imgHtml('/Fountain/Cover.png', '') this.imgHtml('/Fountain/Cover.png', '')
, { text: `${this.currentActivateHero.heroFullName} Recovered ${result.description}.` }); , { text: `${this.currentActivateHero.heroFullName} Recovered ${result.description}.` });
} }
public openTreasureChest() { public openTreasureChest() {
let result = new DrawingBag<DrawingItem>([ let result = new DrawingBag<DrawingItem>([
new DrawingItem('Common', `${this.stateService.treasureImageHtml(TreasureType.Common)} x 2`, '', 10), new DrawingItem('Common', `${this.treasureImageHtml(TreasureType.Common)} x 2`, '', 10),
new DrawingItem('Open Treasure Chest', `${this.stateService.treasureImageHtml(TreasureType.Rare)} x 2`, '', 5), new DrawingItem('Open Treasure Chest', `${this.treasureImageHtml(TreasureType.Rare)} x 2`, '', 5),
]) ])
.Draw(1)[0]; .Draw(1)[0];
this.msgBoxService.show( this.msgBoxService.show(
this.stateService.imgHtml('/TreasureChest/SmallTresureChest.jpg', ''), { text: `${this.currentActivateHero.heroFullName} gets ${result.description}.` }); this.imgHtml('/TreasureChest/SmallTresureChest.jpg', ''), { text: `${this.currentActivateHero.heroFullName} gets ${result.description}.` });
} }
public openGreatTreasureChest() { public openGreatTreasureChest() {
let result = new DrawingBag<DrawingItem>([ let result = new DrawingBag<DrawingItem>([
new DrawingItem('Common', `${this.stateService.treasureImageHtml(TreasureType.Rare)} x 3`, '', 3), new DrawingItem('Common', `${this.treasureImageHtml(TreasureType.Rare)} x 3`, '', 3),
new DrawingItem('Open Treasure Chest', `${this.stateService.treasureImageHtml(TreasureType.Epic)} x 3`, '', 1), new DrawingItem('Open Treasure Chest', `${this.treasureImageHtml(TreasureType.Epic)} x 3`, '', 1),
]).Draw(1)[0]; ]).Draw(1)[0];
this.msgBoxService.show( this.msgBoxService.show(
this.stateService.imgHtml('/TreasureChest/BigTresureChest.png', ''), { text: `${this.currentActivateHero.heroFullName} gets ${result.description}.` }); this.imgHtml('/TreasureChest/BigTresureChest.png', ''), { text: `${this.currentActivateHero.heroFullName} gets ${result.description}.` });
} }
public iconHtml(icon: MD2Icon, cssClass = '') {
if (icon == MD2Icon.Fire) {
cssClass += ' g-color-google-plus ';
}
if (icon == MD2Icon.Frost || icon == MD2Icon.Mana) {
cssClass += ' g-color-aqua ';
}
if (icon < MD2Icon.RedDice) {
return `<span md2-icon='${String.fromCharCode(65 + icon)}' class='MD2Icon ${cssClass}'>${String.fromCharCode(65 + icon)}</span>`
} else if (icon < MD2Icon.TreasureToken) {
return `<span md2-icon='${String.fromCharCode(65 + icon)}' class='MD2Icon dice ${MD2Icon[icon].replace('Dice', '')} ${cssClass}'></span>`;
} else {
if (!cssClass) {
cssClass = 'g-height-25 mr-1';
}
//image based icons
switch (icon) {
case MD2Icon.HP_Color:
return this.imgHtml('HeartIcon.png', cssClass);
case MD2Icon.Mana_Color:
return this.imgHtml('ManaIcon.png', cssClass);
case MD2Icon.CorruptToken:
return this.imgHtml('Tokens/CorruptToken.png', cssClass);
case MD2Icon.TimeToken:
return this.imgHtml('Tokens/TimeToken.png', cssClass);
case MD2Icon.FireToken:
return this.imgHtml('Tokens/FireToken.png', cssClass);
case MD2Icon.FrozenToken:
return this.imgHtml('Tokens/FrozenToken.png', cssClass);
case MD2Icon.TreasureToken:
return this.imgHtml('TreasureToken/Cover.png', cssClass);
case MD2Icon.TreasureToken_Common:
return this.imgHtml('TreasureToken/Common.png', cssClass);
case MD2Icon.TreasureToken_Rare:
return this.imgHtml('TreasureToken/Rare.png', cssClass);
case MD2Icon.TreasureToken_Epic:
return this.imgHtml('TreasureToken/Epic.png', cssClass);
case MD2Icon.TreasureToken_Legendary:
return this.imgHtml('TreasureToken/Legendary.png', cssClass);
}
}
}
public imgHtml(imgPath: string, cssClass = 'g-height-25 mr-1') {
return `<img src='${this.imgUrl(imgPath)}' class='${cssClass}'>`;
}
public imgUrl(imgPath: string) {
return this.fileService.ImageUrl('MD2/' + imgPath);
}
public treasureImage(type: TreasureType) {
return this.imgUrl(`TreasureToken/${TreasureType[type]}.png`);
}
public treasureImageHtml(type: TreasureType) {
return this.imgHtml(`TreasureToken/${TreasureType[type]}.png`, 'g-height-40 mr-1');
}
public broadcastAllHeroInfoToAll() {
let message = {
receiver: { isGroup: true, connectionId: this.gameRoomService.gameRoomId } as SignalRClient,
from: { isGroup: false, connectionId: this.loginUserService.userAccess.signalRConnectionId },
actionType: 'heroes',
actionName: 'updateAll',
} as SignalRMessage;
message.parameters = { heros: JSON.stringify(this.info.heros) };
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
});
this.broadcastMobsInfo();
}
public broadcastHeroAction(action: string, extraValue: any = null) {
let parameters = {};
parameters['tabId'] = this.loginUserService.sessionTabId;
parameters['extraValue'] = JSON.stringify(extraValue);
this.broadcastMessage('heroAction', action, parameters);
}
public broadcastHeroInfoToAll(hero: MD2HeroInfo, fromDashboard: boolean = false) {
let message = {
receiver: { isGroup: true, connectionId: this.gameRoomService.gameRoomId } as SignalRClient,
from: { isGroup: fromDashboard, connectionId: fromDashboard ? this.gameRoomService.gameRoomId : hero.playerInfo.signalRClientId },
actionType: 'hero',
actionName: 'update',
} as SignalRMessage;
message.parameters = { hero: JSON.stringify(hero) };
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
});
}
public broadcastHeroInfoToOwner(hero: MD2HeroInfo) {
let message = {
receiver: { isGroup: false, connectionId: hero.playerInfo.signalRClientId } as SignalRClient,
from: { isGroup: true, connectionId: this.gameRoomService.gameRoomId },
actionType: 'hero',
actionName: 'updateMyHero',
} as SignalRMessage;
message.parameters = { hero: JSON.stringify(hero) };
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
});
}
/**
* `sessionId` = null means broadcast to all
*/
public broadcastMessage(actionType: string,
actionName: string,
parameters: { [key: string]: string; } = {},
sessionId: string = null) {
let message = {
receiver: { isGroup: !sessionId, connectionId: sessionId } as SignalRClient,
from: { isGroup: false, connectionId: this.loginUserService.userAccess.signalRConnectionId },
actionType: actionType,
actionName: actionName,
parameters: parameters
} as SignalRMessage;
if (sessionId == null) {
message.receiver = { isGroup: true, connectionId: this.gameRoomService.gameRoomId } as SignalRClient
} else {
message.receiver = { isGroup: false, connectionId: this.gameRoomService.gameRoomId } as SignalRClient
}
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
});
}
public broadcastGameInfo() {
let parameters = {};
if (this.info.boss) {
this.info.boss.md2Service = undefined;
}
parameters['gameInfo'] = JSON.stringify(this.info);
if (this.info.boss) {
this.info.boss.md2Service = this;
}
this.broadcastMessage('GameRoom', 'update', parameters);
}
broadcastFetchGameInfo() {
this.broadcastMessage('GameRoom', 'getGameInfo', {});
}
public broadcastRoundPhase() {
let parameters = {};
parameters['phase'] = JSON.stringify(this.info.roundPhase);
this.broadcastMessage('GameRoom', 'phase', parameters);
}
public broadcastMobsInfo() {
let parameters = {};
parameters['roamingMonsters'] = JSON.stringify(this.info.roamingMonsters);
parameters['mobs'] = JSON.stringify(this.info.mobs);
this.broadcastMessage('mobs', 'update', parameters);
}
public broadcastMyHeroInfo() {
this.broadcastHeroInfoToAll(this.playerHero);
}
// #endregion Public Methods (27) // #endregion Public Methods (27)
} }
@ -491,7 +644,7 @@ export class MD2GameInfo {
this.mobs = this.mobs.map(m => new MobInfo(m)); this.mobs = this.mobs.map(m => new MobInfo(m));
this.roamingMonsters = this.roamingMonsters.map(m => new MobInfo(m)); this.roamingMonsters = this.roamingMonsters.map(m => new MobInfo(m));
if (this.boss.info) { if (this.boss && this.boss.info) {
this.boss.info = new MobInfo(this.boss.info); this.boss.info = new MobInfo(this.boss.info);
} }
this.heros = this.heros.map(h => new MD2HeroInfo(h)); this.heros = this.heros.map(h => new MD2HeroInfo(h));
@ -502,6 +655,7 @@ export class MD2GameInfo {
public mobs: MobInfo[] = []; public mobs: MobInfo[] = [];
public roamingMonsters: MobInfo[] = []; public roamingMonsters: MobInfo[] = [];
public heros: MD2HeroInfo[] = []; public heros: MD2HeroInfo[] = [];
public disconnectedHeroes: MD2HeroInfo[] = [];
public round = 1; public round = 1;
public roundPhase: RoundPhase = RoundPhase.HeroPhase; public roundPhase: RoundPhase = RoundPhase.HeroPhase;
public showAttackBtn: boolean = false; public showAttackBtn: boolean = false;

View File

@ -1,15 +0,0 @@
import { Injectable } from "@angular/core";
import { MD2StateService } from "./md2-state.service";
@Injectable({
providedIn: 'root'
})
export class MD2SpawnMobService {
/**
*
*/
constructor(
public stateService: MD2StateService,) {
}
}

View File

@ -23,18 +23,19 @@ export class GameRoomService {
return { return {
name: this.loginUserService.userAccess.firstName, name: this.loginUserService.userAccess.firstName,
id: this.loginUserService.userAccess.memberId, id: this.loginUserService.userAccess.memberId,
signalRClientId: this.loginUserService.userAccess.signalRSessionId, signalRClientId: this.loginUserService.userAccess.signalRConnectionId,
isPlayer: true, isPlayer: true,
gameRoomId: this.gameRoomId, gameRoomId: this.gameRoomId,
tabId: this.loginUserService.sessionTabId tabId: this.loginUserService.sessionTabId
} as IGamePlayer; } as IGamePlayer;
} }
createGameRoom(name: string) { createGameRoom(name: string) {
let gameRoom = this.currentPlayer(); //Using current player to create game room
gameRoom.gameRoomId = gameRoom.id; let currentPlayer = this.currentPlayer();
this.gameRoomId = gameRoom.id; currentPlayer.gameRoomId = currentPlayer.id;
this.gameRoomId = currentPlayer.id;
this.http.post<boolean>(GAME_ROOM_URL, this.http.post<boolean>(GAME_ROOM_URL,
JSON.stringify(gameRoom), { JSON.stringify(currentPlayer), {
headers: { headers: {
'Accept': 'application/json', 'Accept': 'application/json',
'Content-Type': 'application/json', 'Content-Type': 'application/json',

View File

@ -14,7 +14,7 @@ export class LoginUserService {
} }
setSignalRConnectionId(id: string) { setSignalRConnectionId(id: string) {
this.userAccess.signalRSessionId = id; this.userAccess.signalRConnectionId = id;
} }
public get sessionTabId(): string { public get sessionTabId(): string {

View File

@ -15,7 +15,7 @@ const SIGNAL_R_URL = (id: string = null) => { return `${environment.apiUrl}/${id
}) })
export class SignalRService { export class SignalRService {
ReceivedSignalRMessageSubject = new Subject<SignalRMessage>(); ReceivedSignalRMessageSubject = new Subject<SignalRMessage>();
signalRMessageConnSubject = new Subject<any>(); signalRMessageConnSubject = new Subject<SignalRConnectionState>();
private hubConnection: signalR.HubConnection private hubConnection: signalR.HubConnection
constructor( constructor(
private loginUserService: LoginUserService, private loginUserService: LoginUserService,
@ -42,6 +42,7 @@ export class SignalRService {
} }
public startSignalRConnection(gameRoomId: string = '') { public startSignalRConnection(gameRoomId: string = '') {
this.signalRMessageConnSubject.next({ status: 'connecting' });
if (!this.loginUserService.sessionTabId) { if (!this.loginUserService.sessionTabId) {
this.loginUserService.sessionTabId = UuidUtils.generate(); this.loginUserService.sessionTabId = UuidUtils.generate();
} }
@ -65,13 +66,21 @@ export class SignalRService {
.withUrl(`${SIGNAL_R_URL('GameRoomHub')}?${params.toString()}`) .withUrl(`${SIGNAL_R_URL('GameRoomHub')}?${params.toString()}`)
.withAutomaticReconnect() .withAutomaticReconnect()
.build(); .build();
this.registerHubConnectionLifecycleHandlers();
let me = this let me = this
this.hubConnection this.hubConnection
.start() .start()
.then(() => { .then(() => {
me.setSignalRConnectionId(this.hubConnection.connectionId); me.setSignalRConnectionId(this.hubConnection.connectionId);
this.signalRMessageConnSubject.next({
status: 'connected',
connectionId: this.hubConnection.connectionId
});
})
.catch(err => {
console.log('Error while starting connection: ' + err);
this.signalRMessageConnSubject.next({ status: 'error', error: err });
}) })
.catch(err => console.log('Error while starting connection: ' + err))
this.hubConnection.on("ReceivedMessage", (jsonString) => { this.hubConnection.on("ReceivedMessage", (jsonString) => {
this.BroadcastAPIMessageReceive(JSON.parse(jsonString)); this.BroadcastAPIMessageReceive(JSON.parse(jsonString));
}); });
@ -79,12 +88,37 @@ export class SignalRService {
} }
public setSignalRConnectionId(connectionId: string) { public setSignalRConnectionId(connectionId: string) {
this.loginUserService.userAccess.signalRSessionId = connectionId; this.loginUserService.userAccess.signalRConnectionId = connectionId;
this.loginUserService.signalRInitialized.next(connectionId); this.loginUserService.signalRInitialized.next(connectionId);
console.log('GameRoomHub start connection: ' + this.hubConnection.connectionId) console.log('GameRoomHub start connection: ' + this.hubConnection.connectionId)
} }
private registerHubConnectionLifecycleHandlers() {
this.hubConnection.onreconnecting(error => {
console.warn('SignalR reconnecting...', error);
this.signalRMessageConnSubject.next({ status: 'reconnecting', error });
});
this.hubConnection.onreconnected(connectionId => {
console.log('SignalR reconnected with new connectionId', connectionId);
this.setSignalRConnectionId(connectionId);
this.signalRMessageConnSubject.next({ status: 'reconnected', connectionId });
});
this.hubConnection.onclose(error => {
console.warn('SignalR connection closed', error);
this.clearSignalRConnectionId();
this.signalRMessageConnSubject.next({ status: 'closed', error });
});
}
private clearSignalRConnectionId() {
if (this.loginUserService?.userAccess) {
this.loginUserService.userAccess.signalRConnectionId = null;
}
}
BroadcastAPIMessageReceive(msg: SignalRMessage) { BroadcastAPIMessageReceive(msg: SignalRMessage) {
@ -102,16 +136,23 @@ export class SignalRService {
} }
export interface SignalRMessage { export interface SignalRMessage {
from: SignalRSession; from: SignalRClient;
receiver: SignalRSession; receiver: SignalRClient;
actionType: string; actionType: string;
actionName: string; actionName: string;
parameters: { [key: string]: string; }; parameters: { [key: string]: string; };
value: any;
jsonValue: string; jsonValue: string;
} }
export interface SignalRSession { export interface SignalRClient {
sessionId: string; connectionId: string;
name: string; name: string;
isGroup: boolean; isGroup: boolean;
} }
export interface SignalRConnectionState {
status: 'connecting' | 'connected' | 'reconnecting' | 'reconnected' | 'closed' | 'error';
connectionId?: string;
error?: any;
}

View File

@ -24,3 +24,9 @@ p {
margin-top: 0; margin-top: 0;
margin-bottom: 0; margin-bottom: 0;
} }
button,
a,
input {
touch-action: manipulation;
}

View File

@ -6,8 +6,9 @@
<title>真幸福的幸福小組</title> <title>真幸福的幸福小組</title>
<base href="/"> <base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" href="favicon.png"> <link rel="icon" type="image/png" href="favicon.png">
<link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="icon" type="image/x-icon" href="favicon.ico">
<script defer <script defer