WIP
This commit is contained in:
parent
6301d6008b
commit
b46392bc41
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -5,6 +5,7 @@
|
||||
"git.decorations.enabled": false,
|
||||
"workbench.editor.decorations.badges": false,
|
||||
"workbench.editor.decorations.colors": false,
|
||||
"typescript.preferences.includePackageJsonAutoImports": "on",
|
||||
"vscode_custom_css.imports": [
|
||||
"file:///C:/VScode/custom.css"
|
||||
],
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
"ng": "ng",
|
||||
"conventional-changelog": "conventional-changelog",
|
||||
"start": "ng serve",
|
||||
"build": "ng build --output-path \\\\ArkNAS\\docker\\nginx-proxy\\data\\ChurchAngular --prod",
|
||||
"build": "ng build --output-path \\\\ArkNAS\\docker\\nginx-proxy\\data\\ChurchAngular --configuration production",
|
||||
"build:prod": "npm run build -- --configuration production --aot",
|
||||
"test": "ng test",
|
||||
"test:coverage": "rimraf coverage && npm run test -- --code-coverage",
|
||||
|
||||
@ -6,10 +6,10 @@ import { LayoutService } from '../../../@core/utils';
|
||||
import { map, takeUntil, first } from 'rxjs/operators';
|
||||
import { Subject } from 'rxjs';
|
||||
import { HeaderService } from '../../../services/header.service';
|
||||
import { NbAuthService } from '@nebular/auth';
|
||||
import { AuthService } from '../../../services/auth.service';
|
||||
import { UserProfileAction } from '../../../entity/Auth';
|
||||
import { Router } from '@angular/router';
|
||||
import { LoginUserService } from '../../../services/login-user.service';
|
||||
import { AuthService } from '../../../services/auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-header',
|
||||
@ -54,7 +54,7 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
||||
}];
|
||||
|
||||
public get user() {
|
||||
return this.authService.userAccess;
|
||||
return this.loginUserService.userAccess;
|
||||
}
|
||||
public get userFullName() {
|
||||
if (this.user) {
|
||||
@ -78,7 +78,7 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
||||
private layoutService: LayoutService,
|
||||
private breakpointService: NbMediaBreakpointsService,
|
||||
private headerService: HeaderService,
|
||||
private oAuthService: NbAuthService,
|
||||
private loginUserService: LoginUserService,
|
||||
private authService: AuthService,
|
||||
protected router: Router,
|
||||
) {
|
||||
|
||||
@ -23,4 +23,4 @@ import { Component } from '@angular/core';
|
||||
</nb-layout>
|
||||
`,
|
||||
})
|
||||
export class OneColumnLayoutComponent {}
|
||||
export class OneColumnLayoutComponent { }
|
||||
|
||||
@ -73,7 +73,7 @@ export const routes: Routes = [
|
||||
path: 'games', loadChildren: () => import('./games/games.module').then(m => m.GamesModule),
|
||||
canActivateChild: [AuthGuardService],
|
||||
data: {
|
||||
roles: Role.Admin | Role.Pastor
|
||||
roles: Role.All
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@ -7,7 +7,6 @@ import { SessionService } from '../../services/session.service';
|
||||
import { LineService } from '../../services/line.service';
|
||||
import { HeaderService } from '../../services/header.service';
|
||||
import { StringUtils } from '../../utilities/string-utils';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { DateUtils } from '../../utilities/date-utils';
|
||||
import { NumberUtils } from '../../utilities/number-utils';
|
||||
import { CellGroupRoutineEventsService } from '../../services/crudServices/cell-group-routine-events.service';
|
||||
@ -15,6 +14,7 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { PastoralDomainService } from '../../services/crudServices/pastoral-domain.service';
|
||||
import { MyAppBase } from '../MyAppBase';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { LoginUserService } from '../../services/login-user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-dinner',
|
||||
@ -29,7 +29,7 @@ export class DinnerComponent extends MyAppBase {
|
||||
private sessionService: SessionService,
|
||||
private lineService: LineService,
|
||||
private cdRef: ChangeDetectorRef,
|
||||
private authService: AuthService,
|
||||
private loginUserService: LoginUserService,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected pastoralDomainService: PastoralDomainService
|
||||
@ -57,10 +57,10 @@ export class DinnerComponent extends MyAppBase {
|
||||
this.cellGroupRoutineEventsService.getComingEvent().pipe(first()).subscribe(result => {
|
||||
this.cellGroupEvent = result;
|
||||
|
||||
this.data = this.cellGroupEvent.attendees.find(a => a.id == this.authService.userAccess.memberId);
|
||||
this.data = this.cellGroupEvent.attendees.find(a => a.id == this.loginUserService.userAccess.memberId);
|
||||
if (!this.data) {
|
||||
this.data = new CellGroupRoutineEventAttendee(this.cellGroupEvent.id, this.authService.userAccess.memberId);
|
||||
this.data.name = this.authService.userAccess.firstName;
|
||||
this.data = new CellGroupRoutineEventAttendee(this.cellGroupEvent.id, this.loginUserService.userAccess.memberId);
|
||||
this.data.name = this.loginUserService.userAccess.firstName;
|
||||
this.cellGroupEvent.attendees.push(this.data);
|
||||
} else {
|
||||
this.potluckItems = this.data.potluckItem.split("|").map(s => new DinnerInfo(s));
|
||||
@ -95,9 +95,6 @@ export class DinnerComponent extends MyAppBase {
|
||||
this.potluckItems.push(new DinnerInfo(''));
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
logout() {
|
||||
this.authService.logout();
|
||||
}
|
||||
}
|
||||
|
||||
export class DinnerInfo {
|
||||
|
||||
@ -9,6 +9,7 @@ import { CellGroupRoutineEventsService } from '../../services/crudServices/cell-
|
||||
import { PastoralDomainService } from '../../services/crudServices/pastoral-domain.service';
|
||||
import { HeaderService } from '../../services/header.service';
|
||||
import { LineService } from '../../services/line.service';
|
||||
import { LoginUserService } from '../../services/login-user.service';
|
||||
import { MsgBoxService } from '../../services/msg-box.service';
|
||||
import { SessionService } from '../../services/session.service';
|
||||
import { StateService } from '../../services/state.service';
|
||||
@ -27,6 +28,7 @@ export class PrayerComponent extends MyAppBase {
|
||||
private cellGroupRoutineEventsService: CellGroupRoutineEventsService,
|
||||
private toastrService: NbToastrService,
|
||||
private cdRef: ChangeDetectorRef,
|
||||
private loginUserService: LoginUserService,
|
||||
private authService: AuthService,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
@ -53,9 +55,9 @@ export class PrayerComponent extends MyAppBase {
|
||||
this.cellGroupRoutineEventsService.getLastEvent().pipe(first()).subscribe(result => {
|
||||
this.cellGroupEvent = result;
|
||||
|
||||
this.data = this.cellGroupEvent.prayers.find(a => a.memberId == this.authService.userAccess.memberId);
|
||||
this.data = this.cellGroupEvent.prayers.find(a => a.memberId == this.loginUserService.userAccess.memberId);
|
||||
if (!this.data) {
|
||||
this.data = new CellGroupRoutineEventPrayer(this.cellGroupEvent.id, this.authService.userAccess.memberId);
|
||||
this.data = new CellGroupRoutineEventPrayer(this.cellGroupEvent.id, this.loginUserService.userAccess.memberId);
|
||||
this.cellGroupEvent.prayers.push(this.data);
|
||||
this.prayer = new BindingHelper(['']);
|
||||
} else {
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FamilyMember } from '../../entity/Member';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { FamilyMemberService } from '../../services/crudServices/family-member.service';
|
||||
import { SessionService } from '../../services/session.service';
|
||||
import { first } from "rxjs/operators";
|
||||
import { DropDownOptions } from '../../entity/dropDownOption';
|
||||
import { NbToastrService } from '@nebular/theme';
|
||||
import { LoginUserService } from '../../services/login-user.service';
|
||||
@Component({
|
||||
selector: 'ngx-user-profile',
|
||||
templateUrl: './user-profile.component.html',
|
||||
@ -16,13 +15,13 @@ export class UserProfileComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
private memberService: FamilyMemberService,
|
||||
private authService: AuthService,
|
||||
private loginUserService: LoginUserService,
|
||||
private toastrService: NbToastrService,
|
||||
) { }
|
||||
data: FamilyMember;
|
||||
processing: boolean;
|
||||
ngOnInit(): void {
|
||||
this.memberService.getById(this.authService.userAccess.memberId).pipe(first()).subscribe(result => {
|
||||
this.memberService.getById(this.loginUserService.userAccess.memberId).pipe(first()).subscribe(result => {
|
||||
this.data = result;
|
||||
if (this.data.dateOfBaptized == undefined) this.data.dateOfBaptized = null;
|
||||
if (this.data.dateOfWalkIn == undefined) this.data.dateOfWalkIn = null;
|
||||
|
||||
@ -20,6 +20,8 @@ export interface LoginTokenViewModel {
|
||||
avatarImage: string;
|
||||
role: Role;
|
||||
cellGroup: PastoralDomain;
|
||||
signalRSessionId;
|
||||
sessionTabId: string;
|
||||
}
|
||||
|
||||
export interface GoogleUserInfo {
|
||||
|
||||
@ -1,9 +1,41 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { NbMenuItem } from '@nebular/theme';
|
||||
import { AvalonComponent } from './avalon/avalon.component';
|
||||
import { GamesComponent } from './games.component';
|
||||
import { HeroDashboardComponent } from './massive-darkness2/hero-dashboard/hero-dashboard.component';
|
||||
import { MassiveDarkness2Component } from './massive-darkness2/massive-darkness2.component';
|
||||
export class GameRoomMenuConfig {
|
||||
public static HostMenu: NbMenuItem[] = [
|
||||
|
||||
{
|
||||
title: 'Dashboard',
|
||||
icon: 'people-outline',
|
||||
children: [
|
||||
{
|
||||
title: '小組禱告',
|
||||
//icon: 'people-outline',
|
||||
link: '/games/MD2'
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
public static PlayerMenu: NbMenuItem[] = [
|
||||
|
||||
{
|
||||
title: 'Hero Dashboard',
|
||||
icon: 'people-outline',
|
||||
children: [
|
||||
{
|
||||
title: '小組禱告',
|
||||
//icon: 'people-outline',
|
||||
link: '/myapp/prayer'
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '', component: GamesComponent
|
||||
@ -14,6 +46,7 @@ const routes: Routes = [
|
||||
{ path: 'avalon', component: AvalonComponent },
|
||||
{ path: 'avalonHost', component: AvalonComponent },
|
||||
{ path: 'MD2', component: MassiveDarkness2Component },
|
||||
{ path: 'MD2_Hero/:roomId', component: HeroDashboardComponent },
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
<ngx-one-column-layout>
|
||||
<!-- <nb-menu [items]="MENU_ITEMS"></nb-menu> -->
|
||||
<ngx-plain-layout>
|
||||
<!-- <nb-menu [items]="gameRoomService.sideMenu"></nb-menu> -->
|
||||
<router-outlet></router-outlet>
|
||||
</ngx-one-column-layout>
|
||||
</ngx-plain-layout>
|
||||
|
||||
<!-- ngx-plain-layout ngx-one-column-layout-->
|
||||
@ -1,5 +1,6 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { GameRoomService } from '../services/game-room.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-games',
|
||||
@ -8,7 +9,9 @@ import { Title } from '@angular/platform-browser';
|
||||
})
|
||||
export class GamesComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
constructor(
|
||||
public gameRoomService: GameRoomService
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
//this.browserTitleService.setTitle('我是遊戲王');
|
||||
|
||||
17
src/app/games/games.model.ts
Normal file
17
src/app/games/games.model.ts
Normal file
@ -0,0 +1,17 @@
|
||||
export interface IGamePlayer {
|
||||
id: string;
|
||||
name: string;
|
||||
gameRoomId: string;
|
||||
isPlayer: boolean;
|
||||
signalRClientId: string;
|
||||
tabId: string;
|
||||
}
|
||||
|
||||
export class GamePlayer implements IGamePlayer {
|
||||
id: string;
|
||||
name: string;
|
||||
gameRoomId: string;
|
||||
isPlayer: boolean;
|
||||
signalRClientId: string;
|
||||
tabId: string;
|
||||
}
|
||||
@ -4,7 +4,7 @@ import { QRCodeModule } from 'angular2-qrcode';
|
||||
import { GamesRoutingModule } from './games-routing.module';
|
||||
import { GamesComponent } from './games.component';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { NbMenuModule, NbInputModule, NbCardModule, NbButtonModule, NbActionsModule, NbCheckboxModule, NbRadioModule, NbDatepickerModule, NbSelectModule, NbIconModule, NbTagModule, NbStepperModule, NbListModule, NbUserModule, NbSpinnerModule, NbDialogModule, NbToggleModule } from '@nebular/theme';
|
||||
import { NbMenuModule, NbInputModule, NbCardModule, NbButtonModule, NbActionsModule, NbCheckboxModule, NbRadioModule, NbDatepickerModule, NbSelectModule, NbIconModule, NbTagModule, NbStepperModule, NbListModule, NbUserModule, NbSpinnerModule, NbDialogModule, NbToggleModule, NbAccordionModule, NbBadgeModule, NbAlertModule } from '@nebular/theme';
|
||||
import { ThemeModule } from '../@theme/theme.module';
|
||||
import { AdminRoutingModule } from '../admin/admin-routing.module';
|
||||
import { AlertDlgModule } from '../ui/alert-dlg/alert-dlg.module';
|
||||
@ -17,6 +17,21 @@ import { QuestVoteComponent } from './avalon/quest-vote/quest-vote.component';
|
||||
import { VoteResultComponent } from './avalon/vote-result/vote-result.component';
|
||||
import { QuestTableComponent } from './avalon/quest-table/quest-table.component';
|
||||
import { MassiveDarkness2Component } from './massive-darkness2/massive-darkness2.component';
|
||||
import { TreasureBagComponent } from './massive-darkness2/treasure-bag/treasure-bag.component';
|
||||
import { MobsComponent } from './massive-darkness2/mobs/mobs.component';
|
||||
import { CurrencyInputModule } from '../ui/currency-input/currency-input.module';
|
||||
import { DoorEventsComponent } from './massive-darkness2/door-events/door-events.component';
|
||||
import { HeroDashboardComponent } from './massive-darkness2/hero-dashboard/hero-dashboard.component';
|
||||
import { SpawnMobDlgComponent } from './massive-darkness2/mobs/spawn-mob-dlg/spawn-mob-dlg.component';
|
||||
import { MD2IconComponent } from './massive-darkness2/md2-icon/md2-icon.component';
|
||||
import { MobDetailInfoComponent } from './massive-darkness2/mobs/mob-detail-info/mob-detail-info.component';
|
||||
import { MD2HeroSelectComponent } from './massive-darkness2/md2-hero-select/md2-hero-select.component';
|
||||
import { DropDownListModule } from '../ui/drop-down-list/drop-down-list.module';
|
||||
import { BossFightComponent } from './massive-darkness2/boss-fight/boss-fight.component';
|
||||
import { BossActivationComponent } from './massive-darkness2/boss-fight/boss-activation/boss-activation.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 { MobCombatInfoComponent } from './massive-darkness2/mobs/mob-detail-info/mob-combat-info/mob-combat-info.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@ -30,7 +45,20 @@ import { MassiveDarkness2Component } from './massive-darkness2/massive-darkness2
|
||||
QuestVoteComponent,
|
||||
VoteResultComponent,
|
||||
QuestTableComponent,
|
||||
MassiveDarkness2Component
|
||||
MassiveDarkness2Component,
|
||||
TreasureBagComponent,
|
||||
MobsComponent,
|
||||
DoorEventsComponent,
|
||||
HeroDashboardComponent,
|
||||
SpawnMobDlgComponent,
|
||||
MD2IconComponent,
|
||||
MobDetailInfoComponent,
|
||||
MD2HeroSelectComponent,
|
||||
BossFightComponent,
|
||||
BossActivationComponent,
|
||||
MobAttackInfoComponent,
|
||||
MobDefInfoComponent,
|
||||
MobCombatInfoComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@ -54,9 +82,14 @@ import { MassiveDarkness2Component } from './massive-darkness2/massive-darkness2
|
||||
NbToggleModule,
|
||||
NbUserModule,
|
||||
NbSpinnerModule,
|
||||
NbAccordionModule,
|
||||
NbBadgeModule,
|
||||
NbAlertModule,
|
||||
CurrencyInputModule,
|
||||
NbDialogModule.forRoot(),
|
||||
AlertDlgModule,
|
||||
QRCodeModule,
|
||||
DropDownListModule
|
||||
]
|
||||
})
|
||||
export class GamesModule { }
|
||||
|
||||
230
src/app/games/massive-darkness2/MD2Base.ts
Normal file
230
src/app/games/massive-darkness2/MD2Base.ts
Normal file
@ -0,0 +1,230 @@
|
||||
import { ChangeDetectorRef, Injectable } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { Subject } from "rxjs";
|
||||
import { first, takeUntil } from "rxjs/operators";
|
||||
import { MD2GameInfo, MD2Service } from "../../services/md2.service";
|
||||
import { SignalRMessage } from "../../services/signal-r.service";
|
||||
import { StateService } from "../../services/state.service";
|
||||
import { ADIcon, MessageBoxConfig } from "../../ui/alert-dlg/alert-dlg.model";
|
||||
import { MD2HeroInfo, MD2Icon, MobInfo, RoundPhase } from "./massive-darkness2.model";
|
||||
|
||||
@Injectable()
|
||||
export abstract class MD2Base {
|
||||
MD2Icon = MD2Icon;
|
||||
|
||||
public get gameInfo() {
|
||||
return this.md2Service.info;
|
||||
}
|
||||
|
||||
protected isHeroDashboard: boolean = false;
|
||||
protected roomId: string;
|
||||
protected destroy$: Subject<void> = new Subject<void>();
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor(
|
||||
protected md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
|
||||
}
|
||||
ngOnInit(): void {
|
||||
this.route.paramMap.pipe(first()).subscribe(params => {
|
||||
if (params.get('roomId')) {
|
||||
this.roomId = params.get('roomId');
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
this.stateService.loginUserService.signalRInitialized.pipe(first()).subscribe(result => {
|
||||
console.log('signalRInitialized');
|
||||
|
||||
this.signalRInitialized();
|
||||
});
|
||||
this.md2Service.signalRService.ReceivedSignalRMessageSubject.pipe(takeUntil(this.destroy$)).subscribe(result => this.handleSignalRCallback(result));
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
signalRInitialized() {
|
||||
|
||||
}
|
||||
|
||||
imgUrl(imgPath: string) {
|
||||
return this.md2Service.imgUrl(imgPath);
|
||||
}
|
||||
fileList(folderPath: string) {
|
||||
return this.md2Service.fileList(folderPath);
|
||||
}
|
||||
|
||||
iconHtml(icon: MD2Icon, cssClass = '') {
|
||||
return this.md2Service.iconHtml(icon, cssClass);
|
||||
}
|
||||
|
||||
imgHtml(imgFile: string, cssClass = '') {
|
||||
return `<img src="${this.imgUrl(imgFile)}" class='${cssClass}'>`
|
||||
}
|
||||
detectChanges() {
|
||||
if (!this.cdRef['destroyed']) {
|
||||
this.cdRef.detectChanges();
|
||||
this.refreshUI();
|
||||
this.md2Service.refreshUI$.next();
|
||||
}
|
||||
|
||||
}
|
||||
abstract refreshUI();
|
||||
handleSignalRCallback(message: SignalRMessage): void {
|
||||
switch (message.actionType) {
|
||||
case 'hero':
|
||||
let heroInfo = JSON.parse(message.parameters['hero']) as MD2HeroInfo;
|
||||
switch (message.actionName) {
|
||||
case 'join':
|
||||
this.md2Service.heros.push(heroInfo);
|
||||
break;
|
||||
case 'update':
|
||||
let exitingHero = this.md2Service.heros.find(h => h.playerInfo.signalRClientId == heroInfo.playerInfo.signalRClientId);
|
||||
if (exitingHero) {
|
||||
Object.keys(heroInfo).forEach(key => exitingHero[key] = heroInfo[key]);
|
||||
} else {
|
||||
this.md2Service.heros.push(heroInfo);
|
||||
}
|
||||
if (!this.isHeroDashboard) {
|
||||
if (this.gameInfo.roundPhase == RoundPhase.HeroPhase) {
|
||||
if (!this.md2Service.heros.some(h => h.remainActions > 0)) {
|
||||
if (this.md2Service.mobs.length > 0 || this.md2Service.roamingMonsters.length > 0) {
|
||||
this.md2Service.msgBoxService.show('Enemy Phase', { icon: ADIcon.WARNING }).pipe(first()).subscribe(result => {
|
||||
this.md2Service.runNextPhase();
|
||||
});
|
||||
} else {
|
||||
this.md2Service.runNextPhase();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Object.assign(heroInfo, exitingHero);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this.detectChanges();
|
||||
break;
|
||||
case 'GameRoom':
|
||||
switch (message.actionName) {
|
||||
case 'Leaving':
|
||||
this.md2Service.heros.splice(this.md2Service.heros.findIndex(h => h.playerInfo.signalRClientId == message.from.sessionId));
|
||||
this.detectChanges();
|
||||
break;
|
||||
case 'update':
|
||||
if (this.isHeroDashboard) {
|
||||
this.md2Service.info = new MD2GameInfo(JSON.parse(message.parameters['gameInfo']) as MD2GameInfo);
|
||||
this.detectChanges();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'message':
|
||||
switch (message.actionName) {
|
||||
case 'popup':
|
||||
let msg = JSON.parse(message.parameters['msg']) as MessageBoxConfig;
|
||||
this.md2Service.msgBoxService.show(msg.title, msg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'roundPhase':
|
||||
switch (message.actionName) {
|
||||
case 'bossFight':
|
||||
this.gameInfo.isBossFight = true;
|
||||
break;
|
||||
default:
|
||||
this.gameInfo.roundPhase = Number.parseInt(message.parameters['phase']);
|
||||
break;
|
||||
}
|
||||
this.detectChanges();
|
||||
break;
|
||||
case 'mobs':
|
||||
if (this.isHeroDashboard) {
|
||||
this.gameInfo.roamingMonsters = JSON.parse(message.parameters['roamingMonsters']);
|
||||
this.gameInfo.mobs = JSON.parse(message.parameters['mobs']);
|
||||
this.detectChanges();
|
||||
}
|
||||
break;
|
||||
case 'heroAction':
|
||||
if (!this.isHeroDashboard) {
|
||||
this.gameInfo.currentActivateHero = this.md2Service.heros.find(h => h.playerInfo.tabId == message.parameters['tabId']);
|
||||
switch (message.actionName) {
|
||||
case 'attackAction':
|
||||
this.gameInfo.showAttackBtn = true;
|
||||
break;
|
||||
case 'openDoor':
|
||||
//Door component listen for it
|
||||
break;
|
||||
case 'tradeAction':
|
||||
this.md2Service.msgBoxService.show('Trade and Equip', {
|
||||
text: `every one in the <b>same zone</b> with ${this.md2Service.heroFullName(this.gameInfo.currentActivateHero)} may freely trade and
|
||||
equip items!`,
|
||||
icon: ADIcon.INFO
|
||||
});
|
||||
break;
|
||||
default:
|
||||
//this.md2Service.roundPhase = Number.parseInt(message.parameters['phase']);
|
||||
break;
|
||||
}
|
||||
this.heroAction(this.gameInfo.currentActivateHero, message.actionName);
|
||||
this.detectChanges();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
abstract heroAction(hero: MD2HeroInfo, action: string);
|
||||
}
|
||||
|
||||
|
||||
@Injectable()
|
||||
export abstract class MD2ComponentBase {
|
||||
protected roomId: string;
|
||||
protected destroy$: Subject<void> = new Subject<void>();
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor(
|
||||
protected md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
|
||||
}
|
||||
ngOnInit(): void {
|
||||
this.md2Service.refreshUI$.pipe(takeUntil(this.destroy$)).subscribe(result => {
|
||||
this.cdRef.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
imgUrl(imgPath: string) {
|
||||
return this.md2Service.imgUrl(imgPath);
|
||||
}
|
||||
fileList(folderPath: string) {
|
||||
return this.md2Service.fileList(folderPath);
|
||||
}
|
||||
|
||||
iconHtml(icon: MD2Icon, cssClass = '') {
|
||||
return this.md2Service.iconHtml(icon, cssClass);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
<nb-card>
|
||||
|
||||
<nb-card-body>
|
||||
<div class="row form-group" style="
|
||||
height: 53vh;
|
||||
overflow: auto;
|
||||
">
|
||||
<div class="col-md-5">
|
||||
<img src="{{boss.standUrl}}" class="img-fluid">
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
<label class="MD2text g-font-size-40 mt-5" [innerHtml]="bossAction.skillName">
|
||||
|
||||
</label>
|
||||
<label class="g-font-size-20 mt-3" [innerHtml]="bossAction.skillDescription">
|
||||
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</nb-card-body>
|
||||
<nb-card-footer>
|
||||
<button class="float-right" nbButton hero status="danger" size="small" (click)="close()">Close</button>
|
||||
<button class="float-right mr-2" nbButton hero status="primary" size="small" (click)="close()">Submit</button>
|
||||
</nb-card-footer>
|
||||
</nb-card>
|
||||
@ -0,0 +1,4 @@
|
||||
nb-card {
|
||||
height: 70vh;
|
||||
width: 80vw;
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { BossActivationComponent } from './boss-activation.component';
|
||||
|
||||
describe('BossActivationComponent', () => {
|
||||
let component: BossActivationComponent;
|
||||
let fixture: ComponentFixture<BossActivationComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ BossActivationComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BossActivationComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,47 @@
|
||||
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NbDialogRef } from '@nebular/theme';
|
||||
import { MD2Service } from '../../../../services/md2.service';
|
||||
import { MsgBoxService } from '../../../../services/msg-box.service';
|
||||
import { StateService } from '../../../../services/state.service';
|
||||
import { MobDlgType, MD2Icon, MD2HeroInfo } from '../../massive-darkness2.model';
|
||||
import { MobSkill, IBossFight } from '../../massive-darkness2.model.boss';
|
||||
import { MD2ComponentBase } from '../../MD2Base';
|
||||
import { SpawnMobDlgComponent } from '../../mobs/spawn-mob-dlg/spawn-mob-dlg.component';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-boss-activation',
|
||||
templateUrl: './boss-activation.component.html',
|
||||
styleUrls: ['./boss-activation.component.scss']
|
||||
})
|
||||
export class BossActivationComponent extends MD2ComponentBase implements OnInit {
|
||||
boss: IBossFight;
|
||||
bossAction: MobSkill;
|
||||
|
||||
MobDlgType = MobDlgType;
|
||||
mode: MobDlgType;
|
||||
|
||||
title: string;
|
||||
titleHtml: string;
|
||||
actionHtml: string;
|
||||
MD2Icon = MD2Icon;
|
||||
|
||||
beenAttackedHero = [] as MD2HeroInfo[];
|
||||
attackTarget: string;
|
||||
otherAttackTarget: string;
|
||||
constructor(
|
||||
private dlgRef: NbDialogRef<SpawnMobDlgComponent>,
|
||||
private msgBoxService: MsgBoxService,
|
||||
public md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
super(md2Service, stateService, route, cdRef);
|
||||
}
|
||||
close() {
|
||||
this.boss.standUrl
|
||||
this.dlgRef.close();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
<nb-card>
|
||||
<nb-card-header>
|
||||
{{boss.name}}
|
||||
<button nbButton hero status="primary" (click)="activate()">Action</button>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
<img src="{{boss.standUrl}}" class="w-100 g-max-height-80vh">
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<md2-mob-detail-info [mob]="boss.info">
|
||||
</md2-mob-detail-info>
|
||||
|
||||
<adj-number-input name="mob{{boss.info.name}}" [(ngModel)]="boss.info.unitRemainHp" minimum="0"
|
||||
title="Boss HP" (hitMinimum)="WIN()">
|
||||
</adj-number-input>
|
||||
<button nbButton hero status="danger" size="small" (click)="attack(boss.info)">Attack It</button>
|
||||
|
||||
<label class="MD2Text mt-3" [innerHtml]="boss.combatInfo.skillName">
|
||||
</label>
|
||||
<label class="MD2Text" [innerHtml]="boss.combatInfo.skillDescription">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
@ -0,0 +1,4 @@
|
||||
nb-card {
|
||||
height: 80vh;
|
||||
//width: 80vw;
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { BossFightComponent } from './boss-fight.component';
|
||||
|
||||
describe('BossFightComponent', () => {
|
||||
let component: BossFightComponent;
|
||||
let fixture: ComponentFixture<BossFightComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ BossFightComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BossFightComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,63 @@
|
||||
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NbDialogService } from '@nebular/theme';
|
||||
import { Subject, Observable } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { MD2Service } from '../../../services/md2.service';
|
||||
import { MsgBoxService } from '../../../services/msg-box.service';
|
||||
import { StateService } from '../../../services/state.service';
|
||||
import { ADIcon } from '../../../ui/alert-dlg/alert-dlg.model';
|
||||
import { MD2Icon, MobDlgType, MobInfo } from '../massive-darkness2.model';
|
||||
import { MD2ComponentBase } from '../MD2Base';
|
||||
import { SpawnMobDlgComponent } from '../mobs/spawn-mob-dlg/spawn-mob-dlg.component';
|
||||
|
||||
@Component({
|
||||
selector: 'md2-boss-fight',
|
||||
templateUrl: './boss-fight.component.html',
|
||||
styleUrls: ['./boss-fight.component.scss']
|
||||
})
|
||||
export class BossFightComponent extends MD2ComponentBase {
|
||||
|
||||
|
||||
public get boss() {
|
||||
return this.md2Service.info.boss;
|
||||
}
|
||||
|
||||
|
||||
constructor(
|
||||
private msgBoxService: MsgBoxService,
|
||||
private dlgService: NbDialogService,
|
||||
public md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
super(md2Service, stateService, route, cdRef);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
|
||||
}
|
||||
activate() {
|
||||
this.boss.activating();
|
||||
}
|
||||
WIN() {
|
||||
|
||||
}
|
||||
attack(mob: MobInfo) {
|
||||
|
||||
this.dlgService.open(SpawnMobDlgComponent, { context: { title: `Attack ${this.boss.name}`, mode: MobDlgType.BeenAttacked, mob: mob } })
|
||||
.onClose.pipe(first()).subscribe(mobResult => {
|
||||
if (mobResult) {
|
||||
let attackDamage = mobResult.uiWounds;
|
||||
if (attackDamage) {
|
||||
this.boss.info.hp -= attackDamage;
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
<nb-card>
|
||||
<nb-card-header>
|
||||
<img src="{{imgUrl('DoorEvents/Cover.png')}}" width="40px"> Door Event
|
||||
<!-- <button nbButton hero status="warning" size="small" (click)="initMobDecks()" class="float-right">Reset
|
||||
Mobs</button> -->
|
||||
<button nbButton hero status="info" size="small" (click)="drawDoorCard()" class="float-right mr-2">Draw</button>
|
||||
<!-- <button nbButton hero status="warning" size="tiny" (click)="resetTreasureBag()"
|
||||
class="float-right">Reset</button>
|
||||
<button nbButton hero status="primary" size="tiny" (click)="addTreasure(TreasureType.Epic)"
|
||||
class="float-right mr-1">Add
|
||||
Epic</button>
|
||||
<button nbButton hero status="info" size="tiny" (click)="addTreasure(TreasureType.Rare)"
|
||||
class="float-right mr-1">Add
|
||||
Rare</button> -->
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<!-- <b>Content of the bag</b>
|
||||
<hr class="my-1"> -->
|
||||
<div class="row">
|
||||
<!-- this.mobs <div class="col" *ngFor="let treasure of treasureBag.drawingItems">
|
||||
<img src="{{treasure.imageUrl}}" width="40px"> X {{treasure.unitAmount}}
|
||||
</div> -->
|
||||
<div class="col col-md-3 mb-4" *ngFor="let door of this.drawDoorEvents">
|
||||
|
||||
<img class="g-width-95x" src="{{door.imageUrl}}" (click)="showMobImage(mob)" /><br>
|
||||
<label class="badge badge-info">{{door.name}}</label>
|
||||
<button nbButton hero status="primary" size="small" class="float-right"
|
||||
(click)="discard(door)">Discard</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="container">
|
||||
<img id="clip" src="{{imgUrl('Mobs/CoreGame/CoreGameMob.jpg')}}" />
|
||||
</div> -->
|
||||
<!-- <button nbButton hero fullWidth="" status="primary" class="mt-3" (click)="drawTreasure()">I feel
|
||||
LUCKY!!!!!</button> -->
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DoorEventsComponent } from './door-events.component';
|
||||
|
||||
describe('DoorEventsComponent', () => {
|
||||
let component: DoorEventsComponent;
|
||||
let fixture: ComponentFixture<DoorEventsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ DoorEventsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DoorEventsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,72 @@
|
||||
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { FileService } from '../../../services/file.service';
|
||||
import { MsgBoxService } from '../../../services/msg-box.service';
|
||||
import { ADButtons } from '../../../ui/alert-dlg/alert-dlg.model';
|
||||
import { DrawingBag, DrawingItem } from '../massive-darkness2.model';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { MD2Service } from '../../../services/md2.service';
|
||||
import { StateService } from '../../../services/state.service';
|
||||
import { MD2Base, MD2ComponentBase } from '../MD2Base';
|
||||
import { SignalRMessage } from '../../../services/signal-r.service';
|
||||
|
||||
@Component({
|
||||
selector: 'md2-door-events',
|
||||
templateUrl: './door-events.component.html',
|
||||
styleUrls: ['./door-events.component.scss']
|
||||
})
|
||||
export class DoorEventsComponent extends MD2ComponentBase implements OnInit {
|
||||
|
||||
constructor(
|
||||
private msgBoxService: MsgBoxService,
|
||||
public md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
super(md2Service, stateService, route, cdRef);
|
||||
}
|
||||
drawDoorEvents: DrawingItem[];
|
||||
treasureBag: DrawingBag<DrawingItem> = new DrawingBag<DrawingItem>();
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initDoorEvents();
|
||||
|
||||
this.md2Service.signalRService.ReceivedSignalRMessageSubject.subscribe(result => this.handleSignalRCallback(result));
|
||||
}
|
||||
handleSignalRCallback(result: SignalRMessage): void {
|
||||
if (result.actionName == 'openDoor') {
|
||||
this.drawDoorCard();
|
||||
}
|
||||
}
|
||||
|
||||
initDoorEvents() {
|
||||
this.drawDoorEvents = [];
|
||||
this.treasureBag = new DrawingBag<DrawingItem>();
|
||||
for (let i = 1; i <= 30; i++) {
|
||||
this.treasureBag.AddItem(new DrawingItem(i.toString(), '', this.imgUrl(`DoorEvents/DoorEvent-${i}.png`), 1));
|
||||
}
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
drawDoorCard() {
|
||||
if (this.treasureBag.bagIsEmpty()) {
|
||||
this.treasureBag.RestoreRemoveItems();
|
||||
}
|
||||
let door = this.treasureBag.DrawAndRemove()[0];
|
||||
|
||||
this.msgBoxService.show('', { text: `<img src="${door.imageUrl}" class="g-height-70vh g-max-width-80vw">`, buttons: ADButtons.YesNo, confirmButtonText: 'Keep It', cancelButtonText: 'Discard' })
|
||||
.pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
door.name = this.md2Service.heroFullName(this.md2Service.info.currentActivateHero);
|
||||
this.drawDoorEvents.push(door);
|
||||
}
|
||||
this.cdRef.detectChanges();
|
||||
});
|
||||
}
|
||||
discard(door: DrawingItem) {
|
||||
this.drawDoorEvents.splice(this.drawDoorEvents.indexOf(door), 1);
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,141 @@
|
||||
<nb-card *ngIf="!md2Service.playerHero">
|
||||
<nb-card-body>
|
||||
<button nbButton hero status="primary" fullWidth (click)="initHero()">Choose Hero</button>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
<div *ngIf="md2Service.playerHero">
|
||||
<div class="row no-gutters">
|
||||
<div class="col-12 col-sm-7">
|
||||
<div class="tp-wrapper mb-2">
|
||||
<div class="tp-box g-height-300 g-height-350--sm g-height-500--md" (click)="toggleFlip()"
|
||||
[@flipState]="flip">
|
||||
<div class="tp-box__side tp-box__front ">
|
||||
|
||||
<img class="MD2HeroCard " src="{{md2Service.playerHero.imgUrl}}">
|
||||
</div>
|
||||
<div class="tp-box__side tp-box__back">
|
||||
|
||||
|
||||
<img class="MD2HeroCard " src="{{imgUrl('Heros/Guide/'+className+'.jpg')}}">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="g-max-height-80vh mb-2">
|
||||
<img class="MD2HeroCard" src="{{md2Service.playerHero.imgUrl}}">
|
||||
<div class="MD2HeroCard">
|
||||
<span class="MD2text MD2Name">{{md2Service.playerHero.name}}</span>
|
||||
<span class="MD2text MD2Hp">{{md2Service.playerHero.hpMaximum}}</span>
|
||||
<span class="MD2text MD2Mp">{{md2Service.playerHero.mpMaximum}}</span>
|
||||
</div>
|
||||
<img class="MD2HeroCard" src="{{md2Service.playerHero.imgUrl}}">
|
||||
<img class="MD2HeroCard HpMpBar" src="{{imgUrl('/Heros/Template/Border.png')}}">
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-5">
|
||||
<nb-card>
|
||||
<nb-card-body>
|
||||
<div class="row no-gutters">
|
||||
<div class="col-6">
|
||||
|
||||
<!-- <adj-number-input name="heroHP" [(ngModel)]="md2Service.playerHero.hp"
|
||||
[maximum]="md2Service.playerHero.hpMaximum" minimum="0"
|
||||
title="{{iconHtml(MD2Icon.HP,'g-color-google-plus mr-1 g-font-size-18')}}HP" showMaximum
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()" (hitDecreasing)="increaseRage()">
|
||||
</adj-number-input> -->
|
||||
|
||||
|
||||
<adj-number-input name="heroHP" [(ngModel)]="md2Service.playerHero.hp"
|
||||
[maximum]="md2Service.playerHero.hpMaximum" minimum="0"
|
||||
title="{{imgHtml('HpIcon.png','g-height-25 mr-1')}}HP" showMaximum
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()" (hitDecreasing)="increaseRage()">
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroMana" [(ngModel)]="md2Service.playerHero.mp"
|
||||
[maximum]="md2Service.playerHero.mpMaximum" minimum="0"
|
||||
title="{{imgHtml('HeroIcon.png','g-height-25 mr-1')}}Mana" showMaximum
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
|
||||
<adj-number-input name="heroFire" [(ngModel)]="md2Service.playerHero.fireToken" minimum="0"
|
||||
title="{{iconHtml(MD2Icon.Fire,'g-color-google-plus mr-1 g-font-size-18')}}Fire Token"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroFire" [(ngModel)]="md2Service.playerHero.frozenToken"
|
||||
minimum="0"
|
||||
title="{{iconHtml(MD2Icon.Frost,'g-color-aqua mr-1 g-font-size-18')}}Frozen Token"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
|
||||
<adj-number-input name="remainActions" [(ngModel)]="md2Service.playerHero.remainActions"
|
||||
minimum="0" title="Remain Actions" (blur)="heroUpdateDebounceTimer.resetTimer()"
|
||||
hideIncreaseBtn>
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroLevel" [(ngModel)]="md2Service.playerHero.level" minimum="1"
|
||||
maximum="5" title="Level" (blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroExp" [(ngModel)]="md2Service.playerHero.exp" minimum="0"
|
||||
title="Exp" (blur)="heroUpdateDebounceTimer.resetTimer()">
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroRage" [(ngModel)]="md2Service.playerHero.rage" minimum="0"
|
||||
maximum="7"
|
||||
title="{{iconHtml(MD2Icon.Rage,'g-color-google-plus mr-1 g-font-size-18')}}Rage"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()"
|
||||
*ngIf="md2Service.playerHero.class==HeroClass.Berserker">
|
||||
</adj-number-input>
|
||||
<adj-number-input name="heroCorruption" [(ngModel)]="md2Service.playerHero.corruptionToken"
|
||||
minimum="0" title="{{imgHtml('Tokens/CorruptToken.png','g-height-18')}} Corruption"
|
||||
(blur)="heroUpdateDebounceTimer.resetTimer()"
|
||||
*ngIf="md2Service.playerHero.corruptionToken>0">
|
||||
</adj-number-input>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div *ngIf="md2Service.info.isBossFight"></div>
|
||||
<div *ngIf="md2Service.playerHero.remainActions>0">
|
||||
<button nbButton hero class="mr-2" status="info" (click)="moveAction()"
|
||||
*ngIf="!showMoveAction">Move</button>
|
||||
<button nbButton hero class="mr-2" status="info" (click)="moveActionEnd()"
|
||||
*ngIf="showMoveAction">Move End</button>
|
||||
<button nbButton hero class="mr-2" status="danger" (click)="action('attackAction')"
|
||||
*ngIf="!showMoveAction&&allowAttack">Attack!</button>
|
||||
<button nbButton hero class="mr-2" status="info" (click)="action('tradeAction')"
|
||||
*ngIf="!showMoveAction">Trade</button>
|
||||
<button nbButton hero status="success" (click)="action('recoveryAction')"
|
||||
*ngIf="!showMoveAction">Recovery</button>
|
||||
|
||||
</div>
|
||||
|
||||
<button nbButton hero status="info" class="mt-2" (click)="openDoor()" *ngIf="showMoveAction">Open
|
||||
Door</button>
|
||||
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- <nb-flip-card *ngIf="md2Service.playerHero">
|
||||
<nb-card-front>
|
||||
<nb-card>
|
||||
|
||||
<nb-card-body>
|
||||
|
||||
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
</nb-card-front>
|
||||
<nb-card-back>
|
||||
<nb-card>
|
||||
<nb-card-body>
|
||||
Back card text
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
</nb-card-back>
|
||||
</nb-flip-card> -->
|
||||
@ -0,0 +1,80 @@
|
||||
.MD2Hp {
|
||||
font-size: xx-large;
|
||||
position: fixed;
|
||||
z-index: 92;
|
||||
color: white;
|
||||
bottom: 23%;
|
||||
left: 46%;
|
||||
}
|
||||
.MD2Mp {
|
||||
font-size: xx-large;
|
||||
position: fixed;
|
||||
z-index: 92;
|
||||
color: white;
|
||||
bottom: 22%;
|
||||
left: 7%;
|
||||
}
|
||||
|
||||
.MD2Name {
|
||||
font-size: xx-large;
|
||||
position: fixed;
|
||||
z-index: 92;
|
||||
color: #2e2e30;
|
||||
bottom: 20%;
|
||||
left: 23%;
|
||||
}
|
||||
.MD2HeroCard {
|
||||
//position: absolute;
|
||||
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
&.HpMpBar {
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
|
||||
.tp-wrapper {
|
||||
-webkit-perspective: 800px;
|
||||
perspective: 800px;
|
||||
}
|
||||
|
||||
.tp-box {
|
||||
position: relative;
|
||||
//width: 200px;
|
||||
//height: 100px;
|
||||
//margin: 3rem auto;
|
||||
-webkit-transform-style: preserve-3d;
|
||||
transform-style: preserve-3d;
|
||||
-webkit-transform: transform 1s;
|
||||
-ms-transform: transform 1s;
|
||||
transform: transform 1s;
|
||||
}
|
||||
.tp-box__side {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden;
|
||||
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
line-height: 100px;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.tp-box__front {
|
||||
-webkit-transform: rotateY(0deg);
|
||||
-ms-transform: rotateY(0deg);
|
||||
transform: rotateY(0deg);
|
||||
}
|
||||
.tp-box__back {
|
||||
-webkit-transform: rotateY(-180deg);
|
||||
-ms-transform: rotateY(-180deg);
|
||||
transform: rotateY(-180deg);
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HeroDashboardComponent } from './hero-dashboard.component';
|
||||
|
||||
describe('HeroDashboardComponent', () => {
|
||||
let component: HeroDashboardComponent;
|
||||
let fixture: ComponentFixture<HeroDashboardComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ HeroDashboardComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(HeroDashboardComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,205 @@
|
||||
import { trigger, state, style, transition, animate } from '@angular/animations';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { DropDownOption } from '../../../entity/dropDownOption';
|
||||
import { GameRoomService } from '../../../services/game-room.service';
|
||||
import { MD2Service } from '../../../services/md2.service';
|
||||
import { MsgBoxService } from '../../../services/msg-box.service';
|
||||
import { StateService } from '../../../services/state.service';
|
||||
import { ADButtonColor, ADButtons } from '../../../ui/alert-dlg/alert-dlg.model';
|
||||
import { StringUtils } from '../../../utilities/string-utils';
|
||||
import { DebounceTimer } from '../../../utilities/timer-utils';
|
||||
import { HeroClass, MD2HeroInfo } from '../massive-darkness2.model';
|
||||
import { MD2Base } from '../MD2Base';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-hero-dashboard',
|
||||
templateUrl: './hero-dashboard.component.html',
|
||||
styleUrls: ['./hero-dashboard.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
animations: [
|
||||
trigger('flipState', [
|
||||
state('active', style({
|
||||
transform: 'rotateY(179deg)'
|
||||
})),
|
||||
state('inactive', style({
|
||||
transform: 'rotateY(0)'
|
||||
})),
|
||||
transition('active => inactive', animate('500ms ease-out')),
|
||||
transition('inactive => active', animate('500ms ease-in'))
|
||||
])
|
||||
]
|
||||
})
|
||||
export class HeroDashboardComponent extends MD2Base implements OnInit {
|
||||
heroAction(hero: MD2HeroInfo, action: string) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
showMoveAction: boolean = false;
|
||||
HeroClass = HeroClass;
|
||||
refreshUI() {
|
||||
console.log('HeroDashboard RefreshUI');
|
||||
|
||||
}
|
||||
|
||||
heroUpdateDebounceTimer = new DebounceTimer(1000, () => { this.broadcastHeroInfo(); })
|
||||
|
||||
classOptions: DropDownOption[] = [
|
||||
new DropDownOption(HeroClass.Berserker, 'Berserker'),
|
||||
new DropDownOption(HeroClass.Paladin, 'Paladin'),
|
||||
new DropDownOption(HeroClass.Ranger, 'Ranger'),
|
||||
new DropDownOption(HeroClass.Rogue, 'Rogue'),
|
||||
new DropDownOption(HeroClass.Wizard, 'Wizard'),
|
||||
new DropDownOption(HeroClass.Shaman, 'Shaman'),
|
||||
];
|
||||
heros = [] as MD2HeroInfo[];
|
||||
wizards: MD2HeroInfo[] = [
|
||||
new MD2HeroInfo({ name: 'Ajax', mpMaximum: 6, hpMaximum: 4, skillHtml: '', shadowSkillHtml: '' }),
|
||||
new MD2HeroInfo({ name: 'Baldric', mpMaximum: 5, hpMaximum: 4, skillHtml: '', shadowSkillHtml: '' }),
|
||||
new MD2HeroInfo({ name: 'Ego', mpMaximum: 5, hpMaximum: 6, skillHtml: '', shadowSkillHtml: '' }),
|
||||
new MD2HeroInfo({ name: 'Elias', mpMaximum: 6, hpMaximum: 5, skillHtml: '', shadowSkillHtml: '' }),
|
||||
new MD2HeroInfo({ name: 'Megan', mpMaximum: 5, hpMaximum: 5, skillHtml: '', shadowSkillHtml: '' }),
|
||||
new MD2HeroInfo({ name: 'Moira', mpMaximum: 6, hpMaximum: 5, skillHtml: '', shadowSkillHtml: '' }),
|
||||
new MD2HeroInfo({ name: 'Myriam', mpMaximum: 7, hpMaximum: 4, skillHtml: '', shadowSkillHtml: '' }),
|
||||
new MD2HeroInfo({ name: 'Valdis', mpMaximum: 6, hpMaximum: 4, skillHtml: '', shadowSkillHtml: '' })
|
||||
]
|
||||
constructor(
|
||||
private gameRoomService: GameRoomService,
|
||||
public md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
private msgBoxService: MsgBoxService,
|
||||
) {
|
||||
super(md2Service, stateService, route, cdRef);
|
||||
this.isHeroDashboard = true;
|
||||
}
|
||||
|
||||
public get allowAttack(): boolean {
|
||||
return (!!this.md2Service.mobs && this.md2Service.mobs.length > 0) || (!!this.md2Service.roamingMonsters && this.md2Service.roamingMonsters.length > 0);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
}
|
||||
|
||||
override signalRInitialized() {
|
||||
// this.gameRoomService.joinGameRoom(this.roomId);
|
||||
// if (this.md2Service.initialized == false) {
|
||||
// this.gameRoomService.createGameRoom('MD2');
|
||||
// this.md2Service.initialized = true;
|
||||
// }
|
||||
}
|
||||
initHero() {
|
||||
this.gameRoomService.gameRoomId = this.roomId;
|
||||
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' })
|
||||
.pipe(first()).subscribe(heroClass => {
|
||||
if (heroClass != null) {
|
||||
// switch (heroClass) {
|
||||
// case HeroClass.Berserker: break;
|
||||
// case HeroClass.Wizard:
|
||||
// this.heros = this.wizards;
|
||||
// break;
|
||||
// case HeroClass.Rogue: break;
|
||||
// case HeroClass.Ranger: break;
|
||||
// case HeroClass.Shaman: break;
|
||||
// case HeroClass.Paladin: break;
|
||||
// default: break;
|
||||
// }
|
||||
// this.showHeroList(heroClass, 0);
|
||||
this.initClassHeroList(heroClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
className: string;
|
||||
initClassHeroList(heroClass: HeroClass) {
|
||||
this.heros = [];
|
||||
this.className = HeroClass[heroClass];
|
||||
this.fileList(`Heros/${this.className}`).pipe(first()).subscribe(fileNames => {
|
||||
for (let i = 0; i < fileNames.length; i++) {
|
||||
const heroNames = fileNames[i].split('.')[0].split('-');
|
||||
|
||||
this.heros.push(new MD2HeroInfo({
|
||||
name: heroNames[0].replace('/', ''),
|
||||
mpMaximum: Number.parseInt(heroNames[1]),
|
||||
hpMaximum: Number.parseInt(heroNames[2]),
|
||||
imgUrl: this.imgUrl(`Heros/${this.className}/${fileNames[i]}`),
|
||||
class: heroClass
|
||||
}))
|
||||
}
|
||||
this.heros = this.heros.sort((a, b) => StringUtils.compareSemVer(a.name, b.name));
|
||||
this.showHeroList(heroClass, 0);
|
||||
});
|
||||
}
|
||||
showHeroList(heroClass: HeroClass, index: number) {
|
||||
let className = HeroClass[heroClass];
|
||||
let heroInfo = this.heros[index];
|
||||
this.msgBoxService.show(`${className}(${index + 1}/${this.heros.length})`, {
|
||||
text: `<img src='${heroInfo.imgUrl}' class="g-width-50vw-md g-width-80vw">`,
|
||||
buttons: ADButtons.YesNo,
|
||||
cardWidthClass: '',
|
||||
confirmButtonText: 'It\'s Me!',
|
||||
cancelButtonText: 'Next',
|
||||
cancelButtonColor: ADButtonColor.INFO
|
||||
}).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
this.md2Service.playerJoin(heroInfo);
|
||||
this.detectChanges();
|
||||
} else {
|
||||
index++;
|
||||
if (index == this.heros.length) index = 0;
|
||||
this.showHeroList(heroClass, index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
broadcastHeroInfo() {
|
||||
this.md2Service.broadcastMyHeroInfo();
|
||||
this.heroUpdateDebounceTimer.clearOut();
|
||||
}
|
||||
increaseRage() {
|
||||
if (this.md2Service.playerHero.rage < 7) {
|
||||
this.md2Service.playerHero.rage++;
|
||||
}
|
||||
}
|
||||
openDoor() {
|
||||
this.md2Service.broadcastHeroAction('openDoor');
|
||||
this.showMoveAction = false;
|
||||
this.detectChanges();
|
||||
}
|
||||
moveAction() {
|
||||
this.showMoveAction = true;
|
||||
this.detectChanges();
|
||||
}
|
||||
moveActionEnd() {
|
||||
this.showMoveAction = false;
|
||||
this.reduceAction();
|
||||
this.detectChanges();
|
||||
}
|
||||
action(action: string) {
|
||||
this.showMoveAction = false;
|
||||
switch (action) {
|
||||
case 'recoveryAction':
|
||||
this.msgBoxService.show('Recovery', { text: 'takes the Recover action may gain up to 2 Health or Mana in any combination (either 2 Health, 2 Mana, or 1 of each).' })
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this.md2Service.broadcastHeroAction(action);
|
||||
this.reduceAction();
|
||||
}
|
||||
reduceAction() {
|
||||
this.md2Service.playerHero.remainActions -= 1;
|
||||
this.detectChanges();
|
||||
this.broadcastHeroInfo();
|
||||
}
|
||||
flip: string = 'inactive';
|
||||
|
||||
toggleFlip() {
|
||||
this.flip = (this.flip == 'inactive') ? 'active' : 'inactive';
|
||||
}
|
||||
}
|
||||
@ -1,25 +1,101 @@
|
||||
<div class="row">
|
||||
<!-- <div class="col-12 mb-1">
|
||||
<nb-accordion>
|
||||
<nb-accordion-item>
|
||||
<nb-accordion-item-header class="my-0">
|
||||
Global Config
|
||||
</nb-accordion-item-header>
|
||||
<nb-accordion-item-body>
|
||||
|
||||
<div class="col-12 col-md-4">
|
||||
</nb-accordion-item-body>
|
||||
</nb-accordion-item>
|
||||
</nb-accordion>
|
||||
</div> -->
|
||||
|
||||
<div class="col-12 col-md-5">
|
||||
<nb-card>
|
||||
<nb-card-header>
|
||||
Treasure Bag
|
||||
<button nbButton hero status="primary" size="small">Reset</button>
|
||||
<button nbButton hero status="primary" size="small" (click)="addTreasure(TreasureType.Rare)">Add
|
||||
Rare</button>
|
||||
<button nbButton hero status="primary" size="small" (click)="addTreasure(TreasureType.Epic)">Add
|
||||
Epic</button>
|
||||
<img src="{{imgUrl('HeroIcon.png')}}" width="40px"> Game Info
|
||||
|
||||
<button nbButton hero status="info" size="small" (click)="showQrCode()"
|
||||
class="float-right">Invite</button>
|
||||
<button nbButton hero status="info" size="small" [disabled]="anyHeroRemainAction"
|
||||
(click)="md2Service.runNextPhase()" class="float-right mr-2">Next Phase</button>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div>
|
||||
<ng-template *ngFor="let treasure of treasureBag.drawingItems">
|
||||
<img src="{{treasure.imageUrl}}"> X {{treasure.unitAmount}}
|
||||
</ng-template>
|
||||
|
||||
<div class="row" *ngIf="md2Service.heros.length==0">
|
||||
<div class="col-6">
|
||||
|
||||
<adj-number-input name="heroLevel" [(ngModel)]="md2Service.playerAmount" [maximum]="6"
|
||||
minimum="1" title="Hero Amount">
|
||||
</adj-number-input>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<adj-number-input name="heroLevel" [(ngModel)]="md2Service.highestPlayerLevel" [maximum]="5"
|
||||
minimum="1" title="Highest Hero Level">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" *ngIf="md2Service.heros.length>0">
|
||||
<div class="col-12 g-font-size-17" [innerHtml]="roundPhase"></div>
|
||||
|
||||
<!-- <div class="col-6">
|
||||
<label for='playerAmount' class='label'>Hero Amount ({{md2Service.playerAmount}})</label>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label for='playerAmount' class='label'>Highest Hero Level
|
||||
({{md2Service.highestPlayerLevel}})</label>
|
||||
</div> -->
|
||||
<div class="col-12" *ngFor="let hero of md2Service.heros">
|
||||
<label class='label mr-1'>{{hero.playerInfo.name}} ({{heroClassName(hero)}} -
|
||||
{{hero.name}})</label>
|
||||
<span class="badge badge-primary mr-1">Lv.:{{hero.level}}</span>
|
||||
<span class="badge badge-primary mr-1">HP: {{hero.hp}}/{{hero.hpMaximum}}</span>
|
||||
<span class="badge badge-primary mr-1">Mana: {{hero.mp}}/{{hero.mpMaximum}}</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-info mr-1" *ngIf="hero.frozenToken">Frozen:{{hero.frozenToken}}</span>
|
||||
<span class="badge badge-light mr-1" *ngIf="hero.remainActions==0">Inactive</span>
|
||||
<span class="badge badge-success mr-1" *ngIf="hero.remainActions>0">Remain
|
||||
Actions: {{hero.remainActions}}</span>
|
||||
<!-- <span class="badge badge-success mr-1">{{hero.playerInfo.signalRClientId}}</span> -->
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<button nbButton hero status="primary">I feel LUCKY!!!!!</button>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<md2-treasure-bag></md2-treasure-bag>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-3">
|
||||
<div class="row">
|
||||
<div class="form-group col-12">
|
||||
|
||||
<button nbButton hero fullWidth status="primary" (click)="enterBossFight()">Enter Boss Fight</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-12 col-md-12" *ngIf="md2Service.info.isBossFight">
|
||||
<md2-boss-fight></md2-boss-fight>
|
||||
</div>
|
||||
<div class="col-12 col-md-12">
|
||||
<md2-mobs isRoamingMonster="true"></md2-mobs>
|
||||
</div>
|
||||
<div class="col-12 col-md-12">
|
||||
<md2-mobs></md2-mobs>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-12">
|
||||
<md2-door-events></md2-door-events>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -4,6 +4,17 @@ import { FileService } from '../../services/file.service';
|
||||
import { MsgBoxService } from '../../services/msg-box.service';
|
||||
import { ArrayUtils } from '../../utilities/array-utils';
|
||||
import { ObjectUtils } from '../../utilities/object-utils';
|
||||
import { first, map, take, takeUntil } from 'rxjs/operators';
|
||||
import { TreasureType, DrawingBag, DrawingItem, HeroClass, MD2HeroInfo, RoundPhase, MobInfo, MobDlgType } from './massive-darkness2.model';
|
||||
import { MD2Service } from '../../services/md2.service';
|
||||
import { GameRoomService } from '../../services/game-room.service';
|
||||
import { MD2Base } from './MD2Base';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { QRCodeService } from '../../services/qrcode.service';
|
||||
import { StringUtils } from '../../utilities/string-utils';
|
||||
import { SpawnMobDlgComponent } from './mobs/spawn-mob-dlg/spawn-mob-dlg.component';
|
||||
import { BossMicheal } from './massive-darkness2.model.boss';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-massive-darkness2',
|
||||
@ -11,133 +22,113 @@ import { ObjectUtils } from '../../utilities/object-utils';
|
||||
styleUrls: ['./massive-darkness2.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class MassiveDarkness2Component implements OnInit {
|
||||
TreasureType = TreasureType;
|
||||
treasureBag: DrawingBag = new DrawingBag();
|
||||
|
||||
export class MassiveDarkness2Component extends MD2Base implements OnInit {
|
||||
HeroClass: HeroClass
|
||||
constructor(
|
||||
private fileService: FileService,
|
||||
private cdRef: ChangeDetectorRef,
|
||||
private msgBoxService: MsgBoxService
|
||||
) { }
|
||||
private msgBoxService: MsgBoxService,
|
||||
private qrCodeService: QRCodeService,
|
||||
public gameRoomService: GameRoomService,
|
||||
public md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
super(md2Service, stateService, route, cdRef);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.resetTreasureBag();
|
||||
this.detectChanges();
|
||||
}
|
||||
detectChanges() {
|
||||
super.ngOnInit();
|
||||
this.md2Service.enemyPhaseSubject.pipe(takeUntil(this.destroy$)).subscribe(result => {
|
||||
this.showEnemyPhaseAction(0);
|
||||
});
|
||||
|
||||
if (!this.cdRef['destroyed']) {
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
override signalRInitialized() {
|
||||
|
||||
}
|
||||
showQrCode() {
|
||||
if (this.md2Service.initialized == false) {
|
||||
this.gameRoomService.createGameRoom('MD2');
|
||||
this.md2Service.initialized = true;
|
||||
}
|
||||
let initUrl = `${window.location.origin}/games/MD2_Hero/${this.gameRoomService.gameRoomId}`;
|
||||
this.msgBoxService.show("Scan To Join", { text: `<img src='${this.qrCodeService.QRCodeUrl(initUrl, 5)}'><br><a href='${initUrl}' target='_blank'>Link</a>` });
|
||||
}
|
||||
resetTreasureBag() {
|
||||
this.treasureBag.ClearAllItems();
|
||||
this.addTreasure(TreasureType.Common, 15);
|
||||
}
|
||||
addTreasure(type: TreasureType, amount: number = 1) {
|
||||
let item = new DrawingItem(`${TreasureType[type]} Treasure`, `It's a ${TreasureType[type]} Treasure!`, this.fileService.ImageUrl(`TreasureToken/${TreasureType[type]}.png`), amount);
|
||||
|
||||
this.treasureBag.AddItem(item);
|
||||
this.detectChanges();
|
||||
}
|
||||
treasureImage(type: TreasureType) {
|
||||
return this.fileService.ImageUrl(`TreasureToken/${TreasureType[type]}.png`);
|
||||
}
|
||||
drawTreasure() {
|
||||
refreshUI() {
|
||||
console.log('Dashboard RefreshUI');
|
||||
|
||||
}
|
||||
}
|
||||
export enum TreasureType {
|
||||
Common,
|
||||
Rare,
|
||||
Epic,
|
||||
Legendary
|
||||
}
|
||||
export class DrawingBag {
|
||||
constructor() {
|
||||
this.drawingItems = [];
|
||||
this.removedItems = [];
|
||||
heroClassName(heroInfo: MD2HeroInfo) {
|
||||
return HeroClass[heroInfo.class];
|
||||
}
|
||||
drawingItems: DrawingItem[]
|
||||
removedItems: DrawingItem[]
|
||||
|
||||
public Draw(amount: number): DrawingItem[] {
|
||||
let drawItems: DrawingItem[] = [];
|
||||
for (let i = 0; i < amount; i++) {
|
||||
showEnemyPhaseAction(index: number) {
|
||||
let mob = new MobInfo(this.md2Service.enemyPhaseMobs[index]);
|
||||
let enemyInfo = `<img src="${mob.imageUrl}" class='g-height-70vh'><br>`;
|
||||
let extraRule = '';
|
||||
|
||||
drawItems.push(this.DrawAndRemove());
|
||||
}
|
||||
this.RestoreRemoveItems();
|
||||
return drawItems;
|
||||
}
|
||||
public DrawAndRemove(): DrawingItem {
|
||||
if (this.drawingItems.length > 0) {
|
||||
let drawItem = null as DrawingItem;
|
||||
let drawIndex = Math.random() * this.drawingItems.reduce((sum, current) => sum + current.unitAmount, 0);
|
||||
// switch (Math.random() * 3) {
|
||||
|
||||
let drawCalc = 0;
|
||||
for (let i = 0; i < this.drawingItems.length; i++) {
|
||||
const item = this.drawingItems[i];
|
||||
drawCalc += item.unitAmount;
|
||||
if (drawCalc >= drawIndex) {
|
||||
drawItem = ObjectUtils.CloneValue(item);
|
||||
drawItem.unitAmount = 1;
|
||||
item.unitAmount -= 1;
|
||||
break;
|
||||
// case 1:
|
||||
|
||||
// break;
|
||||
// case 2:
|
||||
|
||||
// break;
|
||||
// case 3:
|
||||
|
||||
// break;
|
||||
|
||||
// case 0:
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
|
||||
this.msgBoxService.dlgService.open(SpawnMobDlgComponent, { context: { title: `Enemy Phase(${(index + 1)}/${this.md2Service.enemyPhaseMobs.length})`, mode: MobDlgType.Activating, mob: mob } })
|
||||
.onClose.pipe(first()).subscribe(result => {
|
||||
index++;
|
||||
if (index < this.md2Service.enemyPhaseMobs.length) {
|
||||
this.showEnemyPhaseAction(index);
|
||||
} else {
|
||||
this.md2Service.runNextPhase();
|
||||
}
|
||||
}
|
||||
//ObjectUtils.CloneValue
|
||||
this.RemoveItem(drawItem);
|
||||
return drawItem;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
// return this.msgBoxService.show(`Enemy Phase(${(this.enemyPhaseMobs.indexOf(mob) + 1)}/${this.enemyPhaseMobs.length})`, {
|
||||
// text: enemyInfo,
|
||||
// confirmButtonText: 'Next', buttons: ADButtons.OK
|
||||
// }).pipe(first()).subscribe(result => {
|
||||
// if ((this.enemyPhaseMobs.indexOf(mob) + 1) < this.enemyPhaseMobs.length) {
|
||||
// this.showEnemyPhaseAction(this.enemyPhaseMobs[this.enemyPhaseMobs.indexOf(mob) + 1]);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
public RestoreRemoveItems() {
|
||||
for (let i = 0; i < this.removedItems.length; i++) {
|
||||
const removedItem = this.removedItems[i];
|
||||
this.AddItem(removedItem);
|
||||
}
|
||||
|
||||
}
|
||||
public AddItem(item: DrawingItem) {
|
||||
let existingItem = this.drawingItems.find(i => i.name == item.name);
|
||||
if (existingItem) {
|
||||
existingItem.unitAmount += item.unitAmount;
|
||||
} else {
|
||||
this.drawingItems.push(item);
|
||||
public get roundPhase(): string {
|
||||
switch (this.md2Service.info.roundPhase) {
|
||||
case RoundPhase.HeroPhase:
|
||||
return StringUtils.getHtmlBadge("Hero Action Phase", "primary")
|
||||
case RoundPhase.EnemyPhase:
|
||||
return StringUtils.getHtmlBadge("Enemy Action Phase", "danger")
|
||||
case RoundPhase.LevelUpPhase:
|
||||
return StringUtils.getHtmlBadge("Level Up Phase", "success")
|
||||
case RoundPhase.DarknessPhase:
|
||||
return StringUtils.getHtmlBadge("Darkness Phase", "dark")
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
public RemoveItem(item: DrawingItem) {
|
||||
let existingItem = this.removedItems.find(i => i.name == item.name);
|
||||
if (existingItem) {
|
||||
existingItem.unitAmount += item.unitAmount;
|
||||
} else {
|
||||
this.removedItems.push(item);
|
||||
}
|
||||
public get anyHeroRemainAction(): boolean {
|
||||
return this.md2Service.heros.some(h => h.remainActions > 0);
|
||||
}
|
||||
public ClearAllItems() {
|
||||
|
||||
this.drawingItems = [];
|
||||
this.removedItems = [];
|
||||
heroAction(hero: MD2HeroInfo, action: string) {
|
||||
|
||||
}
|
||||
}
|
||||
export class DrawingItem {
|
||||
constructor(
|
||||
name: string,
|
||||
description: string,
|
||||
imageUrl: string,
|
||||
unitAmount: number = 1
|
||||
) {
|
||||
this.imageUrl = imageUrl
|
||||
this.name = name
|
||||
this.description = description
|
||||
this.unitAmount = unitAmount
|
||||
enterBossFight() {
|
||||
this.msgBoxService.showInputbox('Boss Fight', 'Choose the boss').pipe(first()).subscribe(result => {
|
||||
this.md2Service.info.isBossFight = true;
|
||||
this.md2Service.info.boss = new BossMicheal(this.md2Service);
|
||||
this.detectChanges();
|
||||
});
|
||||
}
|
||||
imageUrl: string
|
||||
name: string
|
||||
description: string
|
||||
unitAmount: number
|
||||
}
|
||||
135
src/app/games/massive-darkness2/massive-darkness2.model.boss.ts
Normal file
135
src/app/games/massive-darkness2/massive-darkness2.model.boss.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import { Subject } from "rxjs"
|
||||
import { first } from "rxjs/operators"
|
||||
import { MD2Service } from "../../services/md2.service"
|
||||
import { StringUtils } from "../../utilities/string-utils"
|
||||
import { BossActivationComponent } from "./boss-fight/boss-activation/boss-activation.component"
|
||||
import { TreasureType, AttackInfo, DefenseInfo, AttackType, MD2Icon, MD2HeroInfo, AttackTarget, MobInfo } from "./massive-darkness2.model"
|
||||
import { 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
|
||||
combatInfo: MobSkill
|
||||
activating(): boolean
|
||||
prepareForBossFight(): void
|
||||
nextRound(): void
|
||||
}
|
||||
export class BossMicheal implements IBossFight {
|
||||
|
||||
constructor(private md2Service: MD2Service) {
|
||||
this.name = 'Michael - The Corrupted Archangel';
|
||||
this.imgUrl = md2Service.imgUrl('/Boss/Michael - The Corrupted Archangel.jpg');
|
||||
this.standUrl = md2Service.imgUrl('/Boss/Michael.png');
|
||||
this.hpPerHero = 15;
|
||||
this.info = new MobInfo({
|
||||
isRoamingMonster: true,
|
||||
hp: this.md2Service.heros.length * this.hpPerHero,
|
||||
level: 10
|
||||
});
|
||||
this.info.defenseInfos = new DefenseInfo(5, 1);
|
||||
this.info.attackInfos = [new AttackInfo(MD2Icon.Melee, 2, 2, 0, 1)];
|
||||
this.actions = 1;
|
||||
this.rounds = 0;
|
||||
this.actionBlackDice = 2;
|
||||
this.corruptionTokenHtml = this.md2Service.imgHtml('Tokens/CorruptToken.png');
|
||||
|
||||
this.combatInfo = new MobSkill(`Combat 1${this.md2Service.iconHtml(MD2Icon.EnemySkill)}`,
|
||||
`Deal 1 Wound for each ${this.corruptionTokenHtml} on the attacking or defending Hero. Discard the tokens afterwards(once per combat).`);;
|
||||
|
||||
}
|
||||
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
|
||||
combatInfo: MobSkill
|
||||
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.`, beenAttackedHero);
|
||||
|
||||
break;
|
||||
case 1:
|
||||
//Lance Dash
|
||||
beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.LeastCorruption, true);
|
||||
bossAction = new MobSkill('Lance Dash',
|
||||
`Move Michael and Place 1 ${this.corruptionTokenHtml} in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.`, beenAttackedHero);
|
||||
break;
|
||||
case 2:
|
||||
//Dark Blessing
|
||||
bossAction = new MobSkill('Dark Blessing',
|
||||
`Place Michael ion the central Zone and add 1 ${this.corruptionTokenHtml} to the Corruption Stone Zone with the least amount of ${this.corruptionTokenHtml}.<br>` +
|
||||
`Deal <b>${this.darkBlessingCorruptionAmt}</b> Wounds per ${this.corruptionTokenHtml} to all Heros in each Tiles <b>distributed as they wish</b>.`, beenAttackedHero);
|
||||
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.defenseInfos[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 class MobSkill {
|
||||
constructor(skillName: string, skillDescription: string, targetHeros: MD2HeroInfo[] = []) {
|
||||
this.skillName = skillName
|
||||
this.skillDescription = skillDescription
|
||||
this.targetHeros = targetHeros
|
||||
}
|
||||
skillName: string
|
||||
skillDescription: string
|
||||
targetHeros: MD2HeroInfo[]
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
export class RollingBlackDice {
|
||||
roll(times: number) {
|
||||
let wounds = 0;
|
||||
let claws = 0;
|
||||
//miss 33%
|
||||
//1 claw 33%
|
||||
//1 wound 17%
|
||||
//1 claw, 1 wound 17%
|
||||
|
||||
for (let i = 0; i < times; i++) {
|
||||
let result = Math.random() * 100;
|
||||
if (result <= 33) {
|
||||
} else if (result <= 67) {
|
||||
claws += 1;
|
||||
} else if (result <= 83) {
|
||||
wounds += 1;
|
||||
} else {
|
||||
claws += 1;
|
||||
wounds += 1;
|
||||
}
|
||||
}
|
||||
return { claws, wounds };
|
||||
}
|
||||
}
|
||||
447
src/app/games/massive-darkness2/massive-darkness2.model.ts
Normal file
447
src/app/games/massive-darkness2/massive-darkness2.model.ts
Normal file
@ -0,0 +1,447 @@
|
||||
import { Subject } from "rxjs";
|
||||
import { ObjectUtils } from "../../utilities/object-utils";
|
||||
import { GamePlayer } from "../games.model";
|
||||
import { MobSkill } from "./massive-darkness2.model.boss";
|
||||
|
||||
export enum MobDlgType {
|
||||
Spawn,
|
||||
Activating,
|
||||
BeenAttacked,
|
||||
PreView
|
||||
}
|
||||
export enum RoundPhase {
|
||||
HeroPhase,
|
||||
EnemyPhase,
|
||||
LevelUpPhase,
|
||||
DarknessPhase,
|
||||
BossActivation
|
||||
}
|
||||
export enum TreasureType {
|
||||
Common,
|
||||
Rare,
|
||||
Epic,
|
||||
Legendary
|
||||
}
|
||||
export enum HeroClass {
|
||||
Berserker,
|
||||
Wizard,
|
||||
Rogue,
|
||||
Ranger,
|
||||
Shaman,
|
||||
Paladin,
|
||||
}
|
||||
export enum MD2Icon {
|
||||
Attack,
|
||||
Defense,
|
||||
Mana,
|
||||
Shadow,
|
||||
EnemySkill,
|
||||
EnemyClaw,
|
||||
Reroll,
|
||||
Fire,
|
||||
Frost,
|
||||
OneHand,
|
||||
TwoHand,
|
||||
Helmet,
|
||||
Armor,
|
||||
Ring,
|
||||
Foot,
|
||||
Melee,
|
||||
Range,
|
||||
Magic,
|
||||
HP,
|
||||
MP,
|
||||
Dice,
|
||||
Arrow,
|
||||
ArrowBullseye,
|
||||
ArrowOverload,
|
||||
SoulToken,
|
||||
Rage,
|
||||
RedDice,
|
||||
BlueDice,
|
||||
YellowDice,
|
||||
OrangeDice
|
||||
}
|
||||
export enum AttackTarget {
|
||||
Random = 40,
|
||||
LowestHp = 50,
|
||||
HighestHp = 60,
|
||||
HighestMp = 70,
|
||||
LowestLevel = 80,
|
||||
MostCorruption = 200,
|
||||
LeastCorruption = 201
|
||||
}
|
||||
export enum AttackType {
|
||||
Melee = 15,
|
||||
Range = 16,
|
||||
Magic = 17
|
||||
}
|
||||
export class AttackInfo {
|
||||
constructor(
|
||||
type: MD2Icon,
|
||||
yellow: number = 0,
|
||||
orange: number = 0,
|
||||
red: number = 0,
|
||||
black: number = 0
|
||||
) {
|
||||
this.type = type
|
||||
this.orange = orange
|
||||
this.red = red
|
||||
this.yellow = yellow
|
||||
this.black = black
|
||||
}
|
||||
type: MD2Icon
|
||||
orange: number
|
||||
red: number
|
||||
yellow: number
|
||||
black: number
|
||||
attackSkill: MobSkill
|
||||
}
|
||||
export class DefenseInfo {
|
||||
constructor(blue: number, black: number = 0) {
|
||||
this.blue = blue
|
||||
this.black = black
|
||||
}
|
||||
blue: number
|
||||
black: number
|
||||
defenseSkill: MobSkill
|
||||
}
|
||||
export class MD2LevelUpReward {
|
||||
constructor(config: Partial<MD2LevelUpReward>) {
|
||||
Object.assign(this, config);
|
||||
|
||||
}
|
||||
level: number = 1;
|
||||
needExp: number = 0;
|
||||
currentExp = 0
|
||||
extraHp = 0
|
||||
extraMp = 0
|
||||
extraRareToken = 0
|
||||
extraEpicToken = 0
|
||||
}
|
||||
export class DrawingBag<T extends IDrawingItem> {
|
||||
constructor(drawingItems: IDrawingItem[] = []) {
|
||||
this.drawingItems = drawingItems;
|
||||
this.removedItems = [];
|
||||
}
|
||||
drawingItems: IDrawingItem[]
|
||||
removedItems: IDrawingItem[]
|
||||
public bagIsEmpty() {
|
||||
return this.drawingItems.reduce((sum, current) => sum + current.drawingWeight, 0) == 0;
|
||||
}
|
||||
|
||||
public Draw(amount: number): T[] {
|
||||
let drawItems: T[] = this.DrawAndRemove(amount);
|
||||
this.RestoreRemoveItems();
|
||||
return drawItems;
|
||||
}
|
||||
public DrawAndRemove(amount: number = 1, predicate: (value: T) => boolean = undefined): T[] {
|
||||
let drawItems: T[] = [];
|
||||
for (let i = 0; i < amount; i++) {
|
||||
if (!this.bagIsEmpty()) {
|
||||
let drawItem = null as T;
|
||||
let drawingPool = [] as T[];
|
||||
if (predicate) {
|
||||
drawingPool = this.drawingItems.filter(predicate) as T[];
|
||||
} else {
|
||||
drawingPool = this.drawingItems as T[];
|
||||
}
|
||||
let drawIndex = Math.random() * drawingPool.reduce((sum, current) => sum + current.drawingWeight, 0);
|
||||
|
||||
let drawCalc = 0;
|
||||
for (let i = 0; i < drawingPool.length; i++) {
|
||||
const item = drawingPool[i];
|
||||
drawCalc += item.drawingWeight;
|
||||
if (drawCalc >= drawIndex) {
|
||||
drawItem = ObjectUtils.CloneValue(item);
|
||||
drawItem.drawingWeight = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//ObjectUtils.CloneValue
|
||||
this.RemoveItem(drawItem);
|
||||
drawItems.push(drawItem);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return drawItems;
|
||||
}
|
||||
|
||||
public RestoreRemoveItems() {
|
||||
for (let i = 0; i < this.removedItems.length; i++) {
|
||||
const removedItem = this.removedItems[i];
|
||||
this.AddItem(removedItem);
|
||||
}
|
||||
this.removedItems = [];
|
||||
}
|
||||
public AddItem(item: IDrawingItem) {
|
||||
let existingItem = this.drawingItems.find(i => i.name == item.name);
|
||||
if (existingItem) {
|
||||
existingItem.drawingWeight += item.drawingWeight;
|
||||
} else {
|
||||
this.drawingItems.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
public RemoveItem(item: IDrawingItem) {
|
||||
if (item) {
|
||||
let existingItem = this.drawingItems.find(i => i.name == item.name);
|
||||
if (existingItem) {
|
||||
existingItem.drawingWeight -= item.drawingWeight;
|
||||
|
||||
let removedItem = this.removedItems.find(i => i.name == item.name);
|
||||
if (removedItem) {
|
||||
removedItem.drawingWeight += item.drawingWeight;
|
||||
} else {
|
||||
this.removedItems.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public ClearAllItems() {
|
||||
|
||||
this.drawingItems = [];
|
||||
this.removedItems = [];
|
||||
}
|
||||
}
|
||||
|
||||
export interface IDrawingItem {
|
||||
imageUrl: string
|
||||
name: string
|
||||
description: string
|
||||
drawingWeight: number
|
||||
}
|
||||
export class DrawingItem implements IDrawingItem {
|
||||
constructor(
|
||||
name: string,
|
||||
description: string,
|
||||
imageUrl: string,
|
||||
drawingWeight: number = 1
|
||||
) {
|
||||
this.imageUrl = imageUrl
|
||||
this.name = name
|
||||
this.description = description
|
||||
this.drawingWeight = drawingWeight
|
||||
}
|
||||
imageUrl: string
|
||||
name: string
|
||||
description: string
|
||||
drawingWeight: number
|
||||
}
|
||||
|
||||
export class MobInfo implements IDrawingItem {
|
||||
constructor(
|
||||
config: Partial<MobInfo> = {}
|
||||
) {
|
||||
Object.assign(this, config);
|
||||
this.description = config.name;
|
||||
this.drawingWeight = 1;
|
||||
this.unitRemainHp = config.hp
|
||||
}
|
||||
imageUrl: string
|
||||
standUrl: string
|
||||
name: string
|
||||
description: string
|
||||
drawingWeight: number
|
||||
level: number;
|
||||
rewardTokens: number;
|
||||
hp: number;
|
||||
mobAmount: number;
|
||||
carriedTreasure: DrawingItem[];
|
||||
fixedCarriedTreasure: DrawingItem[];
|
||||
unitRemainHp: number;
|
||||
isRoamingMonster: boolean = false;
|
||||
attackInfos: AttackInfo[];
|
||||
defenseInfos: DefenseInfo;
|
||||
fireToken: number = 0;
|
||||
frozenToken: number = 0;
|
||||
corruptionToken: number = 0;
|
||||
uiWounds: number;
|
||||
uiFireTokens: number;
|
||||
uiFrozenTokens: number;
|
||||
uiCorruptionTokens: number;
|
||||
uiAttackedBy: string;
|
||||
|
||||
public get carriedTreasureHtml(): string {
|
||||
if (!this.carriedTreasure) return '';
|
||||
return this.carriedTreasure.map(i => `<img src="${i.imageUrl}" class='mr-1' width="40px">`)
|
||||
.concat(this.fixedCarriedTreasure?.map(i => `<img src="${i.imageUrl}" class='mr-1' width="40px">`)).join();
|
||||
}
|
||||
|
||||
public get totalHp(): number {
|
||||
return this.isRoamingMonster ? this.unitRemainHp : (this.mobAmount - 1) * this.hp + this.unitRemainHp;
|
||||
}
|
||||
|
||||
public get minionAmount(): number {
|
||||
return (this.mobAmount - 1);
|
||||
}
|
||||
public get leaderExp(): number {
|
||||
return this.isRoamingMonster ? 4 : 2;
|
||||
}
|
||||
|
||||
public get mobInfoHtml(): string {
|
||||
let html = `<img src="${this.imageUrl}" class="g-height-50vh">`
|
||||
+ `<br>Target Unit HP:${this.unitRemainHp}`;
|
||||
|
||||
if (this.isRoamingMonster) {
|
||||
html += `<br><label class="label">Alive Units:${this.mobAmount}`;
|
||||
} else {
|
||||
html += `<br>Total HP:${this.totalHp}`;
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
public getCssClass(): string {
|
||||
let levelString = '';
|
||||
if (this.level < 3) {
|
||||
levelString = '-lv1-2';
|
||||
} else if (this.level < 5) {
|
||||
|
||||
levelString = '-lv3-4';
|
||||
} else {
|
||||
levelString = '-lv5';
|
||||
}
|
||||
return `${this.name.replace(' ', '')}${levelString}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class MD2HeroInfo {
|
||||
constructor(
|
||||
config: Partial<MD2HeroInfo> = {}
|
||||
) {
|
||||
Object.assign(this, config);
|
||||
}
|
||||
class: HeroClass;
|
||||
name: string;
|
||||
hp: number;
|
||||
mp: number;
|
||||
ap: number;
|
||||
hpMaximum: number;
|
||||
mpMaximum: number;
|
||||
exp: number = 0;
|
||||
level: number = 1;
|
||||
fireToken: number = 0;
|
||||
frozenToken: number = 0;
|
||||
corruptionToken: number = 0;
|
||||
playerInfo: GamePlayer;
|
||||
imgUrl: string;
|
||||
skillHtml: string;
|
||||
shadowSkillHtml: string;
|
||||
remainActions: number = 3;
|
||||
rage: number = 0;
|
||||
|
||||
|
||||
public get heroFullName(): string {
|
||||
return `${this.playerInfo.name} (${HeroClass[this.class]} - ${this.name}`
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class MD2Rules {
|
||||
public static CoreGameLevelBoard = [
|
||||
new MD2LevelUpReward({ level: 2, needExp: 5, extraHp: 1, extraMp: 0, extraRareToken: 1 }),
|
||||
new MD2LevelUpReward({ level: 3, needExp: 10, extraHp: 1, extraMp: 1, extraEpicToken: 1 }),
|
||||
new MD2LevelUpReward({ level: 4, needExp: 12, extraHp: 2, extraMp: 2, extraEpicToken: 1 }),
|
||||
new MD2LevelUpReward({ level: 5, needExp: 18, extraHp: 2, extraMp: 2, extraEpicToken: 1 }),
|
||||
];
|
||||
public static checkCoreGameLevelup(currentLevel: number, currentExp: number): MD2LevelUpReward {
|
||||
let result = null as MD2LevelUpReward;
|
||||
let nextLevel = this.CoreGameLevelBoard.find(r => r.level > currentLevel && currentExp >= r.needExp);
|
||||
|
||||
if (nextLevel) {
|
||||
result = ObjectUtils.CloneValue(nextLevel) as MD2LevelUpReward;
|
||||
result.currentExp = currentExp - nextLevel.needExp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export class MD2EnemyPhaseSpecialInfo {
|
||||
specialRule: MD2EnemyPhaseSpecialRule
|
||||
specialRules = [
|
||||
new MD2EnemyPhaseSpecialRule(30, '', ''),
|
||||
new MD2EnemyPhaseSpecialRule(44, 'Surprise Attack',
|
||||
'One Mob with line of sight to a hero changes its attack profile to ranged during its <b>SECOND</b> action.<br>' +
|
||||
'You have to apply this to a Mob that is not in the same zone as a hero, and that could not attack otherwise.<br>' +
|
||||
'If no Mob has line of sight to a hero, one Mob moves an additional zone instead.<br>'),
|
||||
new MD2EnemyPhaseSpecialRule(22, 'Fast Advance', 'Each Mob moves 1 additional zone in its first move action.<br>' +
|
||||
'If a Mob doesn’t move, it adds one additional black enemy die to it’s first attack action instead.<br>' +
|
||||
'(If it already has all six black enemy dice in its attack pool, it rerolls one blank result on a black die instead.)<br>'),
|
||||
new MD2EnemyPhaseSpecialRule(4, 'A Dark Portal Appears, Guiding Predators To Their Prey',
|
||||
'First, move one green portal token from the board to the zone with the hero who has the least amount of health tokens. (If there is no green portal token on the board, put one onto the board.)<br>' +
|
||||
'Then, move the Mob that is furthest away from any heroes into this new portal zone. In its following activation, this Mob only has one action.'),
|
||||
]
|
||||
}
|
||||
export class MD2EnemyPhaseSpecialRule implements IDrawingItem {
|
||||
constructor(drawingWeight: number, title: string, description: string) {
|
||||
this.drawingWeight = drawingWeight
|
||||
this.title = title
|
||||
this.description = description
|
||||
}
|
||||
imageUrl: string;
|
||||
name: string;
|
||||
drawingWeight: number;
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface IDarknessPhaseRule {
|
||||
addTreasureToken: Subject<TreasureType>
|
||||
spawnMob: Subject<void>
|
||||
spawnRoamingMonster: Subject<void>
|
||||
runDarknessPhase(): boolean
|
||||
}
|
||||
export class CoreGameDarknessPhaseRule implements IDarknessPhaseRule {
|
||||
round: number = 1;
|
||||
frontEndRound: number = 9;
|
||||
extraRound: number = 0;
|
||||
constructor() {
|
||||
}
|
||||
addTreasureToken = new Subject<TreasureType>();
|
||||
spawnMob = new Subject<void>();
|
||||
spawnRoamingMonster = new Subject<void>();
|
||||
runDarknessPhase() {
|
||||
if (this.round >= this.frontEndRound) {
|
||||
this.extraRound++;
|
||||
if (this.extraRound % 4 == 0) {
|
||||
this.spawnRoamingMonster.next();
|
||||
return false;
|
||||
} else if (this.extraRound % 2 == 0) {
|
||||
this.spawnMob.next();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
this.round++;
|
||||
|
||||
switch (this.round) {
|
||||
case 3:
|
||||
case 9:
|
||||
this.spawnMob.next();
|
||||
return false;
|
||||
break;
|
||||
case 4:
|
||||
this.addTreasureToken.next(TreasureType.Rare);
|
||||
break;
|
||||
case 5:
|
||||
case 7:
|
||||
this.spawnRoamingMonster.next();
|
||||
return false;
|
||||
break;
|
||||
case 6:
|
||||
case 8:
|
||||
this.addTreasureToken.next(TreasureType.Epic);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1 @@
|
||||
<op-drop-down [(ngModel)]="playerTabId" [source]="heroOptions" (blur)="onBlur()"></op-drop-down>
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MD2HeroSelectComponent } from './md2-hero-select.component';
|
||||
|
||||
describe('MD2HeroSelectComponent', () => {
|
||||
let component: MD2HeroSelectComponent;
|
||||
let fixture: ComponentFixture<MD2HeroSelectComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MD2HeroSelectComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MD2HeroSelectComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,105 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, Renderer2 } from '@angular/core';
|
||||
import { ControlValueAccessor, Validator, AbstractControl, ValidationErrors, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { DropDownOption } from '../../../entity/dropDownOption';
|
||||
import { MD2Service } from '../../../services/md2.service';
|
||||
import { ArrayUtils } from '../../../utilities/array-utils';
|
||||
import { HeroClass, MD2HeroInfo } from '../massive-darkness2.model';
|
||||
|
||||
@Component({
|
||||
selector: 'md2-hero-select',
|
||||
templateUrl: './md2-hero-select.component.html',
|
||||
styleUrls: ['./md2-hero-select.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => MD2HeroSelectComponent),
|
||||
multi: true
|
||||
},
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class MD2HeroSelectComponent implements ControlValueAccessor, Validator {
|
||||
|
||||
private _lastBlurValue: string;
|
||||
playerTabId: string;
|
||||
readonly: boolean = false;
|
||||
isRequired: boolean = false;
|
||||
heroOptions: DropDownOption[];
|
||||
@Input() id?= '';
|
||||
@Input() name = '';
|
||||
@Input() data: MD2HeroInfo;
|
||||
|
||||
@Output() focus = new EventEmitter();
|
||||
@Output() blur = new EventEmitter<MD2HeroInfo>();
|
||||
|
||||
@Input('readonly')
|
||||
public set input_readonly(value) {
|
||||
this.readonly = typeof value !== 'undefined' && value !== false;
|
||||
}
|
||||
@Input('isRequired')
|
||||
public set input_isRequired(value) {
|
||||
this.isRequired = typeof value !== 'undefined' && value !== false;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private elementRef: ElementRef,
|
||||
private renderer: Renderer2,
|
||||
private md2Service: MD2Service,
|
||||
private cdRef: ChangeDetectorRef
|
||||
) {
|
||||
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();
|
||||
}
|
||||
|
||||
validate(control: AbstractControl): ValidationErrors {
|
||||
// if (this.required && (this.value == null || this.value == 0)) {
|
||||
// return { 'currency': '' };
|
||||
// }
|
||||
return null;
|
||||
}
|
||||
registerOnValidatorChange?(fn: () => void): void {
|
||||
}
|
||||
onChange = (value: number) => { };
|
||||
onTouched = () => { };
|
||||
writeValue(obj: MD2HeroInfo): void {
|
||||
this.data = obj;
|
||||
}
|
||||
registerOnChange(fn: any): void {
|
||||
this.onChange = fn;
|
||||
}
|
||||
registerOnTouched(fn: any): void {
|
||||
this.onTouched = fn;
|
||||
}
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
this.readonly = isDisabled;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
this.heroOptions = ArrayUtils.ToDropDownOptions(this.md2Service.heros, h => h.playerInfo.tabId,
|
||||
hero => this.md2Service.heroFullName(hero));
|
||||
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.renderer.removeAttribute(this.elementRef.nativeElement, 'id')
|
||||
}
|
||||
onBlur() {
|
||||
if (this._lastBlurValue != this.playerTabId) {
|
||||
this._lastBlurValue = this.playerTabId;
|
||||
this.data = this.md2Service.heros.find(h => h.playerInfo.tabId == this.playerTabId);
|
||||
this.blur.emit(this.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
<span class="MD2Icon {{icon}} {{iconClass}}"></span>
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MD2IconComponent } from './md2-icon.component';
|
||||
|
||||
describe('MD2IconComponent', () => {
|
||||
let component: MD2IconComponent;
|
||||
let fixture: ComponentFixture<MD2IconComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MD2IconComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MD2IconComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,21 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { MD2Icon } from '../massive-darkness2.model';
|
||||
|
||||
@Component({
|
||||
selector: 'md2-icon',
|
||||
templateUrl: './md2-icon.component.html',
|
||||
styleUrls: ['./md2-icon.component.scss']
|
||||
})
|
||||
export class MD2IconComponent implements OnInit {
|
||||
|
||||
@Input() iconClass: string = '';
|
||||
|
||||
@Input("icon") icon: MD2Icon;
|
||||
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
<label class='label'>Weapon Info</label>
|
||||
<div class="g-brd-3 g-brd-bottom--dashed g-brd-gray-light-v2 mb-3 mt-2 row" *ngFor="let info of mob.attackInfos">
|
||||
<div class="col-md-4">
|
||||
<span class=" g-font-size-50" [innerHtml]="iconHtml(info.type)"></span>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div *ngIf="info.yellow" class="g-height-45">
|
||||
<span class="MD2Icon Yellow dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{info.yellow}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="info.orange" class="g-height-45 mt-1">
|
||||
<span class="MD2Icon Orange dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{info.orange}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="info.red" class="g-height-45 mt-1">
|
||||
<span class="MD2Icon Red dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{info.red}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MobAttackInfoComponent } from './mob-attack-info.component';
|
||||
|
||||
describe('MobAttackInfoComponent', () => {
|
||||
let component: MobAttackInfoComponent;
|
||||
let fixture: ComponentFixture<MobAttackInfoComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MobAttackInfoComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MobAttackInfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,29 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { MD2Icon, MobInfo } from '../../../massive-darkness2.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-mob-attack-info',
|
||||
templateUrl: './mob-attack-info.component.html',
|
||||
styleUrls: ['./mob-attack-info.component.scss']
|
||||
})
|
||||
export class MobAttackInfoComponent 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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
<p>mob-combat-info works!</p>
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MobCombatInfoComponent } from './mob-combat-info.component';
|
||||
|
||||
describe('MobCombatInfoComponent', () => {
|
||||
let component: MobCombatInfoComponent;
|
||||
let fixture: ComponentFixture<MobCombatInfoComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MobCombatInfoComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MobCombatInfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,15 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-mob-combat-info',
|
||||
templateUrl: './mob-combat-info.component.html',
|
||||
styleUrls: ['./mob-combat-info.component.scss']
|
||||
})
|
||||
export class MobCombatInfoComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
<p>mob-def-info works!</p>
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MobDefInfoComponent } from './mob-def-info.component';
|
||||
|
||||
describe('MobDefInfoComponent', () => {
|
||||
let component: MobDefInfoComponent;
|
||||
let fixture: ComponentFixture<MobDefInfoComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MobDefInfoComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MobDefInfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,15 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-mob-def-info',
|
||||
templateUrl: './mob-def-info.component.html',
|
||||
styleUrls: ['./mob-def-info.component.scss']
|
||||
})
|
||||
export class MobDefInfoComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
<label class='label'>Level <b class="MD2text g-font-size-18">{{mob.level}}</b></label><br>
|
||||
|
||||
<ng-container *ngIf="mob.mobAmount">
|
||||
<label class='label'>Alive Units <b class="MD2text g-font-size-18">{{mob.mobAmount}}</b></label><br>
|
||||
</ng-container>
|
||||
|
||||
<label class='label'>Total HP <b class="MD2text g-font-size-18">{{mob.totalHp}}</b></label><br>
|
||||
|
||||
<ng-container *ngIf="mob.carriedTreasureHtml">
|
||||
<label class='label'>Carried Treasure</label><br>
|
||||
<div [innerHtml]="mob.carriedTreasureHtml"></div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="mob.attackInfos&&mob.attackInfos.length>0&& !hideWeaponInfo">
|
||||
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="mob.defenseInfos&& !hideWeaponInfo">
|
||||
<label class='label'>Defense Info</label>
|
||||
<div class="g-brd-3 g-brd-bottom--dashed g-brd-gray-light-v2 mb-3 mt-2 row">
|
||||
<div class="col-md-4">
|
||||
<span class="g-font-size-50" [innerHtml]="iconHtml(MD2Icon.Defense)"></span>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div *ngIf="mob.defenseInfos.blue" class="g-height-45">
|
||||
<span class="MD2Icon Blue dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{mob.defenseInfos.blue}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="mob.defenseInfos.black" class="g-height-45 mt-1">
|
||||
<span class="MD2Icon Black dice g-font-size-50">
|
||||
<span class="MD2text diceAmount">x{{mob.defenseInfos.black}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div *ngIf="showBlackDice" class="row">
|
||||
<!-- <md2-icon></md2-icon> -->
|
||||
<div class="col-md-4">
|
||||
<span class=" g-font-size-50" [innerHtml]="iconHtml(MD2Icon.EnemySkill)"></span>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<span class="MD2Icon Black dice g-font-size-50">
|
||||
<span class="MD2text blackDiceAmount">x{{mob.minionAmount}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,19 @@
|
||||
.diceAmount {
|
||||
color: white;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
left: 25px;
|
||||
font-size: 30px;
|
||||
}
|
||||
.dice {
|
||||
&::before {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
.blackDiceAmount {
|
||||
color: white;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
left: 26px;
|
||||
font-size: 30px;
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MobDetailInfoComponent } from './mob-detail-info.component';
|
||||
|
||||
describe('MobDetailInfoComponent', () => {
|
||||
let component: MobDetailInfoComponent;
|
||||
let fixture: ComponentFixture<MobDetailInfoComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MobDetailInfoComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MobDetailInfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,67 @@
|
||||
import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { MD2Service } from '../../../../services/md2.service';
|
||||
import { StateService } from '../../../../services/state.service';
|
||||
import { MD2Icon, MobDlgType, MobInfo } from '../../massive-darkness2.model';
|
||||
import { MD2ComponentBase } from '../../MD2Base';
|
||||
|
||||
@Component({
|
||||
selector: 'md2-mob-detail-info',
|
||||
templateUrl: './mob-detail-info.component.html',
|
||||
styleUrls: ['./mob-detail-info.component.scss']
|
||||
})
|
||||
export class MobDetailInfoComponent 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;
|
||||
}
|
||||
|
||||
|
||||
showAttackingInfo: boolean = false;
|
||||
@Input("showAttackingInfo")
|
||||
public set input_showAttackingInfo(value) {
|
||||
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(
|
||||
public md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
super(md2Service, stateService, route, cdRef);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
if (this.mob.attackInfos) {
|
||||
|
||||
}
|
||||
}
|
||||
initWeaponInfo() {
|
||||
|
||||
if (this.mob.attackInfos) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
71
src/app/games/massive-darkness2/mobs/mobs.component.html
Normal file
71
src/app/games/massive-darkness2/mobs/mobs.component.html
Normal file
@ -0,0 +1,71 @@
|
||||
<nb-card>
|
||||
<nb-card-header>
|
||||
<img src="{{imgUrl('Mobs/MobToken.png')}}" width="40px"> {{(isRoamingMonster?'Roaming Monsters':'Mobs')}}
|
||||
<!-- <button nbButton hero status="warning" size="small" (click)="initMobDecks()" class="float-right">Reset
|
||||
Mobs</button> -->
|
||||
<button nbButton hero status="danger" size="small" (click)="spawnMob()" class="float-right"
|
||||
*ngIf="isRoamingMonster">Spawn Roaming
|
||||
Monster</button>
|
||||
<button nbButton hero status="warning" size="small" (click)="spawnMob()" class="float-right mr-2"
|
||||
*ngIf="!isRoamingMonster">Spawn
|
||||
Mob</button>
|
||||
<!-- <button nbButton hero status="warning" size="tiny" (click)="resetTreasureBag()"
|
||||
class="float-right">Reset</button>
|
||||
<button nbButton hero status="primary" size="tiny" (click)="addTreasure(TreasureType.Epic)"
|
||||
class="float-right mr-1">Add
|
||||
Epic</button>
|
||||
<button nbButton hero status="info" size="tiny" (click)="addTreasure(TreasureType.Rare)"
|
||||
class="float-right mr-1">Add
|
||||
Rare</button> -->
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<!-- <b>Content of the bag</b>
|
||||
<hr class="my-1"> -->
|
||||
<div class="row">
|
||||
<!-- this.mobs <div class="col" *ngFor="let treasure of treasureBag.drawingItems">
|
||||
<img src="{{treasure.imageUrl}}" width="40px"> X {{treasure.unitAmount}}
|
||||
</div> -->
|
||||
<div class="col col-sm-6 col-md-6 col-lg-4 mb-4" *ngFor="let mob of this.mobs">
|
||||
<div class="row no-gutters">
|
||||
<div class="col-12 col-md-8">
|
||||
|
||||
<img class="mobImg g-width-95x" src="{{mob.imageUrl}}" (click)="showMobImage(mob)" />
|
||||
</div>
|
||||
<div class=" col-12 col-md-4">
|
||||
|
||||
|
||||
<md2-mob-detail-info name="mobDetail{{mob.name}}" [mob]="mob" [mode]="MobDlgType.PreView">
|
||||
</md2-mob-detail-info>
|
||||
|
||||
<adj-number-input name="mob{{mob.name}}" [(ngModel)]="mob.unitRemainHp"
|
||||
[maximum]="isRoamingMonster?null: mob.hp" minimum="1" title="Target Unit HP"
|
||||
(hitMinimum)="killOneUnit(mob)" (hitMaximum)="addOneUnit(mob)">
|
||||
</adj-number-input>
|
||||
<button nbButton hero status="danger" size="small" (click)="attackMob(mob)"
|
||||
*ngIf="md2Service.showAttackBtn">Attack It</button>
|
||||
<!-- <div class='form-group'>
|
||||
<label for='playerAmount' class='label'>Current Unit HP</label>
|
||||
<div>
|
||||
<button nbButton outline type="button" status="primary" size="small"
|
||||
(click)="adjustUnitHp(mob,false)">
|
||||
<nb-icon pack='eva' icon='minus-outline'></nb-icon>
|
||||
</button>
|
||||
<b class="mx-2">{{mob.unitRemainHp}}</b>
|
||||
<button nbButton outline type="button" status="primary" size="small"
|
||||
(click)="adjustUnitHp(mob,true)">
|
||||
<nb-icon pack='eva' icon='plus-outline'></nb-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="container">
|
||||
<img id="clip" src="{{imgUrl('Mobs/CoreGame/CoreGameMob.jpg')}}" />
|
||||
</div> -->
|
||||
<!-- <button nbButton hero fullWidth="" status="primary" class="mt-3" (click)="drawTreasure()">I feel
|
||||
LUCKY!!!!!</button> -->
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
103
src/app/games/massive-darkness2/mobs/mobs.component.scss
Normal file
103
src/app/games/massive-darkness2/mobs/mobs.component.scss
Normal file
@ -0,0 +1,103 @@
|
||||
$mob-card-width: 399px;
|
||||
$mob-card-height: 556px;
|
||||
.mobCol {
|
||||
height: 310px;
|
||||
}
|
||||
.mobImgContainer {
|
||||
overflow: hidden;
|
||||
width: $mob-card-width;
|
||||
height: $mob-card-height;
|
||||
|
||||
transition: all 0.4s ease-in-out;
|
||||
transform: translateX(-23%) translateY(-23%) scale(0.6);
|
||||
&.zoomIn {
|
||||
transform: scale(1.2);
|
||||
position: fixed;
|
||||
top: 25vh;
|
||||
left: 40vw;
|
||||
/* right: auto; */
|
||||
z-index: 99;
|
||||
}
|
||||
.mobImg {
|
||||
object-fit: cover;
|
||||
&.Gargoyles-lv1-2 {
|
||||
object-position: 0 0;
|
||||
}
|
||||
&.Demons-lv1-2 {
|
||||
object-position: calc(#{$mob-card-width} * -1) 0;
|
||||
}
|
||||
&.Undead-lv1-2 {
|
||||
object-position: calc(#{$mob-card-width} * -2) 0;
|
||||
}
|
||||
&.FireEntities-lv1-2 {
|
||||
object-position: calc(#{$mob-card-width} * -3) 0;
|
||||
}
|
||||
&.FallenAngels-lv1-2 {
|
||||
object-position: calc(#{$mob-card-width} * -4) 0;
|
||||
}
|
||||
&.InfernalImps-lv1-2 {
|
||||
object-position: calc(#{$mob-card-width} * -5) 0;
|
||||
}
|
||||
&.Skeletons-lv1-2 {
|
||||
object-position: calc(#{$mob-card-width} * -6) 0;
|
||||
}
|
||||
&.Satyrs-lv1-2 {
|
||||
object-position: calc(#{$mob-card-width} * -7) 0;
|
||||
}
|
||||
|
||||
&.Gargoyles-lv3-4 {
|
||||
object-position: 0 calc(#{$mob-card-height} * -1);
|
||||
}
|
||||
&.FireEntities-lv3-4 {
|
||||
object-position: calc(#{$mob-card-width} * -1) calc(#{$mob-card-height} * -1);
|
||||
}
|
||||
&.Skeletons-lv3-4 {
|
||||
object-position: calc(#{$mob-card-width} * -2) calc(#{$mob-card-height} * -1);
|
||||
}
|
||||
&.InfernalImps-lv3-4 {
|
||||
object-position: calc(#{$mob-card-width} * -3) calc(#{$mob-card-height} * -1);
|
||||
}
|
||||
&.Undead-lv3-4 {
|
||||
object-position: calc(#{$mob-card-width} * -4) calc(#{$mob-card-height} * -1);
|
||||
}
|
||||
&.Demons-lv3-4 {
|
||||
object-position: calc(#{$mob-card-width} * -5) calc(#{$mob-card-height} * -1);
|
||||
}
|
||||
&.Satyrs-lv3-4 {
|
||||
object-position: calc(#{$mob-card-width} * -6) calc(#{$mob-card-height} * -1);
|
||||
}
|
||||
&.FallenAngels-lv3-4 {
|
||||
object-position: calc(#{$mob-card-width} * -7) calc(#{$mob-card-height} * -1);
|
||||
}
|
||||
|
||||
&.Satyrs-lv5 {
|
||||
object-position: 0 calc(#{$mob-card-height} * -2);
|
||||
}
|
||||
&.FallenAngels-lv5 {
|
||||
object-position: calc(#{$mob-card-width} * -1) calc(#{$mob-card-height} * -2);
|
||||
}
|
||||
&.Gargoyles-lv5 {
|
||||
object-position: calc(#{$mob-card-width} * -2) calc(#{$mob-card-height} * -2);
|
||||
}
|
||||
&.FireEntities-lv5 {
|
||||
object-position: calc(#{$mob-card-width} * -3) calc(#{$mob-card-height} * -2);
|
||||
}
|
||||
&.InfernalImps-lv5 {
|
||||
object-position: calc(#{$mob-card-width} * -4) calc(#{$mob-card-height} * -2);
|
||||
}
|
||||
&.Undead-lv5 {
|
||||
object-position: calc(#{$mob-card-width} * -5) calc(#{$mob-card-height} * -2);
|
||||
}
|
||||
&.Skeletons-lv5 {
|
||||
object-position: calc(#{$mob-card-width} * -6) calc(#{$mob-card-height} * -2);
|
||||
}
|
||||
&.Demons-lv5 {
|
||||
object-position: calc(#{$mob-card-width} * -7) calc(#{$mob-card-height} * -2);
|
||||
}
|
||||
}
|
||||
}
|
||||
#clip {
|
||||
position: absolute;
|
||||
clip: rect(0, 100px, 200px, 0);
|
||||
/* clip: shape(top, right, bottom, left); NB 'rect' is the only available option */
|
||||
}
|
||||
25
src/app/games/massive-darkness2/mobs/mobs.component.spec.ts
Normal file
25
src/app/games/massive-darkness2/mobs/mobs.component.spec.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MobsComponent } from './mobs.component';
|
||||
|
||||
describe('MobsComponent', () => {
|
||||
let component: MobsComponent;
|
||||
let fixture: ComponentFixture<MobsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MobsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MobsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
233
src/app/games/massive-darkness2/mobs/mobs.component.ts
Normal file
233
src/app/games/massive-darkness2/mobs/mobs.component.ts
Normal file
@ -0,0 +1,233 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NbDialogService } from '@nebular/theme';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { FileService } from '../../../services/file.service';
|
||||
import { MD2Service } from '../../../services/md2.service';
|
||||
import { MsgBoxService } from '../../../services/msg-box.service';
|
||||
import { StateService } from '../../../services/state.service';
|
||||
import { ADIcon } from '../../../ui/alert-dlg/alert-dlg.model';
|
||||
import { NumberUtils } from '../../../utilities/number-utils';
|
||||
import { DrawingBag, DrawingItem, MD2Icon, MobDlgType, MobInfo, TreasureType } from '../massive-darkness2.model';
|
||||
import { MD2Base, MD2ComponentBase } from '../MD2Base';
|
||||
import { SpawnMobDlgComponent } from './spawn-mob-dlg/spawn-mob-dlg.component';
|
||||
|
||||
@Component({
|
||||
selector: 'md2-mobs',
|
||||
templateUrl: './mobs.component.html',
|
||||
styleUrls: ['./mobs.component.scss']
|
||||
})
|
||||
export class MobsComponent extends MD2ComponentBase implements OnInit {
|
||||
MobDlgType = MobDlgType;
|
||||
isRoamingMonster: boolean = false;
|
||||
|
||||
attacking: boolean = false;
|
||||
attackingAllExp: number = 0;
|
||||
attackingAttackerExp: number = 0;
|
||||
attackingAttackerReward: string = '';
|
||||
showRoundMessage: boolean = false;
|
||||
@Input("isRoamingMonster")
|
||||
public set input_isRoamingMonster(value) {
|
||||
this.isRoamingMonster = typeof value !== "undefined" && value !== false;
|
||||
}
|
||||
constructor(
|
||||
private fileService: FileService,
|
||||
private msgBoxService: MsgBoxService,
|
||||
private dlgService: NbDialogService,
|
||||
public md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
super(md2Service, stateService, route, cdRef);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initMobDecks();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public get mobs(): MobInfo[] {
|
||||
return this.isRoamingMonster ? this.md2Service.roamingMonsters : this.md2Service.mobs;
|
||||
}
|
||||
public set mobs(v: MobInfo[]) {
|
||||
if (this.isRoamingMonster) {
|
||||
this.md2Service.info.roamingMonsters = v;
|
||||
} else {
|
||||
this.md2Service.info.mobs = v;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
initMobDecks() {
|
||||
this.md2Service.initMobDecks();
|
||||
this.mobs = [];
|
||||
this.cdRef.detectChanges();
|
||||
let spawn$ = this.isRoamingMonster ? this.md2Service.darknessPhaseRule.spawnRoamingMonster : this.md2Service.darknessPhaseRule.spawnMob;
|
||||
spawn$.subscribe(result => {
|
||||
this.showRoundMessage = true;
|
||||
this.spawnMob();
|
||||
});
|
||||
}
|
||||
spawnMob() {
|
||||
let result = this.md2Service.drawMob(this.isRoamingMonster);
|
||||
if (result.exitingMob) {
|
||||
this.dlgService.open(SpawnMobDlgComponent, { context: { title: `${result.mob.description} Activate One Action Now!`, mode: MobDlgType.Activating, mob: result.mob } })
|
||||
.onClose.pipe(first()).subscribe(result => {
|
||||
this.afterSpawn()
|
||||
});
|
||||
} else {
|
||||
this.md2Service.refreshTreasureBagSubject.next(this.md2Service.treasureBag);
|
||||
//this.msgBoxService.show(`${newSpawnMob.description} Shows Up`, { text: actionText });
|
||||
this.dlgService.open(SpawnMobDlgComponent, { context: { title: `${result.mob.description} Shows Up`, mode: MobDlgType.Spawn, mob: result.mob } })
|
||||
.onClose.pipe(first()).subscribe(result => {
|
||||
this.afterSpawn();
|
||||
});
|
||||
}
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
afterSpawn() {
|
||||
this.cdRef.detectChanges();
|
||||
this.md2Service.broadcastMobsInfo();
|
||||
if (this.showRoundMessage) {
|
||||
this.msgBoxService.show(`${NumberUtils.Ordinal(this.md2Service.info.round)} Hero Phase`, { icon: ADIcon.INFO });
|
||||
}
|
||||
this.showRoundMessage = false;
|
||||
}
|
||||
adjustUnitHp(mob: MobInfo, adding: boolean) {
|
||||
|
||||
if (adding) {
|
||||
if (mob.unitRemainHp < mob.hp) {
|
||||
mob.unitRemainHp++;
|
||||
}
|
||||
} else {
|
||||
if (mob.unitRemainHp > 1) {
|
||||
mob.unitRemainHp--;
|
||||
}
|
||||
}
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
killOneUnit(mob: MobInfo) {
|
||||
if (mob.mobAmount > 1) {
|
||||
mob.mobAmount--;
|
||||
mob.unitRemainHp = mob.hp;
|
||||
if (this.attacking) {
|
||||
this.attackingAttackerExp += 1;
|
||||
} else {
|
||||
this.msgBoxService.show('The Attacker Gain 1 Exp', { icon: ADIcon.INFO }).pipe(first()).subscribe(result => {
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
this.mobs.splice(this.mobs.indexOf(mob), 1);
|
||||
mob.mobAmount = 0;
|
||||
|
||||
if (this.attacking) {
|
||||
this.attackingAllExp += mob.leaderExp;
|
||||
this.attackingAttackerExp += 1;
|
||||
this.attackingAttackerReward = `<br> and gains ${mob.carriedTreasureHtml}`;
|
||||
} else {
|
||||
this.msgBoxService.show(`All Hero Gain ${mob.leaderExp} Exp`, { text: `The Attacker Gain 1 Extra Exp.<br> and gains ${mob.carriedTreasureHtml}`, icon: ADIcon.INFO }).pipe(first()).subscribe(result => {
|
||||
|
||||
});
|
||||
}
|
||||
this.md2Service.mobBeenKilledSubject.next(mob);
|
||||
|
||||
}
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
addOneUnit(mob: MobInfo) {
|
||||
if (!this.isRoamingMonster) {
|
||||
mob.mobAmount++;
|
||||
mob.unitRemainHp = 1;
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
}
|
||||
attackMob(mob: MobInfo) {
|
||||
|
||||
this.attacking = true;
|
||||
this.attackingAllExp = 0;
|
||||
this.attackingAttackerExp = 0;
|
||||
this.attackingAttackerReward = '';
|
||||
this.dlgService.open(SpawnMobDlgComponent, { context: { title: `Attack ${mob.description}`, mode: MobDlgType.BeenAttacked, mob: mob } })
|
||||
.onClose.pipe(first()).subscribe(mobResult => {
|
||||
if (mobResult) {
|
||||
let attackDamage = mobResult.uiWounds;
|
||||
if (attackDamage) {
|
||||
do {
|
||||
if (attackDamage >= mob.unitRemainHp) {
|
||||
attackDamage -= mob.unitRemainHp;
|
||||
this.killOneUnit(mob);
|
||||
} else {
|
||||
let originAttackDamage = attackDamage;
|
||||
attackDamage -= mob.unitRemainHp;
|
||||
mob.unitRemainHp -= originAttackDamage;
|
||||
}
|
||||
} while (attackDamage > 0 && mob.mobAmount > 0);
|
||||
|
||||
this.attacking = false;
|
||||
|
||||
let attacker = this.md2Service.info.currentActivateHero;
|
||||
let attackerTitle = 'The Attacker';
|
||||
if (attacker) {
|
||||
attackerTitle = this.md2Service.heroFullName(attacker);
|
||||
}
|
||||
if (this.attackingAllExp > 0) {
|
||||
this.msgBoxService.show(`All Hero Gains ${this.attackingAllExp} Exp`,
|
||||
{ text: `${attackerTitle} Gains ${this.attackingAttackerExp} Extra Exp.<br> and gains ${mob.carriedTreasureHtml}`, icon: ADIcon.INFO })
|
||||
.pipe(first()).subscribe(result => {
|
||||
for (let i = 0; i < this.md2Service.heros.length; i++) {
|
||||
const hero = this.md2Service.heros[i];
|
||||
hero.exp += this.attackingAllExp;
|
||||
}
|
||||
attacker.exp += this.attackingAttackerExp;
|
||||
this.md2Service.broadcastAllHeroInfoToAll();
|
||||
});
|
||||
} else if (this.attackingAttackerExp > 0) {
|
||||
|
||||
this.msgBoxService.show(`${attackerTitle} Gains ${this.attackingAttackerExp} Exp`, { icon: ADIcon.INFO }).pipe(first()).subscribe(result => {
|
||||
attacker.exp += this.attackingAttackerExp;
|
||||
this.md2Service.broadcastAllHeroInfoToAll();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
this.cdRef.detectChanges();
|
||||
this.md2Service.broadcastMobsInfo();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
mobHpChanged(mob: MobInfo, newHp: number) {
|
||||
console.log(newHp);
|
||||
mob.unitRemainHp = newHp;
|
||||
this.cdRef.detectChanges();
|
||||
|
||||
}
|
||||
|
||||
showMobImage(mob: MobInfo) {
|
||||
this.dlgService.open(SpawnMobDlgComponent, { context: { title: `${mob.description}`, mode: MobDlgType.PreView, mob: mob } })
|
||||
.onClose.pipe(first()).subscribe(result => {
|
||||
|
||||
});
|
||||
//this.msgBoxService.show('', { text: mob.mobInfoHtml.replace('g-height-50vh', 'g-height-70vh') });
|
||||
}
|
||||
weaponHtml(mob: MobInfo) {
|
||||
let html = '<br>';
|
||||
mob.attackInfos.forEach(attackInfo => {
|
||||
html += this.iconHtml((attackInfo.type), 'mr-2');
|
||||
if (attackInfo.yellow > 0) {
|
||||
html += this.iconHtml(MD2Icon.YellowDice) + `<span class='MD2text g-font-size-18'> x ${attackInfo.yellow}</span>`
|
||||
}
|
||||
if (attackInfo.orange > 0) {
|
||||
html += this.iconHtml(MD2Icon.OrangeDice) + `<span class='MD2text g-font-size-18'> x ${attackInfo.orange}</span>`
|
||||
}
|
||||
html += "<br>";
|
||||
});
|
||||
return html;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
<nb-card status="{{headerStatus}}" class="mobCard" size="giant">
|
||||
<nb-card-header>
|
||||
<img src="{{imgUrl('Mobs/MobToken.png')}}" width="40px"> {{(mob.isRoamingMonster?'Roaming Monster':'Mob')}}
|
||||
<span [innerHtml]="titleHtml"></span>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<div class="row no-gutters">
|
||||
<div class="col-md-7">
|
||||
<img src="{{mob.imageUrl}}" class="g-width-90x">
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
|
||||
<md2-mob-detail-info [mob]="mob" [mode]="mode">
|
||||
</md2-mob-detail-info>
|
||||
<ng-container *ngIf="mode==MobDlgType.Spawn&&!mob.isRoamingMonster">
|
||||
<div class="row form-group mt-2">
|
||||
<div class="col-md-2">
|
||||
<span class="MD2Icon melee g-font-size-40"></span>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<span class="MD2Icon dice Yellow g-font-size-35"></span>
|
||||
<adj-number-input [(ngModel)]="mob.attackInfos[0].yellow" minimum="0" maximum="3">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<span class="MD2Icon dice Orange g-font-size-35"></span>
|
||||
<adj-number-input [(ngModel)]="mob.attackInfos[0].orange" minimum="0" maximum="3">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row form-group">
|
||||
<div class="col-md-2">
|
||||
<span class="MD2Icon range g-font-size-40"></span>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<span class="MD2Icon dice Yellow g-font-size-35"></span>
|
||||
<adj-number-input [(ngModel)]="mob.attackInfos[1].yellow" minimum="0" maximum="3">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<span class="MD2Icon dice Orange g-font-size-35"></span>
|
||||
<adj-number-input [(ngModel)]="mob.attackInfos[1].orange" minimum="0" maximum="3">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row form-group">
|
||||
<div class="col-md-2">
|
||||
<span class="MD2Icon magic g-font-size-40"></span>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<span class="MD2Icon dice Yellow g-font-size-35"></span>
|
||||
<adj-number-input [(ngModel)]="mob.attackInfos[2].yellow" minimum="0" maximum="3">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<span class="MD2Icon dice Orange g-font-size-35"></span>
|
||||
<adj-number-input [(ngModel)]="mob.attackInfos[2].orange" minimum="0" maximum="3">
|
||||
</adj-number-input>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="mode==MobDlgType.BeenAttacked">
|
||||
<!-- <div class='form-group mt-5'>
|
||||
<label for='damages' class='label'>Attacked By</label>
|
||||
<md2-hero-select name="attackedBy" (blur)="mob.uiAttackedBy=$event.playerInfo.tabId">
|
||||
</md2-hero-select>
|
||||
</div> -->
|
||||
<div class='form-group mt-5'>
|
||||
<label for='damages' class='label'>Cause How Many Damages?</label>
|
||||
<input type='number' nbInput fullWidth id='damages' name='damages' [(ngModel)]='mob.uiWounds'>
|
||||
</div>
|
||||
<div class='form-group'>
|
||||
<label for='damages' class='label'>Cause How Many <span
|
||||
[innerHtml]="iconHtml(MD2Icon.Fire,'g-color-google-plus mr-1 g-font-size-18')"></span>?</label>
|
||||
<input type='number' nbInput fullWidth id='uiFireTokens' name='uiFireTokens'
|
||||
[(ngModel)]='mob.uiFireTokens'>
|
||||
</div>
|
||||
<div class='form-group'>
|
||||
<label for='damages' class='label'>Cause How Many <span
|
||||
[innerHtml]="iconHtml(MD2Icon.Frost,'g-color-aqua mr-1 g-font-size-18')"></span>?</label>
|
||||
<input type='number' nbInput fullWidth id='uiFrozenTokens' name='uiFrozenTokens'
|
||||
[(ngModel)]='mob.uiFrozenTokens'>
|
||||
</div>
|
||||
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</nb-card-body>
|
||||
<nb-card-footer>
|
||||
<span [innerHtml]="attackTarget" *ngIf="attackTarget"></span>
|
||||
<button class="float-right" nbButton hero status="warning" size="small" (click)="cancel()"
|
||||
*ngIf="mode==MobDlgType.BeenAttacked">Cancel</button>
|
||||
<button class="float-right mr-2" nbButton hero status="primary" size="small"
|
||||
(click)="submit()">{{submitBtnText}}</button>
|
||||
</nb-card-footer>
|
||||
</nb-card>
|
||||
@ -0,0 +1,4 @@
|
||||
.mobCard {
|
||||
width: 885px;
|
||||
height: 95vh !important;
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SpawnMobDlgComponent } from './spawn-mob-dlg.component';
|
||||
|
||||
describe('SpawnMobDlgComponent', () => {
|
||||
let component: SpawnMobDlgComponent;
|
||||
let fixture: ComponentFixture<SpawnMobDlgComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SpawnMobDlgComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SpawnMobDlgComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,184 @@
|
||||
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NbDialogRef } from '@nebular/theme';
|
||||
import { FileService } from '../../../../services/file.service';
|
||||
import { MD2Service } from '../../../../services/md2.service';
|
||||
import { MsgBoxService } from '../../../../services/msg-box.service';
|
||||
import { StateService } from '../../../../services/state.service';
|
||||
import { StringUtils } from '../../../../utilities/string-utils';
|
||||
import { AttackInfo, AttackTarget, AttackType, MD2HeroInfo, MD2Icon, MobDlgType, MobInfo } from '../../massive-darkness2.model';
|
||||
import { MD2ComponentBase } from '../../MD2Base';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-spawn-mob-dlg',
|
||||
templateUrl: './spawn-mob-dlg.component.html',
|
||||
styleUrls: ['./spawn-mob-dlg.component.scss']
|
||||
})
|
||||
export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit {
|
||||
MobDlgType = MobDlgType;
|
||||
mode: MobDlgType;
|
||||
|
||||
title: string;
|
||||
titleHtml: string;
|
||||
MD2Icon = MD2Icon;
|
||||
mob: MobInfo;
|
||||
|
||||
beenAttackedHero = [] as MD2HeroInfo[];
|
||||
attackTarget: string;
|
||||
otherAttackTarget: string;
|
||||
constructor(
|
||||
private dlgRef: NbDialogRef<SpawnMobDlgComponent>,
|
||||
private msgBoxService: MsgBoxService,
|
||||
public md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
super(md2Service, stateService, route, cdRef);
|
||||
}
|
||||
ngOnInit(): void {
|
||||
//this.mob = new MobInfo(this.mob);
|
||||
if (this.mode == MobDlgType.Spawn) {
|
||||
this.mob.attackInfos = [
|
||||
{
|
||||
type: MD2Icon.Melee,
|
||||
red: 0,
|
||||
yellow: 0,
|
||||
orange: 0
|
||||
} as AttackInfo,
|
||||
{
|
||||
type: MD2Icon.Range,
|
||||
red: 0,
|
||||
yellow: 0,
|
||||
orange: 0
|
||||
} as AttackInfo,
|
||||
{
|
||||
type: MD2Icon.Magic,
|
||||
red: 0,
|
||||
yellow: 0,
|
||||
orange: 0
|
||||
} as AttackInfo
|
||||
|
||||
]
|
||||
}
|
||||
this.mob.uiWounds = 0;
|
||||
this.mob.uiFrozenTokens = 0;
|
||||
this.mob.uiFrozenTokens = 0;
|
||||
this.initTitleHtml();
|
||||
}
|
||||
submit() {
|
||||
if (this.mode == MobDlgType.Spawn) {
|
||||
|
||||
this.mob.attackInfos = this.mob.attackInfos.filter(a => a.yellow > 0 || a.orange > 0 || a.red > 0);
|
||||
}
|
||||
// switch (this.mode) {
|
||||
// case MobDlgType.Spawn:
|
||||
// return 'warning';
|
||||
// break;
|
||||
// case MobDlgType.Activating:
|
||||
// return 'danger';
|
||||
// break;
|
||||
// case MobDlgType.BeenAttacked:
|
||||
// return 'info';
|
||||
// break;
|
||||
// case MobDlgType.PreView:
|
||||
// default:
|
||||
// return '';
|
||||
// break;
|
||||
// }
|
||||
this.md2Service.info.showAttackBtn = false;
|
||||
this.dlgRef.close(this.mob);
|
||||
}
|
||||
cancel() {
|
||||
this.dlgRef.close();
|
||||
}
|
||||
initTitleHtml() {
|
||||
let htmlText = '';
|
||||
if (this.mode == MobDlgType.Spawn) {
|
||||
htmlText = `${this.mob.description} Shows Up`;
|
||||
} else if (this.mode == MobDlgType.Activating) {
|
||||
let targetType = null as AttackTarget;
|
||||
let randomAttack = Math.random() * AttackTarget.LowestLevel;
|
||||
const values = Object.values(AttackTarget);
|
||||
|
||||
const stringKeys = Object
|
||||
.keys(AttackTarget)
|
||||
.filter((v) => isNaN(Number(v)))
|
||||
for (let i = 0; i < stringKeys.length; i++) {
|
||||
const element = AttackTarget[stringKeys[i]];
|
||||
|
||||
if (element >= randomAttack) {
|
||||
targetType = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (targetType) {
|
||||
case AttackTarget.LowestHp:
|
||||
let lowestHp = Math.min(...this.md2Service.heros.map(h => h.hp));
|
||||
this.beenAttackedHero = this.md2Service.heros.filter(h => h.hp == lowestHp);
|
||||
//this.otherAttackTarget = 'attacking the other <b>Lowest HP</b> hero.';
|
||||
break;
|
||||
case AttackTarget.HighestHp:
|
||||
let highestHp = Math.max(...this.md2Service.heros.map(h => h.hp));
|
||||
this.beenAttackedHero = this.md2Service.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.md2Service.heros.map(h => h.mp));
|
||||
this.beenAttackedHero = this.md2Service.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.md2Service.heros.map(h => h.level));
|
||||
this.beenAttackedHero = this.md2Service.heros.filter(h => h.level == lowestLevel);
|
||||
//this.otherAttackTarget = 'attacking the other <b>Lowest Level</b> hero.';
|
||||
break;
|
||||
case AttackTarget.Random:
|
||||
default:
|
||||
this.beenAttackedHero = [this.md2Service.heros[Math.round(Math.random() * (this.md2Service.heros.length - 1))]];
|
||||
//this.otherAttackTarget = 'Just act like normal.';
|
||||
break;
|
||||
}
|
||||
let attackedHeros = StringUtils.makeCommaSeparatedString(this.beenAttackedHero.map(h => this.md2Service.heroFullName(h)), true, true);
|
||||
this.attackTarget = `Attacking <b>${attackedHeros}</b> first if possible.`;
|
||||
htmlText = `${this.title}`;
|
||||
} else {
|
||||
htmlText = `${this.mob.description}`;
|
||||
}
|
||||
this.titleHtml = htmlText;
|
||||
}
|
||||
|
||||
|
||||
public get headerStatus(): string {
|
||||
switch (this.mode) {
|
||||
case MobDlgType.Spawn:
|
||||
return 'warning';
|
||||
break;
|
||||
case MobDlgType.Activating:
|
||||
return 'danger';
|
||||
break;
|
||||
case MobDlgType.BeenAttacked:
|
||||
return 'info';
|
||||
break;
|
||||
case MobDlgType.PreView:
|
||||
default:
|
||||
return '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public get submitBtnText(): string {
|
||||
switch (this.mode) {
|
||||
case MobDlgType.Activating:
|
||||
return 'Next';
|
||||
break;
|
||||
case MobDlgType.BeenAttacked:
|
||||
return 'Attack!!';
|
||||
break;
|
||||
case MobDlgType.PreView:
|
||||
case MobDlgType.Spawn:
|
||||
default:
|
||||
return 'Close';
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
<nb-card>
|
||||
<nb-card-header>
|
||||
<img src="{{imgUrl('TreasureToken/Cover.png')}}" width="40px"> Treasure Bag
|
||||
<!-- <button nbButton hero status="warning" size="tiny" (click)="resetTreasureBag()"
|
||||
class="float-right">Reset</button> -->
|
||||
<button nbButton hero status="primary" size="tiny" (click)="md2Service.addTreasure(TreasureType.Epic)"
|
||||
class="float-right mr-1">Add
|
||||
Epic</button>
|
||||
<button nbButton hero status="info" size="tiny" (click)="md2Service.addTreasure(TreasureType.Rare)"
|
||||
class="float-right mr-1">Add
|
||||
Rare</button>
|
||||
<button nbButton hero status="success" size="tiny" (click)="md2Service.addTreasure(TreasureType.Common)"
|
||||
class="float-right mr-1">Add
|
||||
Common</button>
|
||||
</nb-card-header>
|
||||
<nb-card-body>
|
||||
<!-- <b>Content of the bag</b>
|
||||
<hr class="my-1"> -->
|
||||
<div class="row">
|
||||
<div class="col" *ngFor="let treasure of treasureBag.drawingItems">
|
||||
<img src="{{treasure.imageUrl}}" width="40px"> X {{treasure.drawingWeight}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<button nbButton hero fullWidth="" status="primary" class="mt-3" (click)="drawAndRemoveTreasure()">I feel
|
||||
LUCKY!!!!!</button>
|
||||
</nb-card-body>
|
||||
</nb-card>
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TreasureBagComponent } from './treasure-bag.component';
|
||||
|
||||
describe('TreasureBagComponent', () => {
|
||||
let component: TreasureBagComponent;
|
||||
let fixture: ComponentFixture<TreasureBagComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ TreasureBagComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TreasureBagComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,99 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Subject } from 'rxjs-compat';
|
||||
import { first, takeUntil } from 'rxjs/operators';
|
||||
import { FileService } from '../../../services/file.service';
|
||||
import { MD2Service } from '../../../services/md2.service';
|
||||
import { MsgBoxService } from '../../../services/msg-box.service';
|
||||
import { StateService } from '../../../services/state.service';
|
||||
import { ADButtons, ADIcon } from '../../../ui/alert-dlg/alert-dlg.model';
|
||||
import { TreasureType, DrawingBag, DrawingItem } from '../massive-darkness2.model';
|
||||
import { MD2Base, MD2ComponentBase } from '../MD2Base';
|
||||
|
||||
@Component({
|
||||
selector: 'md2-treasure-bag',
|
||||
templateUrl: './treasure-bag.component.html',
|
||||
styleUrls: ['./treasure-bag.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class TreasureBagComponent extends MD2ComponentBase implements OnInit {
|
||||
|
||||
TreasureType = TreasureType;
|
||||
drawingAmount = 1;
|
||||
constructor(
|
||||
private fileService: FileService,
|
||||
private msgBoxService: MsgBoxService,
|
||||
public md2Service: MD2Service,
|
||||
protected stateService: StateService,
|
||||
protected route: ActivatedRoute,
|
||||
protected cdRef: ChangeDetectorRef,
|
||||
) {
|
||||
super(md2Service, stateService, route, cdRef);
|
||||
|
||||
this.md2Service.mobBeenKilledSubject.pipe(takeUntil(this.destroy$)).subscribe(mob => {
|
||||
//this.drawingTreasure(mob.rewardTokens);
|
||||
for (let i = 0; i < mob.carriedTreasure.length; i++) {
|
||||
const carriedTreasure = mob.carriedTreasure[i];
|
||||
carriedTreasure.drawingWeight = 1;
|
||||
this.md2Service.treasureBag.AddItem(carriedTreasure);
|
||||
}
|
||||
this.detectChanges();
|
||||
});
|
||||
this.md2Service.refreshTreasureBagSubject.pipe(takeUntil(this.destroy$)).subscribe(treasureBag => {
|
||||
//this.drawingTreasure(mob.rewardTokens);
|
||||
this.detectChanges();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.md2Service.initTreasureBag();
|
||||
this.detectChanges();
|
||||
}
|
||||
detectChanges() {
|
||||
|
||||
if (!this.cdRef['destroyed']) {
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
}
|
||||
resetTreasureBag(showConfirmMsg = true) {
|
||||
this.msgBoxService.show("Restore Treasure Bag", { text: 'Are you sure?', icon: ADIcon.QUESTION, buttons: ADButtons.YesNo }).pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
this.md2Service.initTreasureBag();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
treasureImage(type: TreasureType) {
|
||||
return this.imgUrl(`TreasureToken/${TreasureType[type]}.png`);
|
||||
}
|
||||
drawTreasure() {
|
||||
this.msgBoxService.showInputbox("Drawing", "drawing how many treasure?", { inputType: 'number', inputValue: this.drawingAmount.toString() })
|
||||
.pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
this.drawingTreasure(result, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
drawAndRemoveTreasure() {
|
||||
this.msgBoxService.showInputbox("Draw And Remove", "drawing how many treasure?", { inputType: 'number', inputValue: this.drawingAmount.toString() })
|
||||
.pipe(first()).subscribe(result => {
|
||||
if (result) {
|
||||
this.drawingTreasure(result, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
drawingTreasure(amount: number, drawAndRemove: boolean) {
|
||||
this.drawingAmount = amount;
|
||||
let drawItems = drawAndRemove ? this.md2Service.treasureBag.DrawAndRemove(this.drawingAmount) : this.md2Service.treasureBag.Draw(amount);
|
||||
|
||||
let resultHtmlText = drawItems.map(i => `<img src="${i.imageUrl}" class='mr-1' width="80px">`).join();
|
||||
|
||||
this.msgBoxService.show("You Got", { text: resultHtmlText });
|
||||
this.detectChanges();
|
||||
}
|
||||
public get treasureBag(): DrawingBag<DrawingItem> {
|
||||
return this.md2Service.treasureBag;
|
||||
}
|
||||
|
||||
}
|
||||
@ -3,6 +3,7 @@ import { NbDialogRef } from '@nebular/theme';
|
||||
import { DropDownOption } from '../../../entity/dropDownOption';
|
||||
import { HappinessCost } from '../../../entity/happiness-cost.model';
|
||||
import { AuthService } from '../../../services/auth.service';
|
||||
import { LoginUserService } from '../../../services/login-user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ngx-add-new-cost-dlg',
|
||||
@ -13,13 +14,13 @@ export class AddNewCostDlgComponent implements OnInit {
|
||||
taskrOptions: DropDownOption[] = [];
|
||||
task: HappinessCost;
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private loginUserService: LoginUserService,
|
||||
private dlgRef: NbDialogRef<AddNewCostDlgComponent>
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.task = new HappinessCost();
|
||||
this.task.tasker = this.authService.userAccess.firstName;
|
||||
this.task.tasker = this.loginUserService.userAccess.firstName;
|
||||
}
|
||||
close() {
|
||||
this.dlgRef.close();
|
||||
|
||||
@ -7,6 +7,7 @@ import { map, mergeMap, first } from 'rxjs/operators';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { GoogleUserInfo, LoginTokenViewModel, RegisterViewModel } from '../entity/Auth';
|
||||
import { PastoralDomainService } from './crudServices/pastoral-domain.service';
|
||||
import { LoginUserService } from './login-user.service';
|
||||
import { SessionService } from './session.service';
|
||||
|
||||
@Injectable({
|
||||
@ -14,7 +15,6 @@ import { SessionService } from './session.service';
|
||||
})
|
||||
export class AuthService {
|
||||
|
||||
userAccess: LoginTokenViewModel
|
||||
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
@ -22,11 +22,12 @@ export class AuthService {
|
||||
private oAuthService: NbAuthService,
|
||||
protected router: Router,
|
||||
private pastoralDomainService: PastoralDomainService,
|
||||
private loginUserService: LoginUserService
|
||||
) { }
|
||||
|
||||
logout() {
|
||||
this.sessionService.loginUserToken = '';
|
||||
this.userAccess = {} as LoginTokenViewModel;
|
||||
this.loginUserService.userAccess = {} as LoginTokenViewModel;
|
||||
this.pastoralDomainService.domains = [];
|
||||
this.oAuthService.getToken().pipe(first()).subscribe(result => {
|
||||
console.log('logoutToken', result);
|
||||
@ -42,9 +43,9 @@ export class AuthService {
|
||||
}
|
||||
|
||||
isAuthenticated(): Observable<LoginTokenViewModel> {
|
||||
if (this.userAccess) {
|
||||
if (this.userAccess.token && this.userAccess.tokenExpireTime > new Date()) {
|
||||
return Observable.of(this.userAccess);
|
||||
if (this.loginUserService.userAccess) {
|
||||
if (this.loginUserService.userAccess.token && this.loginUserService.userAccess.tokenExpireTime > new Date()) {
|
||||
return Observable.of(this.loginUserService.userAccess);
|
||||
}
|
||||
} else if (this.sessionService.loginUserToken) {
|
||||
return this.loginWithToken(this.sessionService.loginUserToken).pipe(map(result => {
|
||||
@ -159,11 +160,11 @@ export class AuthService {
|
||||
}
|
||||
).pipe(map(result => {
|
||||
if (result && result.token) {
|
||||
this.userAccess = result;
|
||||
this.loginUserService.userAccess = result;
|
||||
this.sessionService.loginUserToken = result.token;
|
||||
this.sessionService.loginUserId = result.memberId;
|
||||
} else {
|
||||
this.userAccess = {} as LoginTokenViewModel;
|
||||
this.loginUserService.userAccess = {} as LoginTokenViewModel;
|
||||
}
|
||||
return result;
|
||||
}));
|
||||
|
||||
@ -5,6 +5,7 @@ import { map, mergeMap } from 'rxjs/operators';
|
||||
import { DomainMemberRelationship, DomainType, PastoralDomain } from '../../entity/PastoralDomain';
|
||||
import { AuthService } from '../auth.service';
|
||||
import { SessionService } from '../session.service';
|
||||
import { SignalRService } from '../signal-r.service';
|
||||
import { CombinedKeyCrudService, CrudService } from './crud.service';
|
||||
import { DomainMemberShipService } from './domain-member-ship.service';
|
||||
|
||||
@ -23,7 +24,8 @@ export class PastoralDomainService extends CrudService<PastoralDomain> {
|
||||
constructor(
|
||||
protected http: HttpClient,
|
||||
private domainMemberShipService: DomainMemberShipService,
|
||||
private session: SessionService
|
||||
private session: SessionService,
|
||||
private signalRService: SignalRService
|
||||
) {
|
||||
super(http, BASE_URL);
|
||||
}
|
||||
@ -34,6 +36,7 @@ export class PastoralDomainService extends CrudService<PastoralDomain> {
|
||||
|
||||
initialize() {
|
||||
return this.getCurrentUserPastoralDomain().pipe(map(result => {
|
||||
//this.signalRService.startSignalRConnection();
|
||||
this.domains = result;
|
||||
this.initialized.next(true);
|
||||
return result;
|
||||
|
||||
@ -1,15 +1,25 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { environment } from '../../environments/environment';
|
||||
const FilePath_URL = (id: string = null) => { return `${environment.apiUrl}/Files?filePath=${(id ? `/${id}` : '')}` }
|
||||
const FilePath_URL = (id: string = null) => { return `${environment.apiUrl}/Files/${(id ? `${id}` : '')}` }
|
||||
const FileList_URL = (id: string = null) => { return `${environment.apiUrl}/FileList/${(id ? `${id}` : '')}` }
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FileService {
|
||||
|
||||
constructor() { }
|
||||
constructor(
|
||||
private http: HttpClient
|
||||
) { }
|
||||
|
||||
|
||||
ImageUrl(imagePath: string) {
|
||||
return FilePath_URL(imagePath);
|
||||
return FilePath_URL('Images/' + encodeURI(imagePath));
|
||||
}
|
||||
FileList(folderPath: string) {
|
||||
return this.http.get<string[]>(FileList_URL(folderPath)).pipe(map(response => {
|
||||
return response.filter(s => s.indexOf('Thumbs.db') == -1).map(f => f.replace("/", ""));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
16
src/app/services/game-room.service.spec.ts
Normal file
16
src/app/services/game-room.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { GameRoomService } from './game-room.service';
|
||||
|
||||
describe('GameRoomService', () => {
|
||||
let service: GameRoomService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(GameRoomService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
69
src/app/services/game-room.service.ts
Normal file
69
src/app/services/game-room.service.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { NbMenuItem } from '@nebular/theme';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { IGamePlayer } from '../games/games.model';
|
||||
import { LoginUserService } from './login-user.service';
|
||||
import { SignalRMessage, SignalRService } from './signal-r.service';
|
||||
const GAME_ROOM_URL = `${environment.apiUrl}/api/GameRoom`
|
||||
const GAME_ROOM_Message_URL = `${environment.apiUrl}/api/GameRoomMessage`
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class GameRoomService {
|
||||
gameRoomId: string;
|
||||
sideMenu: NbMenuItem[] = [];
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private loginUserService: LoginUserService,
|
||||
private signalRService: SignalRService
|
||||
) { }
|
||||
currentPlayer() {
|
||||
return {
|
||||
name: this.loginUserService.userAccess.firstName,
|
||||
id: this.loginUserService.userAccess.memberId,
|
||||
signalRClientId: this.loginUserService.userAccess.signalRSessionId,
|
||||
isPlayer: true,
|
||||
gameRoomId: this.gameRoomId,
|
||||
tabId: this.loginUserService.sessionTabId
|
||||
} as IGamePlayer;
|
||||
}
|
||||
createGameRoom(name: string) {
|
||||
let gameRoom = this.currentPlayer();
|
||||
gameRoom.gameRoomId = gameRoom.id;
|
||||
this.gameRoomId = gameRoom.id;
|
||||
this.http.post<boolean>(GAME_ROOM_URL,
|
||||
JSON.stringify(gameRoom), {
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).pipe(first()).subscribe(result => {
|
||||
this.signalRService.startSignalRConnection(this.gameRoomId);
|
||||
});
|
||||
}
|
||||
|
||||
joinGameRoom(roomId: string) {
|
||||
this.http.put<boolean>(GAME_ROOM_URL + '\\' + roomId,
|
||||
JSON.stringify(this.currentPlayer()), {
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).pipe(first()).subscribe(result => {
|
||||
this.gameRoomId = roomId;
|
||||
this.signalRService.startSignalRConnection(this.gameRoomId);
|
||||
});
|
||||
}
|
||||
sendMessage(data: SignalRMessage) {
|
||||
return this.http.post<boolean>(GAME_ROOM_Message_URL,
|
||||
JSON.stringify(data), {
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
16
src/app/services/login-user.service.spec.ts
Normal file
16
src/app/services/login-user.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoginUserService } from './login-user.service';
|
||||
|
||||
describe('LoginUserService', () => {
|
||||
let service: LoginUserService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(LoginUserService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
27
src/app/services/login-user.service.ts
Normal file
27
src/app/services/login-user.service.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Subject } from 'rxjs';
|
||||
import { LoginTokenViewModel } from '../entity/Auth';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class LoginUserService {
|
||||
|
||||
userAccess: LoginTokenViewModel;
|
||||
signalRInitialized = new BehaviorSubject<string>(null);
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
setSignalRConnectionId(id: string) {
|
||||
this.userAccess.signalRSessionId = id;
|
||||
}
|
||||
|
||||
public get sessionTabId(): string {
|
||||
return sessionStorage.getItem('tabId');
|
||||
}
|
||||
public set sessionTabId(v: string) {
|
||||
sessionStorage.setItem('tabId', v);
|
||||
}
|
||||
|
||||
}
|
||||
16
src/app/services/md2.service.spec.ts
Normal file
16
src/app/services/md2.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MD2Service } from './md2.service';
|
||||
|
||||
describe('MD2Service', () => {
|
||||
let service: MD2Service;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(MD2Service);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
589
src/app/services/md2.service.ts
Normal file
589
src/app/services/md2.service.ts
Normal file
@ -0,0 +1,589 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AttackInfo, AttackTarget, CoreGameDarknessPhaseRule, DefenseInfo, DrawingBag, DrawingItem, HeroClass, IDarknessPhaseRule, MD2HeroInfo, MD2Icon, MD2Rules, MobInfo, RoundPhase, TreasureType } from '../games/massive-darkness2/massive-darkness2.model';
|
||||
import { first, map, reduce } from "rxjs/operators";
|
||||
import { Subject } from 'rxjs';
|
||||
import { FileService } from './file.service';
|
||||
import { StringUtils } from '../utilities/string-utils';
|
||||
import { MsgBoxService } from './msg-box.service';
|
||||
import { ADButtons, ADIcon, MessageBoxConfig } from '../ui/alert-dlg/alert-dlg.model';
|
||||
import { GameRoomService } from './game-room.service';
|
||||
import { SignalRMessage, SignalRService, SignalRSession } from './signal-r.service';
|
||||
import { LoginUserService } from './login-user.service';
|
||||
import { NumberUtils } from '../utilities/number-utils';
|
||||
import { SpawnMobDlgComponent } from '../games/massive-darkness2/mobs/spawn-mob-dlg/spawn-mob-dlg.component';
|
||||
import { stringify } from 'querystring';
|
||||
import { NbDialogService } from '@nebular/theme';
|
||||
import { IBossFight } from '../games/massive-darkness2/massive-darkness2.model.boss';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MD2Service {
|
||||
// #region Properties (24)
|
||||
|
||||
private _highestPlayerLevel: number = 1;
|
||||
private _playerAmount: number = 2;
|
||||
|
||||
public info: MD2GameInfo;
|
||||
|
||||
public darknessPhaseRule: IDarknessPhaseRule;
|
||||
public enemyPhaseMobs: MobInfo[];
|
||||
public enemyPhaseSubject = new Subject<MobInfo>();
|
||||
public initialized = false;
|
||||
public mobBeenKilledSubject = new Subject<MobInfo>();
|
||||
public mobDeck: DrawingBag<MobInfo>;
|
||||
public roamingMobDeck: DrawingBag<MobInfo>;
|
||||
public treasureBag: DrawingBag<DrawingItem> = new DrawingBag<DrawingItem>();
|
||||
|
||||
public refreshUI$: Subject<void> = new Subject<void>();
|
||||
public refreshTreasureBagSubject = new Subject<DrawingBag<DrawingItem>>();
|
||||
|
||||
|
||||
public get heros() {
|
||||
return this.info.heros;
|
||||
}
|
||||
public get roamingMonsters() {
|
||||
return this.info.roamingMonsters;
|
||||
}
|
||||
public get mobs() {
|
||||
return this.info.mobs;
|
||||
}
|
||||
|
||||
// #endregion Properties (24)
|
||||
|
||||
// #region Constructors (1)
|
||||
|
||||
constructor(
|
||||
public fileService: FileService,
|
||||
public msgBoxService: MsgBoxService,
|
||||
private gameRoomService: GameRoomService,
|
||||
private loginUserService: LoginUserService,
|
||||
public signalRService: SignalRService,
|
||||
public dlgService: NbDialogService
|
||||
) {
|
||||
this.darknessPhaseRule = new CoreGameDarknessPhaseRule();
|
||||
this.info = new MD2GameInfo();
|
||||
this.darknessPhaseRule.addTreasureToken.subscribe(treasureType => {
|
||||
this.addTreasure(treasureType, 1);
|
||||
});
|
||||
}
|
||||
|
||||
// #endregion Constructors (1)
|
||||
private initCoreGameRoamingMonsters() {
|
||||
let mobs = [];
|
||||
this.roamingMobDeck.AddItem(new MobInfo({ name: 'Andra', hp: 5, level: 1, rewardTokens: 2, isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Rare)] }));
|
||||
this.roamingMobDeck.AddItem(new MobInfo({ name: 'Andra', hp: 7, level: 3, rewardTokens: 2, isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic)] }));
|
||||
this.roamingMobDeck.AddItem(new MobInfo({ name: 'Andra', hp: 5, level: 5, rewardTokens: 0, isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic)] }));
|
||||
|
||||
this.roamingMobDeck.AddItem(new MobInfo({
|
||||
name: 'Ytheria, Undead Queen', hp: 4, level: 1, rewardTokens: 2,
|
||||
attackInfos: [new AttackInfo(MD2Icon.Melee, 1), new AttackInfo(MD2Icon.Rage, 2, 0, 0, 1)],
|
||||
defenseInfos: new DefenseInfo(1, 1),
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Rare)]
|
||||
}));
|
||||
this.roamingMobDeck.AddItem(new MobInfo({
|
||||
name: 'Ytheria, Undead Queen', hp: 6, level: 3, rewardTokens: 2,
|
||||
attackInfos: [new AttackInfo(MD2Icon.Melee, 0, 1), new AttackInfo(MD2Icon.Rage, 1, 1, 0, 1)],
|
||||
defenseInfos: new DefenseInfo(2, 1),
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic)]
|
||||
}));
|
||||
this.roamingMobDeck.AddItem(new MobInfo({
|
||||
name: 'Ytheria, Undead Queen', hp: 8, level: 5, rewardTokens: 0,
|
||||
attackInfos: [new AttackInfo(MD2Icon.Melee, 2, 1), new AttackInfo(MD2Icon.Rage, 2, 1, 1, 1)],
|
||||
defenseInfos: new DefenseInfo(4, 1),
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic)]
|
||||
}));
|
||||
|
||||
this.roamingMobDeck.AddItem(new MobInfo({
|
||||
name: 'Lyidan, Incubus Lord', hp: 7, level: 1, rewardTokens: 2,
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Rare)]
|
||||
}));
|
||||
this.roamingMobDeck.AddItem(new MobInfo({
|
||||
name: 'Lyidan, Incubus Lord', hp: 10, level: 3, rewardTokens: 2,
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic)]
|
||||
}));
|
||||
this.roamingMobDeck.AddItem(new MobInfo({
|
||||
name: 'Lyidan, Incubus Lord', hp: 7, level: 5, rewardTokens: 0,
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic)]
|
||||
}));
|
||||
|
||||
this.roamingMobDeck.AddItem(new MobInfo({
|
||||
name: 'The Ghoul', hp: 5, level: 1, rewardTokens: 2,
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Rare)]
|
||||
}));
|
||||
this.roamingMobDeck.AddItem(new MobInfo({
|
||||
name: 'The Ghoul', hp: 8, level: 3, rewardTokens: 2,
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic)]
|
||||
}));
|
||||
this.roamingMobDeck.AddItem(new MobInfo({
|
||||
name: 'The Ghoul', hp: 5, level: 5, rewardTokens: 0,
|
||||
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic)]
|
||||
}));
|
||||
|
||||
|
||||
}
|
||||
// #region Public Getters And Setters (5)
|
||||
|
||||
public get highestPlayerLevel(): number {
|
||||
if (this.heros.length > 0) {
|
||||
return Math.max(...this.heros.map(h => h.level));
|
||||
} else {
|
||||
return this._highestPlayerLevel;
|
||||
}
|
||||
}
|
||||
|
||||
public set highestPlayerLevel(v: number) {
|
||||
this._highestPlayerLevel = v;
|
||||
}
|
||||
|
||||
public get playerAmount(): number {
|
||||
if (this.heros.length > 0) {
|
||||
return this.heros.length;
|
||||
} else {
|
||||
return this._playerAmount;
|
||||
}
|
||||
}
|
||||
|
||||
public set playerAmount(v: number) {
|
||||
this._playerAmount = v;
|
||||
}
|
||||
|
||||
public get playerHero(): MD2HeroInfo {
|
||||
return this.heros.find(h => h.playerInfo.signalRClientId == this.loginUserService.userAccess.signalRSessionId);
|
||||
}
|
||||
|
||||
// #endregion Public Getters And Setters (5)
|
||||
|
||||
// #region Public Methods (27)
|
||||
|
||||
public addTreasure(type: TreasureType, amount: number = 1) {
|
||||
let item = this.getTreasureDrawingItem(type, amount);
|
||||
this.treasureBag.AddItem(item);
|
||||
this.refreshTreasureBagSubject.next(this.treasureBag);
|
||||
}
|
||||
|
||||
public broadcastAllHeroInfoToAll() {
|
||||
this.heros.forEach(element => {
|
||||
this.broadcastHeroInfoToAll(element);
|
||||
});
|
||||
}
|
||||
|
||||
public broadcastHeroAction(action: string, extraValue: any = null) {
|
||||
let parameters = {};
|
||||
parameters['tabId'] = this.loginUserService.sessionTabId;
|
||||
parameters['extraValue'] = JSON.stringify(extraValue);
|
||||
this.broadcastMessage('heroAction', action, parameters);
|
||||
}
|
||||
|
||||
public broadcastHeroInfoToAll(hero: MD2HeroInfo) {
|
||||
let message = {
|
||||
receiver: { isGroup: true, sessionId: this.gameRoomService.gameRoomId } as SignalRSession,
|
||||
from: { isGroup: false, sessionId: hero.playerInfo.signalRClientId },
|
||||
actionType: 'hero',
|
||||
actionName: 'update',
|
||||
} as SignalRMessage;
|
||||
message.parameters = { hero: JSON.stringify(hero) };
|
||||
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
|
||||
});
|
||||
}
|
||||
|
||||
public broadcastHeroInfoToOwner(hero: MD2HeroInfo) {
|
||||
let message = {
|
||||
receiver: { isGroup: false, sessionId: hero.playerInfo.signalRClientId } as SignalRSession,
|
||||
from: { isGroup: true, sessionId: this.gameRoomService.gameRoomId },
|
||||
actionType: 'hero',
|
||||
actionName: 'update',
|
||||
} as SignalRMessage;
|
||||
message.parameters = { hero: JSON.stringify(hero) };
|
||||
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* `sessionId` = null means broadcast to all
|
||||
*/
|
||||
public broadcastMessage(actionType: string,
|
||||
actionName: string,
|
||||
parameters: { [key: string]: string; } = {},
|
||||
sessionId: string = null) {
|
||||
let message = {
|
||||
receiver: { isGroup: !sessionId, sessionId: sessionId } as SignalRSession,
|
||||
from: { isGroup: false, sessionId: this.loginUserService.userAccess.signalRSessionId },
|
||||
actionType: actionType,
|
||||
actionName: actionName,
|
||||
parameters: parameters
|
||||
} as SignalRMessage;
|
||||
|
||||
if (sessionId == null) {
|
||||
message.receiver = { isGroup: true, sessionId: this.gameRoomService.gameRoomId } as SignalRSession
|
||||
} else {
|
||||
message.receiver = { isGroup: false, sessionId: this.gameRoomService.gameRoomId } as SignalRSession
|
||||
}
|
||||
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
|
||||
});
|
||||
}
|
||||
public broadcastGameInfo() {
|
||||
let parameters = {};
|
||||
parameters['gameInfo'] = JSON.stringify(this.info);
|
||||
this.broadcastMessage('GameRoom', 'update', parameters);
|
||||
}
|
||||
public broadcastMobsInfo() {
|
||||
let parameters = {};
|
||||
parameters['roamingMonsters'] = JSON.stringify(this.roamingMonsters);
|
||||
parameters['mobs'] = JSON.stringify(this.mobs);
|
||||
this.broadcastMessage('mobs', 'update', parameters);
|
||||
}
|
||||
|
||||
public broadcastMyHeroInfo() {
|
||||
this.broadcastHeroInfoToAll(this.playerHero);
|
||||
}
|
||||
|
||||
public darknessPhase() {
|
||||
this.heros.forEach(hero => {
|
||||
hero.remainActions = 3;
|
||||
let remainFrozenToken = Math.max(0, hero.frozenToken - hero.remainActions);
|
||||
hero.remainActions = Math.max(0, hero.remainActions - hero.frozenToken);
|
||||
hero.frozenToken = remainFrozenToken;
|
||||
this.broadcastHeroInfoToOwner(hero);
|
||||
});
|
||||
this.info.roundPhase = RoundPhase.HeroPhase;
|
||||
this.info.round++;
|
||||
if (this.darknessPhaseRule.runDarknessPhase()) {
|
||||
this.msgBoxService.show(`${NumberUtils.Ordinal(this.info.round)} Hero Phase`, { icon: ADIcon.INFO });
|
||||
}
|
||||
//this.runNextPhase();
|
||||
}
|
||||
|
||||
public drawMob(isRoamingMonster: boolean) {
|
||||
let mobDeck = null as DrawingBag<MobInfo>;
|
||||
let level = 1;
|
||||
if (this.highestPlayerLevel < 3) {
|
||||
} else if (this.highestPlayerLevel < 5) {
|
||||
level = 3;
|
||||
} else {
|
||||
level = 5;
|
||||
}
|
||||
if (isRoamingMonster) {
|
||||
mobDeck = this.roamingMobDeck;
|
||||
} else {
|
||||
mobDeck = this.mobDeck;
|
||||
}
|
||||
|
||||
if (mobDeck.drawingItems.filter(m => (m as MobInfo).level == level)
|
||||
.map(d => d.drawingWeight).reduce((a, b) => a + b, 0) == 0) {
|
||||
mobDeck.RestoreRemoveItems();
|
||||
}
|
||||
|
||||
let newSpawnMob = new MobInfo(mobDeck.DrawAndRemove(1, m => m.level == level)[0]);
|
||||
if (isRoamingMonster) {
|
||||
newSpawnMob.unitRemainHp = newSpawnMob.hp * this.playerAmount;
|
||||
newSpawnMob.mobAmount = 1;
|
||||
} else {
|
||||
newSpawnMob.mobAmount = this.playerAmount + 1;
|
||||
}
|
||||
newSpawnMob.imageUrl = this.mobImage(newSpawnMob.name, newSpawnMob.level, isRoamingMonster);
|
||||
|
||||
let exitingMob = this.mobs.find(m => m.name == newSpawnMob.name);
|
||||
|
||||
if (exitingMob) {
|
||||
exitingMob.level = newSpawnMob.level;
|
||||
exitingMob.hp = newSpawnMob.hp;
|
||||
exitingMob.imageUrl = newSpawnMob.imageUrl;
|
||||
} else {
|
||||
this.mobs.push(newSpawnMob);
|
||||
newSpawnMob.carriedTreasure = this.treasureBag.DrawAndRemove(newSpawnMob.rewardTokens);
|
||||
}
|
||||
|
||||
|
||||
return { exitingMob, mob: newSpawnMob };
|
||||
}
|
||||
|
||||
public enemyPhase() {
|
||||
//this.msgBoxService
|
||||
let monsterBag = new DrawingBag<MobInfo>();
|
||||
monsterBag.drawingItems = this.roamingMonsters.concat(this.mobs);
|
||||
for (let i = 0; i < monsterBag.drawingItems.length; i++) {
|
||||
const element = monsterBag.drawingItems[i];
|
||||
element.drawingWeight = 1;
|
||||
}
|
||||
if (monsterBag.drawingItems.length > 0) {
|
||||
this.enemyPhaseMobs = monsterBag.DrawAndRemove(monsterBag.drawingItems.length);
|
||||
//this.showEnemyPhaseAction();
|
||||
this.enemyPhaseSubject.next(this.enemyPhaseMobs[0]);
|
||||
} else {
|
||||
this.runNextPhase();
|
||||
}
|
||||
}
|
||||
|
||||
public enterBossFight() {
|
||||
//this.sendMsgboxMsg
|
||||
}
|
||||
|
||||
public fileList(folderPath: string) {
|
||||
return this.fileService.FileList('Images/MD2/' + folderPath);
|
||||
}
|
||||
|
||||
public getTreasureDrawingItem(type: TreasureType, amount: number = 1) {
|
||||
return new DrawingItem(`${TreasureType[type]} Treasure`, `It's a ${TreasureType[type]} Treasure!`, this.treasureImage(type), amount);
|
||||
}
|
||||
|
||||
public heroFullName(hero: MD2HeroInfo) {
|
||||
if (!hero) return '';
|
||||
return `${hero.playerInfo.name} (${HeroClass[hero.class]} - ${hero.name})`
|
||||
}
|
||||
|
||||
public iconHtml(icon: MD2Icon, cssClass = '') {
|
||||
if (icon < MD2Icon.RedDice) {
|
||||
return `<span class='MD2Icon ${cssClass}'>${String.fromCharCode(65 + icon)}</span>`
|
||||
} else {
|
||||
return `<span class='MD2Icon dice ${MD2Icon[icon].replace('Dice', '')} ${cssClass}'></span>`;
|
||||
}
|
||||
}
|
||||
|
||||
public imgHtml(imgPath: string, cssClass = 'g-height-25 mr-1') {
|
||||
return `<img src='${this.imgUrl(imgPath)}' class='${cssClass}'>`;
|
||||
}
|
||||
|
||||
public imgUrl(imgPath: string) {
|
||||
return this.fileService.ImageUrl('MD2/' + imgPath);
|
||||
}
|
||||
|
||||
public initMobDecks() {
|
||||
this.mobDeck = new DrawingBag();
|
||||
this.roamingMobDeck = new DrawingBag();
|
||||
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Gargoyles', hp: 2, level: 1, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Demons', hp: 3, level: 1, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Undead', hp: 4, level: 1, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Fire Entities', hp: 3, level: 1, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Fallen Angels', hp: 2, level: 1, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Infernal Imps', hp: 3, level: 1, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Skeletons', hp: 2, level: 1, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Satyrs', hp: 3, level: 1, rewardTokens: 1 }));
|
||||
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Gargoyles', hp: 3, level: 3, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Demons', hp: 4, level: 3, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Undead', hp: 5, level: 3, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Fire Entities', hp: 4, level: 3, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Fallen Angels', hp: 3, level: 3, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Infernal Imps', hp: 4, level: 3, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Skeletons', hp: 3, level: 3, rewardTokens: 1 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Satyrs', hp: 4, level: 3, rewardTokens: 1 }));
|
||||
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Gargoyles', hp: 6, level: 5, rewardTokens: 2 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Demons', hp: 6, level: 5, rewardTokens: 2 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Undead', hp: 8, level: 5, rewardTokens: 2 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Fire Entities', hp: 7, level: 5, rewardTokens: 2 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Fallen Angels', hp: 5, level: 5, rewardTokens: 2 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Infernal Imps', hp: 5, level: 5, rewardTokens: 2 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Skeletons', hp: 5, level: 5, rewardTokens: 2 }));
|
||||
this.mobDeck.AddItem(new MobInfo({ name: 'Satyrs', hp: 6, level: 5, rewardTokens: 2 }));
|
||||
this.initCoreGameRoamingMonsters();
|
||||
}
|
||||
|
||||
public initTreasureBag() {
|
||||
this.treasureBag.ClearAllItems();
|
||||
this.addTreasure(TreasureType.Common, 15);
|
||||
this.addTreasure(TreasureType.Rare, 5);
|
||||
}
|
||||
|
||||
public levelUpPhase() {
|
||||
for (let i = 0; i < this.heros.length; i++) {
|
||||
const hero = this.heros[i];
|
||||
let levelUpInfo = MD2Rules.checkCoreGameLevelup(hero.level, hero.exp);
|
||||
while (levelUpInfo != null) {
|
||||
hero.level = levelUpInfo.level;
|
||||
hero.hp += levelUpInfo.extraHp;
|
||||
hero.hpMaximum += levelUpInfo.extraHp;
|
||||
hero.mp += levelUpInfo.extraMp;
|
||||
hero.mpMaximum += levelUpInfo.extraMp;
|
||||
hero.exp = levelUpInfo.currentExp;
|
||||
this.broadcastHeroInfoToOwner(hero);
|
||||
this.sendMsgboxMsg(hero.playerInfo.signalRClientId, { title: 'Level Up', text: 'Please do a skill level up!', icon: ADIcon.INFO });
|
||||
levelUpInfo = MD2Rules.checkCoreGameLevelup(hero.level, hero.exp);
|
||||
}
|
||||
}
|
||||
this.runNextPhase();
|
||||
}
|
||||
|
||||
public mobImage(name: string, level: number, isRoamingMonsters: boolean) {
|
||||
name = StringUtils.replaceAll(name, ' ', '').replace(',', '');
|
||||
if (level < 3) {
|
||||
level = 1;
|
||||
} else if (level < 5) {
|
||||
level = 2;
|
||||
} else {
|
||||
level = 3;
|
||||
}
|
||||
if (isRoamingMonsters) {
|
||||
return this.imgUrl(`Mobs/CoreGame/RoamingMonsters/${name}/${level}.png`);
|
||||
} else {
|
||||
return this.imgUrl(`Mobs/CoreGame/Mobs/${name}/${level}.png`);
|
||||
}
|
||||
}
|
||||
|
||||
public playerJoin(hero: MD2HeroInfo) {
|
||||
hero.playerInfo = this.gameRoomService.currentPlayer();
|
||||
hero.level = 1;
|
||||
hero.hp = hero.hpMaximum;
|
||||
hero.mp = hero.mpMaximum;
|
||||
hero.exp = 0;
|
||||
let message = {
|
||||
receiver: { isGroup: true, sessionId: this.gameRoomService.gameRoomId } as SignalRSession,
|
||||
from: { isGroup: false, sessionId: this.loginUserService.userAccess.signalRSessionId },
|
||||
actionType: 'hero',
|
||||
actionName: 'join',
|
||||
|
||||
} as SignalRMessage;
|
||||
message.parameters = { hero: JSON.stringify(hero) };
|
||||
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
|
||||
});
|
||||
}
|
||||
|
||||
public rollBlackDice(times: number) {
|
||||
let wounds = 0;
|
||||
let claws = 0;
|
||||
//miss 33%
|
||||
//1 claw 33%
|
||||
//1 wound 17%
|
||||
//1 claw, 1 wound 17%
|
||||
|
||||
for (let i = 0; i < times; i++) {
|
||||
let result = Math.random() * 100;
|
||||
if (result <= 33) {
|
||||
} else if (result <= 67) {
|
||||
claws += 1;
|
||||
} else if (result <= 83) {
|
||||
wounds += 1;
|
||||
} else {
|
||||
claws += 1;
|
||||
wounds += 1;
|
||||
}
|
||||
}
|
||||
return { claws, wounds };
|
||||
}
|
||||
|
||||
public runNextPhase() {
|
||||
this.info.roundPhase++;
|
||||
switch (this.info.roundPhase) {
|
||||
case RoundPhase.HeroPhase:
|
||||
this.heros.forEach(hero => {
|
||||
hero.remainActions = 3;
|
||||
let remainFrozenToken = Math.max(0, hero.frozenToken - hero.remainActions);
|
||||
hero.remainActions = Math.max(0, hero.remainActions - hero.frozenToken);
|
||||
hero.frozenToken = remainFrozenToken;
|
||||
this.broadcastHeroInfoToOwner(hero);
|
||||
});
|
||||
break;
|
||||
case RoundPhase.EnemyPhase:
|
||||
this.enemyPhase();
|
||||
break;
|
||||
case RoundPhase.LevelUpPhase:
|
||||
this.levelUpPhase();
|
||||
break;
|
||||
case RoundPhase.DarknessPhase:
|
||||
this.darknessPhase();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
let parameters = {};
|
||||
parameters['phase'] = this.info.roundPhase;
|
||||
this.broadcastMessage('roundPhase', '', parameters);
|
||||
}
|
||||
|
||||
public sendMsgboxMsg(playerSessionId: string, msg: Partial<MessageBoxConfig>) {
|
||||
let message = {
|
||||
receiver: { isGroup: false, sessionId: playerSessionId } as SignalRSession,
|
||||
from: { isGroup: false, sessionId: this.loginUserService.userAccess.signalRSessionId },
|
||||
actionType: 'message',
|
||||
actionName: 'popup',
|
||||
} as SignalRMessage;
|
||||
message.parameters = { msg: JSON.stringify(msg) };
|
||||
this.gameRoomService.sendMessage(message).pipe(first()).subscribe(result => {
|
||||
});
|
||||
}
|
||||
|
||||
public treasureImage(type: TreasureType) {
|
||||
return this.imgUrl(`TreasureToken/${TreasureType[type]}.png`);
|
||||
}
|
||||
public getTargetHerosByFilter(targetType: AttackTarget, onlyOne: boolean = false) {
|
||||
let beenAttackedHero = [] as MD2HeroInfo[];
|
||||
switch (targetType) {
|
||||
case AttackTarget.LowestHp:
|
||||
let lowestHp = Math.min(...this.heros.map(h => h.hp));
|
||||
beenAttackedHero = this.heros.filter(h => h.hp == lowestHp);
|
||||
//this.otherAttackTarget = 'attacking the other <b>Lowest HP</b> hero.';
|
||||
break;
|
||||
case AttackTarget.HighestHp:
|
||||
let highestHp = Math.max(...this.heros.map(h => h.hp));
|
||||
beenAttackedHero = this.heros.filter(h => h.hp == highestHp);
|
||||
//this.otherAttackTarget = 'attacking the other <b>Highest HP</b> hero.';
|
||||
break;
|
||||
case AttackTarget.HighestMp:
|
||||
let highestMp = Math.max(...this.heros.map(h => h.mp));
|
||||
beenAttackedHero = this.heros.filter(h => h.mp == highestMp);
|
||||
//this.otherAttackTarget = 'attacking the other <b>Highest Mp</b> hero.';
|
||||
break;
|
||||
case AttackTarget.LowestLevel:
|
||||
let lowestLevel = Math.max(...this.heros.map(h => h.level));
|
||||
beenAttackedHero = this.heros.filter(h => h.level == lowestLevel);
|
||||
//this.otherAttackTarget = 'attacking the other <b>Lowest Level</b> hero.';
|
||||
break;
|
||||
case AttackTarget.LeastCorruption:
|
||||
|
||||
let leastCor = Math.min(...this.heros.map(h => h.corruptionToken));
|
||||
beenAttackedHero = this.heros.filter(h => h.corruptionToken == leastCor);
|
||||
break;
|
||||
case AttackTarget.MostCorruption:
|
||||
let mostCor = Math.max(...this.heros.map(h => h.corruptionToken));
|
||||
beenAttackedHero = this.heros.filter(h => h.corruptionToken == mostCor);
|
||||
break;
|
||||
case AttackTarget.Random:
|
||||
default:
|
||||
beenAttackedHero = [this.heros[Math.round(Math.random() * (this.heros.length - 1))]];
|
||||
//this.otherAttackTarget = 'Just act like normal.';
|
||||
break;
|
||||
}
|
||||
if (onlyOne && beenAttackedHero.length > 1) {
|
||||
beenAttackedHero = [beenAttackedHero[Math.round(Math.random() * (beenAttackedHero.length - 1))]];
|
||||
}
|
||||
return beenAttackedHero;
|
||||
}
|
||||
|
||||
public getTargetHerosHtml(beenAttackedHero: MD2HeroInfo[]) {
|
||||
return `<b>${StringUtils.makeCommaSeparatedString(beenAttackedHero.map(h => this.heroFullName(h)), false, true)}</b>`;
|
||||
}
|
||||
// #endregion Public Methods (27)
|
||||
|
||||
}
|
||||
|
||||
export class MD2GameInfo {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor(
|
||||
config: Partial<MD2GameInfo> = undefined
|
||||
) {
|
||||
if (config) {
|
||||
Object.assign(this, config);
|
||||
|
||||
this.mobs = this.mobs.map(m => new MobInfo(m));
|
||||
this.roamingMonsters = this.roamingMonsters.map(m => new MobInfo(m));
|
||||
if (this.boss.info) {
|
||||
this.boss.info = new MobInfo(this.boss.info);
|
||||
}
|
||||
this.currentActivateHero = new MD2HeroInfo(this.currentActivateHero);
|
||||
this.heros = this.heros.map(h => new MD2HeroInfo(h));
|
||||
}
|
||||
|
||||
}
|
||||
public currentActivateHero: MD2HeroInfo;
|
||||
public isBossFight: boolean = false;
|
||||
public mobs: MobInfo[] = [];
|
||||
public roamingMonsters: MobInfo[] = [];
|
||||
public heros: MD2HeroInfo[] = [];
|
||||
public round = 1;
|
||||
public roundPhase: RoundPhase = RoundPhase.HeroPhase;
|
||||
public showAttackBtn: boolean = false;
|
||||
public boss: IBossFight;
|
||||
}
|
||||
@ -13,7 +13,7 @@ import { MessageBoxConfig, ADIcon, ADButtons } from '../ui/alert-dlg/alert-dlg.m
|
||||
export class MsgBoxService {
|
||||
|
||||
constructor(
|
||||
private dlgService: NbDialogService
|
||||
public dlgService: NbDialogService
|
||||
) { }
|
||||
|
||||
|
||||
@ -25,11 +25,14 @@ export class MsgBoxService {
|
||||
* @returns Ok,Yes:true, No,Close:false, Cancel:null
|
||||
*/
|
||||
show(title: string,
|
||||
config: Partial<MessageBoxConfig> = { icon: ADIcon.NONE, buttons: ADButtons.OK }): Observable<boolean> {
|
||||
config.title = title;
|
||||
config: Partial<MessageBoxConfig> = {}): Observable<boolean> {
|
||||
let defaultConfig = { buttons: ADButtons.OK } as Partial<MessageBoxConfig>;
|
||||
Object.assign(defaultConfig, config);
|
||||
defaultConfig.title = title;
|
||||
|
||||
return this.dlgService.open(AlertDlgComponent, {
|
||||
context: {
|
||||
config: new MessageBoxConfig(config)
|
||||
config: new MessageBoxConfig(defaultConfig)
|
||||
},
|
||||
closeOnBackdropClick: false,
|
||||
}).onClose.pipe(first())
|
||||
|
||||
16
src/app/services/qrcode.service.spec.ts
Normal file
16
src/app/services/qrcode.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { QRCodeService } from './qrcode.service';
|
||||
|
||||
describe('QRCodeService', () => {
|
||||
let service: QRCodeService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(QRCodeService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
14
src/app/services/qrcode.service.ts
Normal file
14
src/app/services/qrcode.service.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
const QRCode_URL = (content: string, size: number = 1) => { return `${environment.apiUrl}/api/QRCode?content=${encodeURIComponent(content)}&size=${size}` }
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class QRCodeService {
|
||||
|
||||
constructor() { }
|
||||
QRCodeUrl(content: string, size: number = 3) {
|
||||
return QRCode_URL(content, size);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,12 @@
|
||||
import { HttpParams } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router, UrlSerializer } from '@angular/router';
|
||||
import * as signalR from "@microsoft/signalr"
|
||||
import { Subject } from 'rxjs';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { UuidUtils } from '../utilities/uuid-utils';
|
||||
import { AuthService } from './auth.service';
|
||||
import { LoginUserService } from './login-user.service';
|
||||
|
||||
|
||||
const SIGNAL_R_URL = (id: string = null) => { return `${environment.apiUrl}/${id}` }
|
||||
@ -8,11 +14,16 @@ const SIGNAL_R_URL = (id: string = null) => { return `${environment.apiUrl}/${id
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class SignalRService {
|
||||
|
||||
ReceivedSignalRMessageSubject = new Subject<SignalRMessage>();
|
||||
signalRMessageConnSubject = new Subject<any>();
|
||||
private hubConnection: signalR.HubConnection
|
||||
constructor() { }
|
||||
constructor(
|
||||
private loginUserService: LoginUserService,
|
||||
private router: Router,
|
||||
private serializer: UrlSerializer
|
||||
) { }
|
||||
|
||||
public startConnection = () => {
|
||||
public startWhoIsSpyConnection = () => {
|
||||
this.hubConnection = new signalR.HubConnectionBuilder()
|
||||
.withUrl(SIGNAL_R_URL('WhoIsSpyHub'))
|
||||
.withAutomaticReconnect()
|
||||
@ -30,6 +41,75 @@ export class SignalRService {
|
||||
});
|
||||
|
||||
}
|
||||
public startSignalRConnection(gameRoomId: string = '') {
|
||||
this.loginUserService.sessionTabId = UuidUtils.generate();
|
||||
|
||||
const tree = this.router.createUrlTree([], {
|
||||
queryParams: {
|
||||
userId: this.loginUserService.userAccess.memberId,
|
||||
userName: this.loginUserService.userAccess.firstName,
|
||||
tabId: this.loginUserService.sessionTabId,
|
||||
roomId: gameRoomId
|
||||
}
|
||||
});
|
||||
const params = new HttpParams()
|
||||
.set('userId', this.loginUserService.userAccess.memberId)
|
||||
.set('userName', this.loginUserService.userAccess.firstName)
|
||||
.set('tabId', this.loginUserService.sessionTabId)
|
||||
.set('roomId', gameRoomId);
|
||||
|
||||
Object.defineProperty(WebSocket, 'OPEN', { value: 1, });
|
||||
this.hubConnection = new signalR.HubConnectionBuilder()
|
||||
.withUrl(`${SIGNAL_R_URL('GameRoomHub')}?${params.toString()}`)
|
||||
.withAutomaticReconnect()
|
||||
.build();
|
||||
let me = this
|
||||
this.hubConnection
|
||||
.start()
|
||||
.then(() => {
|
||||
me.setSignalRConnectionId(this.hubConnection.connectionId);
|
||||
})
|
||||
.catch(err => console.log('Error while starting connection: ' + err))
|
||||
this.hubConnection.on("ReceivedMessage", (jsonString) => {
|
||||
this.BroadcastAPIMessageReceive(JSON.parse(jsonString));
|
||||
});
|
||||
|
||||
}
|
||||
public setSignalRConnectionId(connectionId: string) {
|
||||
|
||||
this.loginUserService.userAccess.signalRSessionId = connectionId;
|
||||
this.loginUserService.signalRInitialized.next(connectionId);
|
||||
console.log('GameRoomHub start connection: ' + this.hubConnection.connectionId)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
BroadcastAPIMessageReceive(msg: SignalRMessage) {
|
||||
let jsonContent = null;
|
||||
if (msg.jsonValue) {
|
||||
try {
|
||||
msg.jsonValue = JSON.parse(msg.jsonValue);
|
||||
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
this.ReceivedSignalRMessageSubject.next(msg);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
export interface SignalRMessage {
|
||||
from: SignalRSession;
|
||||
receiver: SignalRSession;
|
||||
actionType: string;
|
||||
actionName: string;
|
||||
parameters: { [key: string]: string; };
|
||||
jsonValue: string;
|
||||
}
|
||||
|
||||
export interface SignalRSession {
|
||||
sessionId: string;
|
||||
name: string;
|
||||
isGroup: boolean;
|
||||
}
|
||||
@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
import { DomainType, PastoralDomain } from '../entity/PastoralDomain';
|
||||
import { AuthService } from './auth.service';
|
||||
import { HeaderService } from './header.service';
|
||||
import { LoginUserService } from './login-user.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -10,7 +11,8 @@ export class StateService {
|
||||
|
||||
constructor(
|
||||
private headerService: HeaderService,
|
||||
private authService: AuthService
|
||||
private authService: AuthService,
|
||||
public loginUserService: LoginUserService
|
||||
) { }
|
||||
|
||||
domains: PastoralDomain[] = [];
|
||||
@ -20,7 +22,7 @@ export class StateService {
|
||||
}
|
||||
|
||||
public get isCellGroupLeader(): boolean {
|
||||
return this.myCellGroup && this.myCellGroup.leaderMemberId == this.authService.userAccess.memberId;
|
||||
return this.myCellGroup && this.myCellGroup.leaderMemberId == this.loginUserService.userAccess.memberId;
|
||||
}
|
||||
|
||||
public get isAdministrator(): boolean {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<form (ngSubmit)="confirm()" #inputForm="ngForm">
|
||||
<nb-card class="alertCard">
|
||||
<nb-card class="alertCard {{config.cardWidthClass}} g-px-0 g-px-20--md">
|
||||
|
||||
<nb-card-body>
|
||||
<nb-card-body class="g-px-0 g-px-20--md">
|
||||
<div class="icon {{config.icon}}" *ngIf="config.icon" [ngSwitch]="config.icon">
|
||||
|
||||
<div class="icon-content" *ngSwitchCase="'question'">
|
||||
@ -36,6 +36,8 @@
|
||||
nbTooltip="{{config.inputTooltip}}" nbTooltipTrigger="noop" nbTooltipPlacement="top"
|
||||
nbTooltipStatus="danger">
|
||||
|
||||
<op-drop-down name='inputBox' id='msgInputBox' [(ngModel)]='config.inputValue'
|
||||
[source]='config.dropDownOptions' *ngSwitchCase="'dropdown'"></op-drop-down>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
|
||||
.alertCard {
|
||||
min-width: 280px;
|
||||
max-width: 600px;
|
||||
//max-width: 600px;
|
||||
padding: 0 1.25em;
|
||||
position: relative;
|
||||
/* Add animation */
|
||||
|
||||
@ -130,6 +130,9 @@ export class AlertDlgComponent implements OnInit {
|
||||
case 'number':
|
||||
this.dlgRef.close(this.config.inputValue);
|
||||
break;
|
||||
case 'dropdown':
|
||||
this.dlgRef.close(this.config.inputValue);
|
||||
break;
|
||||
|
||||
default:
|
||||
this.dlgRef.close(true);
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { DropDownOption } from "../../entity/dropDownOption";
|
||||
|
||||
export enum ADIcon {
|
||||
NONE = 'none',
|
||||
@ -23,14 +24,16 @@ export enum ADButtonColor {
|
||||
WARNING = 'warning',
|
||||
DANGER = 'danger',
|
||||
}
|
||||
export declare type ADInputFiledType = null | 'string' | 'password' | 'number';
|
||||
export declare type ADInputFiledType = null | 'string' | 'password' | 'number' | 'dropdown';
|
||||
|
||||
|
||||
export class MessageBoxConfig {
|
||||
title: string = '';
|
||||
text: string = '';
|
||||
inputTooltip: string = '';
|
||||
|
||||
dropDownOptions: DropDownOption[] = [];
|
||||
cardWidthClass: string = 'g-max-width-600';
|
||||
cardHeightClass: string = 'g-max-height-90vh';
|
||||
/**
|
||||
* Displayed Icon, Defaults to ADIcon.NONE, available types:
|
||||
* NONE, INFO, QUESTION, WARNING, ERROR
|
||||
|
||||
@ -5,6 +5,7 @@ import { FormsModule } from '@angular/forms';
|
||||
import { MaskDirectiveModule } from '../../directives/mask-directive/mask-directive.module';
|
||||
import { InitFocusModule } from '../../directives/init-focus/init-focus.module';
|
||||
import { AlertDlgComponent } from './alert-dlg.component';
|
||||
import { DropDownListModule } from '../drop-down-list/drop-down-list.module';
|
||||
@NgModule({
|
||||
declarations: [AlertDlgComponent],
|
||||
imports: [
|
||||
@ -17,7 +18,8 @@ import { AlertDlgComponent } from './alert-dlg.component';
|
||||
NbTooltipModule,
|
||||
NbProgressBarModule,
|
||||
MaskDirectiveModule,
|
||||
InitFocusModule
|
||||
InitFocusModule,
|
||||
DropDownListModule
|
||||
],
|
||||
exports: [
|
||||
AlertDlgComponent
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
<div class='form-group'>
|
||||
<label for='playerAmount' class='label' *ngIf="title" [innerHtml]="titleHtml"></label>
|
||||
<div>
|
||||
<button nbButton outline type="button" status="primary" size="{{size}}" (click)="adjustNumber(false)">
|
||||
<nb-icon pack='eva' icon='minus-outline'></nb-icon>
|
||||
</button>
|
||||
<b class="mx-2">{{currentNumber}}</b>
|
||||
<button nbButton outline type="button" status="primary" size="{{size}}" (click)="adjustNumber(true)"
|
||||
*ngIf="!hideIncreaseBtn">
|
||||
<nb-icon pack='eva' icon='plus-outline'></nb-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AdjacentNumberInputComponent } from './adjacent-number-input.component';
|
||||
|
||||
describe('AdjacentNumberInputComponent', () => {
|
||||
let component: AdjacentNumberInputComponent;
|
||||
let fixture: ComponentFixture<AdjacentNumberInputComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ AdjacentNumberInputComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AdjacentNumberInputComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,117 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR, NG_VALIDATORS, ControlValueAccessor } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'adj-number-input',
|
||||
templateUrl: './adjacent-number-input.component.html',
|
||||
styleUrls: ['./adjacent-number-input.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => AdjacentNumberInputComponent),
|
||||
multi: true
|
||||
},
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class AdjacentNumberInputComponent implements ControlValueAccessor, OnInit {
|
||||
|
||||
currentNumber: number;
|
||||
disabled: boolean;
|
||||
maximum: number = null;
|
||||
minimum: number = null;
|
||||
@Input("maximum")
|
||||
public set input_maximum(value) {
|
||||
if (typeof value !== "undefined" && value !== null) {
|
||||
this.maximum = Number.parseInt(value);
|
||||
} else {
|
||||
this.maximum = null;
|
||||
}
|
||||
}
|
||||
@Input("minimum")
|
||||
public set input_minimum(value) {
|
||||
if (typeof value !== "undefined" && value !== null) {
|
||||
this.minimum = Number.parseInt(value);
|
||||
} else {
|
||||
this.minimum = null;
|
||||
}
|
||||
}
|
||||
showMaximum: boolean = false;
|
||||
@Input("showMaximum")
|
||||
public set input_showMaximum(value) {
|
||||
this.showMaximum = typeof value !== "undefined" && value !== false;
|
||||
}
|
||||
|
||||
hideIncreaseBtn: boolean = false;
|
||||
@Input("hideIncreaseBtn")
|
||||
public set input_hideIncreaseBtn(value) {
|
||||
this.hideIncreaseBtn = typeof value !== "undefined" && value !== false;
|
||||
}
|
||||
|
||||
@Input() size = 'small';
|
||||
@Input() title = '';
|
||||
@Output() change = new EventEmitter<number>();
|
||||
@Output() focus = new EventEmitter();
|
||||
@Output() blur = new EventEmitter<number>();
|
||||
@Output() hitMinimum = new EventEmitter<number>();
|
||||
@Output() hitMaximum = new EventEmitter<number>();
|
||||
@Output() hitIncreasing = new EventEmitter<number>();
|
||||
@Output() hitDecreasing = new EventEmitter<number>();
|
||||
|
||||
onChange = (value: number) => {
|
||||
|
||||
};
|
||||
onTouched = () => { };
|
||||
constructor(
|
||||
private cdRef: ChangeDetectorRef
|
||||
) { }
|
||||
writeValue(obj: number): void {
|
||||
this.currentNumber = obj;
|
||||
this.onChange(obj);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: number) => void): void {
|
||||
this.onChange = fn;
|
||||
}
|
||||
registerOnTouched(fn: any): void {
|
||||
this.onTouched = fn;
|
||||
}
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
this.disabled = isDisabled;
|
||||
}
|
||||
|
||||
|
||||
public get titleHtml(): string {
|
||||
let displayText = this.title;
|
||||
if (this.showMaximum) {
|
||||
displayText = `${this.title} (Max. ${this.maximum})`
|
||||
}
|
||||
return displayText;
|
||||
}
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
}
|
||||
|
||||
adjustNumber(adding: boolean) {
|
||||
if (adding) {
|
||||
if (null == this.maximum || this.currentNumber < this.maximum) {
|
||||
this.currentNumber++;
|
||||
this.hitIncreasing.next(1);
|
||||
} else {
|
||||
this.hitMaximum.next(this.currentNumber);
|
||||
}
|
||||
} else {
|
||||
if (null == this.minimum || this.currentNumber > this.minimum) {
|
||||
this.currentNumber--;
|
||||
this.hitDecreasing.next(1);
|
||||
} else {
|
||||
this.hitMinimum.next(this.currentNumber);
|
||||
}
|
||||
}
|
||||
this.onChange(this.currentNumber);
|
||||
this.cdRef.detectChanges();
|
||||
this.blur.next(this.currentNumber);
|
||||
}
|
||||
}
|
||||
@ -2,16 +2,19 @@ import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CurrencyInputComponent } from './currency-input.component';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { NbInputModule, NbTooltipModule } from '@nebular/theme';
|
||||
import { NbButtonModule, NbIconModule, NbInputModule, NbTooltipModule } from '@nebular/theme';
|
||||
import { AdjacentNumberInputComponent } from './adjacent-number-input/adjacent-number-input.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [CurrencyInputComponent],
|
||||
declarations: [CurrencyInputComponent, AdjacentNumberInputComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
NbInputModule,
|
||||
NbTooltipModule,
|
||||
NbIconModule,
|
||||
NbButtonModule
|
||||
],
|
||||
exports: [CurrencyInputComponent]
|
||||
exports: [CurrencyInputComponent, AdjacentNumberInputComponent]
|
||||
})
|
||||
export class CurrencyInputModule { }
|
||||
|
||||
@ -60,12 +60,13 @@ export class StringUtils {
|
||||
return ['a', 'e', 'i', 'o', 'u'].indexOf(s[0].toLowerCase()) !== -1
|
||||
}
|
||||
|
||||
public static makeCommaSeparatedString(arr: string[], useOxfordComma: boolean) {
|
||||
public static makeCommaSeparatedString(arr: string[], useOxfordComma: boolean, usingOr: boolean = false) {
|
||||
let term = usingOr ? 'or' : 'and';
|
||||
arr = arr.filter(s => s);
|
||||
const listStart = arr.slice(0, -1).join(', ');
|
||||
const listEnd = arr.slice(-1);
|
||||
const conjunction = arr.length <= 1 ? '' :
|
||||
useOxfordComma && arr.length > 2 ? ', and ' : ' and ';
|
||||
useOxfordComma && arr.length > 2 ? `, ${term} ` : ` ${term}`;
|
||||
|
||||
return [listStart, listEnd].join(conjunction);
|
||||
}
|
||||
@ -95,14 +96,14 @@ export class StringUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* status could be `info` , `primary` , `danger` , `warning` , `success`
|
||||
* status could be `info` , `primary` , `danger` , `warning` , `success`, `light`, `dark`
|
||||
*/
|
||||
public static getHtmlBadge(text: string, status: string) {
|
||||
return `<label class="badge badge-${status} mr-1 my-0 py-1">${text}</label>`;
|
||||
|
||||
}
|
||||
/**
|
||||
* status could be `info` , `primary` , `danger` , `warning` , `success`
|
||||
* status could be `info` , `primary` , `danger` , `warning` , `success`, `light`, `dark`
|
||||
*/
|
||||
public static getHtmlBadges(text: string[], status: string) {
|
||||
|
||||
@ -123,8 +124,11 @@ export class StringUtils {
|
||||
}
|
||||
|
||||
public static toUpperString(text: string) {
|
||||
if (text?.trim) {
|
||||
|
||||
return text?.trim().toUpperCase();
|
||||
return text?.trim().toUpperCase();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static arrayToUrlParams(paramName: string, arr: string[]) {
|
||||
|
||||
@ -33206,10 +33206,6 @@ pre[class*="language-"] .toolbar-item a {
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
/* Max Height in Viewport Height (vh) */
|
||||
.g-max-height-90vh {
|
||||
height: 90vh !important;
|
||||
}
|
||||
|
||||
/* Min Height in Percentage (%) */
|
||||
.g-min-height-100x {
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
|
||||
|
||||
@import '../../../node_modules/bootstrap/scss/mixins/breakpoints';
|
||||
@import '../../../node_modules/@nebular/theme/styles/global/breakpoints';
|
||||
@import "../../../node_modules/bootstrap/scss/mixins/breakpoints";
|
||||
@import "../../../node_modules/@nebular/theme/styles/global/breakpoints";
|
||||
|
||||
$displays: none, inline, inline-block, block, table, table-row, table-cell, flex, inline-flex !default;
|
||||
|
||||
@ -12,7 +10,7 @@ $displays: none, inline, inline-block, block, table, table-row, table-cell, flex
|
||||
// lg: 992px,
|
||||
// xl: 1200px,
|
||||
// xxl: 1400px,
|
||||
// xxxl: 1600px
|
||||
// xxxl: 1600px,
|
||||
// ) !default;
|
||||
|
||||
// @include _assert-ascending($grid-breakpoints, "$grid-breakpoints");
|
||||
@ -22,7 +20,9 @@ $displays: none, inline, inline-block, block, table, table-row, table-cell, flex
|
||||
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
|
||||
|
||||
@each $value in $displays {
|
||||
.d#{$infix}-#{$value} { display: $value !important; }
|
||||
.d#{$infix}-#{$value} {
|
||||
display: $value !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user