diff --git a/.vscode/settings.json b/.vscode/settings.json index 8a81f3e..0243472 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -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" ], diff --git a/package.json b/package.json index 03c643b..2cf2565 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/app/@theme/components/header/header.component.ts b/src/app/@theme/components/header/header.component.ts index 9d61682..8dba218 100644 --- a/src/app/@theme/components/header/header.component.ts +++ b/src/app/@theme/components/header/header.component.ts @@ -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, ) { diff --git a/src/app/@theme/layouts/one-column/one-column.layout.ts b/src/app/@theme/layouts/one-column/one-column.layout.ts index cc0b789..eda833d 100644 --- a/src/app/@theme/layouts/one-column/one-column.layout.ts +++ b/src/app/@theme/layouts/one-column/one-column.layout.ts @@ -23,4 +23,4 @@ import { Component } from '@angular/core'; `, }) -export class OneColumnLayoutComponent {} +export class OneColumnLayoutComponent { } diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 3d6c7c6..ba83c75 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -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 } }, { diff --git a/src/app/cell-group/dinner/dinner.component.ts b/src/app/cell-group/dinner/dinner.component.ts index a175b44..4ff1995 100644 --- a/src/app/cell-group/dinner/dinner.component.ts +++ b/src/app/cell-group/dinner/dinner.component.ts @@ -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 { diff --git a/src/app/cell-group/prayer/prayer.component.ts b/src/app/cell-group/prayer/prayer.component.ts index 56025bb..c5ae26f 100644 --- a/src/app/cell-group/prayer/prayer.component.ts +++ b/src/app/cell-group/prayer/prayer.component.ts @@ -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 { diff --git a/src/app/cell-group/user-profile/user-profile.component.ts b/src/app/cell-group/user-profile/user-profile.component.ts index 5d22b95..10d6877 100644 --- a/src/app/cell-group/user-profile/user-profile.component.ts +++ b/src/app/cell-group/user-profile/user-profile.component.ts @@ -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; diff --git a/src/app/entity/Auth.ts b/src/app/entity/Auth.ts index 3836300..7623978 100644 --- a/src/app/entity/Auth.ts +++ b/src/app/entity/Auth.ts @@ -20,6 +20,8 @@ export interface LoginTokenViewModel { avatarImage: string; role: Role; cellGroup: PastoralDomain; + signalRSessionId; + sessionTabId: string; } export interface GoogleUserInfo { diff --git a/src/app/games/games-routing.module.ts b/src/app/games/games-routing.module.ts index f427314..e35e04e 100644 --- a/src/app/games/games-routing.module.ts +++ b/src/app/games/games-routing.module.ts @@ -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 }, ] }, diff --git a/src/app/games/games.component.html b/src/app/games/games.component.html index 198b3b6..ee9e676 100644 --- a/src/app/games/games.component.html +++ b/src/app/games/games.component.html @@ -1,4 +1,6 @@ - - + + - \ No newline at end of file + + + \ No newline at end of file diff --git a/src/app/games/games.component.ts b/src/app/games/games.component.ts index 4164a24..065ddea 100644 --- a/src/app/games/games.component.ts +++ b/src/app/games/games.component.ts @@ -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('我是遊戲王'); diff --git a/src/app/games/games.model.ts b/src/app/games/games.model.ts new file mode 100644 index 0000000..48d618f --- /dev/null +++ b/src/app/games/games.model.ts @@ -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; +} \ No newline at end of file diff --git a/src/app/games/games.module.ts b/src/app/games/games.module.ts index 26fb786..d5a2dd1 100644 --- a/src/app/games/games.module.ts +++ b/src/app/games/games.module.ts @@ -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 { } diff --git a/src/app/games/massive-darkness2/MD2Base.ts b/src/app/games/massive-darkness2/MD2Base.ts new file mode 100644 index 0000000..aead4c8 --- /dev/null +++ b/src/app/games/massive-darkness2/MD2Base.ts @@ -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 = new Subject(); + /** + * + */ + 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 `` + } + 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 same zone 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 = new Subject(); + /** + * + */ + 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); + } +} \ No newline at end of file diff --git a/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.html b/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.html new file mode 100644 index 0000000..dcbcd69 --- /dev/null +++ b/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.html @@ -0,0 +1,26 @@ + + + +
+
+ +
+ +
+ + +
+
+
+ + + + +
\ No newline at end of file diff --git a/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.scss b/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.scss new file mode 100644 index 0000000..4d37b1c --- /dev/null +++ b/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.scss @@ -0,0 +1,4 @@ +nb-card { + height: 70vh; + width: 80vw; +} diff --git a/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.spec.ts b/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.spec.ts new file mode 100644 index 0000000..9624f85 --- /dev/null +++ b/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.spec.ts @@ -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; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ BossActivationComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(BossActivationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.ts b/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.ts new file mode 100644 index 0000000..d186c5a --- /dev/null +++ b/src/app/games/massive-darkness2/boss-fight/boss-activation/boss-activation.component.ts @@ -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, + 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(); + } + +} diff --git a/src/app/games/massive-darkness2/boss-fight/boss-fight.component.html b/src/app/games/massive-darkness2/boss-fight/boss-fight.component.html new file mode 100644 index 0000000..52fd653 --- /dev/null +++ b/src/app/games/massive-darkness2/boss-fight/boss-fight.component.html @@ -0,0 +1,29 @@ + + + {{boss.name}} + + + +
+
+ +
+
+ + + + + + + + + +
+ +
+ +
+
\ No newline at end of file diff --git a/src/app/games/massive-darkness2/boss-fight/boss-fight.component.scss b/src/app/games/massive-darkness2/boss-fight/boss-fight.component.scss new file mode 100644 index 0000000..dd4510a --- /dev/null +++ b/src/app/games/massive-darkness2/boss-fight/boss-fight.component.scss @@ -0,0 +1,4 @@ +nb-card { + height: 80vh; + //width: 80vw; +} diff --git a/src/app/games/massive-darkness2/boss-fight/boss-fight.component.spec.ts b/src/app/games/massive-darkness2/boss-fight/boss-fight.component.spec.ts new file mode 100644 index 0000000..a8fae6c --- /dev/null +++ b/src/app/games/massive-darkness2/boss-fight/boss-fight.component.spec.ts @@ -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; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ BossFightComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(BossFightComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/games/massive-darkness2/boss-fight/boss-fight.component.ts b/src/app/games/massive-darkness2/boss-fight/boss-fight.component.ts new file mode 100644 index 0000000..8f368a8 --- /dev/null +++ b/src/app/games/massive-darkness2/boss-fight/boss-fight.component.ts @@ -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(); + } + } + + }); + + } +} diff --git a/src/app/games/massive-darkness2/door-events/door-events.component.html b/src/app/games/massive-darkness2/door-events/door-events.component.html new file mode 100644 index 0000000..a1849ee --- /dev/null +++ b/src/app/games/massive-darkness2/door-events/door-events.component.html @@ -0,0 +1,38 @@ + + + Door Event + + + + + + +
+ +
+ +
+ + +
+
+ + + +
+
\ No newline at end of file diff --git a/src/app/games/massive-darkness2/door-events/door-events.component.scss b/src/app/games/massive-darkness2/door-events/door-events.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/games/massive-darkness2/door-events/door-events.component.spec.ts b/src/app/games/massive-darkness2/door-events/door-events.component.spec.ts new file mode 100644 index 0000000..f6a699b --- /dev/null +++ b/src/app/games/massive-darkness2/door-events/door-events.component.spec.ts @@ -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; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DoorEventsComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DoorEventsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/games/massive-darkness2/door-events/door-events.component.ts b/src/app/games/massive-darkness2/door-events/door-events.component.ts new file mode 100644 index 0000000..5942e9b --- /dev/null +++ b/src/app/games/massive-darkness2/door-events/door-events.component.ts @@ -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 = new DrawingBag(); + + + 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(); + 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: ``, 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(); + } + +} diff --git a/src/app/games/massive-darkness2/hero-dashboard/hero-dashboard.component.html b/src/app/games/massive-darkness2/hero-dashboard/hero-dashboard.component.html new file mode 100644 index 0000000..d4407d6 --- /dev/null +++ b/src/app/games/massive-darkness2/hero-dashboard/hero-dashboard.component.html @@ -0,0 +1,141 @@ + + + + + +
+
+
+
+
+
+ + +
+
+ + + + +
+
+
+ +
+ +
+ + +
+
+ + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+ +
+
+
+ + + + + + +
+ + + +
+
+ + +
+ +
+ +
+ \ No newline at end of file diff --git a/src/app/games/massive-darkness2/hero-dashboard/hero-dashboard.component.scss b/src/app/games/massive-darkness2/hero-dashboard/hero-dashboard.component.scss new file mode 100644 index 0000000..d6ab450 --- /dev/null +++ b/src/app/games/massive-darkness2/hero-dashboard/hero-dashboard.component.scss @@ -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); +} diff --git a/src/app/games/massive-darkness2/hero-dashboard/hero-dashboard.component.spec.ts b/src/app/games/massive-darkness2/hero-dashboard/hero-dashboard.component.spec.ts new file mode 100644 index 0000000..7b4de64 --- /dev/null +++ b/src/app/games/massive-darkness2/hero-dashboard/hero-dashboard.component.spec.ts @@ -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; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ HeroDashboardComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HeroDashboardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/games/massive-darkness2/hero-dashboard/hero-dashboard.component.ts b/src/app/games/massive-darkness2/hero-dashboard/hero-dashboard.component.ts new file mode 100644 index 0000000..a4befa3 --- /dev/null +++ b/src/app/games/massive-darkness2/hero-dashboard/hero-dashboard.component.ts @@ -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: ``, + 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'; + } +} diff --git a/src/app/games/massive-darkness2/massive-darkness2.component.html b/src/app/games/massive-darkness2/massive-darkness2.component.html index 5c63931..5bb0036 100644 --- a/src/app/games/massive-darkness2/massive-darkness2.component.html +++ b/src/app/games/massive-darkness2/massive-darkness2.component.html @@ -1,25 +1,101 @@
+ + +
- Treasure Bag - - - + Game Info + + + -
- - X {{treasure.unitAmount}} - + +
+
+ + + + +
+ +
+ + +
+
+ +
+
+ + +
+ + Lv.:{{hero.level}} + HP: {{hero.hp}}/{{hero.hpMaximum}} + Mana: {{hero.mp}}/{{hero.mpMaximum}} + Exp: {{hero.exp}} + Fire:{{hero.fireToken}} + Frozen:{{hero.frozenToken}} + Inactive + Remain + Actions: {{hero.remainActions}} + + +
+
-
+
+ +
+
+
+
+ + +
+ +
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
\ No newline at end of file diff --git a/src/app/games/massive-darkness2/massive-darkness2.component.ts b/src/app/games/massive-darkness2/massive-darkness2.component.ts index f5d5c1c..8398231 100644 --- a/src/app/games/massive-darkness2/massive-darkness2.component.ts +++ b/src/app/games/massive-darkness2/massive-darkness2.component.ts @@ -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: `
Link` }); } - 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 = `
`; + 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 } \ No newline at end of file diff --git a/src/app/games/massive-darkness2/massive-darkness2.model.boss.ts b/src/app/games/massive-darkness2/massive-darkness2.model.boss.ts new file mode 100644 index 0000000..5319131 --- /dev/null +++ b/src/app/games/massive-darkness2/massive-darkness2.model.boss.ts @@ -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 + spawnMob: Subject + spawnRoamingMonster: Subject + 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 + spawnMob: Subject + spawnRoamingMonster: Subject + 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}.
` + + `Deal ${this.darkBlessingCorruptionAmt} Wounds per ${this.corruptionTokenHtml} to all Heros in each Tiles distributed as they wish.`, 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[] +} \ No newline at end of file diff --git a/src/app/games/massive-darkness2/massive-darkness2.model.dice.ts b/src/app/games/massive-darkness2/massive-darkness2.model.dice.ts new file mode 100644 index 0000000..99e7453 --- /dev/null +++ b/src/app/games/massive-darkness2/massive-darkness2.model.dice.ts @@ -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 }; + } +} diff --git a/src/app/games/massive-darkness2/massive-darkness2.model.ts b/src/app/games/massive-darkness2/massive-darkness2.model.ts new file mode 100644 index 0000000..8c1ed70 --- /dev/null +++ b/src/app/games/massive-darkness2/massive-darkness2.model.ts @@ -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) { + Object.assign(this, config); + + } + level: number = 1; + needExp: number = 0; + currentExp = 0 + extraHp = 0 + extraMp = 0 + extraRareToken = 0 + extraEpicToken = 0 +} +export class DrawingBag { + 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 = {} + ) { + 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 => ``) + .concat(this.fixedCarriedTreasure?.map(i => ``)).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 = `` + + `
Target Unit HP:${this.unitRemainHp}`; + + if (this.isRoamingMonster) { + html += `