This commit is contained in:
Chris Chen 2024-03-22 11:06:42 -07:00
parent b46392bc41
commit 6a031ca478
38 changed files with 993 additions and 376 deletions

View File

@ -2,7 +2,7 @@ import { ChangeDetectorRef, Injectable } from "@angular/core";
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { Subject } from "rxjs"; import { Subject } from "rxjs";
import { first, takeUntil } from "rxjs/operators"; import { first, takeUntil } from "rxjs/operators";
import { MD2GameInfo, MD2Service } from "../../services/md2.service"; import { MD2GameInfo, MD2Service } from "../../services/MD2/md2.service";
import { SignalRMessage } from "../../services/signal-r.service"; import { SignalRMessage } from "../../services/signal-r.service";
import { StateService } from "../../services/state.service"; import { StateService } from "../../services/state.service";
import { ADIcon, MessageBoxConfig } from "../../ui/alert-dlg/alert-dlg.model"; import { ADIcon, MessageBoxConfig } from "../../ui/alert-dlg/alert-dlg.model";
@ -55,14 +55,14 @@ export abstract class MD2Base {
} }
imgUrl(imgPath: string) { imgUrl(imgPath: string) {
return this.md2Service.imgUrl(imgPath); return this.md2Service.stateService.imgUrl(imgPath);
} }
fileList(folderPath: string) { fileList(folderPath: string) {
return this.md2Service.fileList(folderPath); return this.md2Service.fileList(folderPath);
} }
iconHtml(icon: MD2Icon, cssClass = '') { iconHtml(icon: MD2Icon, cssClass = '') {
return this.md2Service.iconHtml(icon, cssClass); return this.md2Service.stateService.iconHtml(icon, cssClass);
} }
imgHtml(imgFile: string, cssClass = '') { imgHtml(imgFile: string, cssClass = '') {
@ -218,13 +218,13 @@ export abstract class MD2ComponentBase {
this.destroy$.complete(); this.destroy$.complete();
} }
imgUrl(imgPath: string) { imgUrl(imgPath: string) {
return this.md2Service.imgUrl(imgPath); return this.md2Service.stateService.imgUrl(imgPath);
} }
fileList(folderPath: string) { fileList(folderPath: string) {
return this.md2Service.fileList(folderPath); return this.md2Service.fileList(folderPath);
} }
iconHtml(icon: MD2Icon, cssClass = '') { iconHtml(icon: MD2Icon, cssClass = '') {
return this.md2Service.iconHtml(icon, cssClass); return this.md2Service.stateService.iconHtml(icon, cssClass);
} }
} }

View File

@ -16,6 +16,10 @@
<label class="g-font-size-20 mt-3" [innerHtml]="bossAction.skillDescription"> <label class="g-font-size-20 mt-3" [innerHtml]="bossAction.skillDescription">
</label> </label>
<md2-mob-attack-info [mob]="boss.info">
</md2-mob-attack-info>
<md2-mob-combat-info [mob]="boss.info"></md2-mob-combat-info>
</div> </div>
</div> </div>
</nb-card-body> </nb-card-body>

View File

@ -1,7 +1,7 @@
import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { NbDialogRef } from '@nebular/theme'; import { NbDialogRef } from '@nebular/theme';
import { MD2Service } from '../../../../services/md2.service'; import { MD2Service } from '../../../../services/MD2/md2.service';
import { MsgBoxService } from '../../../../services/msg-box.service'; import { MsgBoxService } from '../../../../services/msg-box.service';
import { StateService } from '../../../../services/state.service'; import { StateService } from '../../../../services/state.service';
import { MobDlgType, MD2Icon, MD2HeroInfo } from '../../massive-darkness2.model'; import { MobDlgType, MD2Icon, MD2HeroInfo } from '../../massive-darkness2.model';

View File

@ -17,10 +17,10 @@
</adj-number-input> </adj-number-input>
<button nbButton hero status="danger" size="small" (click)="attack(boss.info)">Attack It</button> <button nbButton hero status="danger" size="small" (click)="attack(boss.info)">Attack It</button>
<label class="MD2Text mt-3" [innerHtml]="boss.combatInfo.skillName"> <!-- <label class="MD2Text mt-3" [innerHtml]="boss.info.combatSkill.skillName">
</label>
<label class="MD2Text" [innerHtml]="boss.combatInfo.skillDescription">
</label> </label>
<label class="MD2Text" [innerHtml]="boss.info.combatInfo.skillDescription">
</label> -->
</div> </div>
</div> </div>

View File

@ -3,7 +3,7 @@ import { ActivatedRoute } from '@angular/router';
import { NbDialogService } from '@nebular/theme'; import { NbDialogService } from '@nebular/theme';
import { Subject, Observable } from 'rxjs'; import { Subject, Observable } from 'rxjs';
import { first } from 'rxjs/operators'; import { first } from 'rxjs/operators';
import { MD2Service } from '../../../services/md2.service'; import { MD2Service } from '../../../services/MD2/md2.service';
import { MsgBoxService } from '../../../services/msg-box.service'; import { MsgBoxService } from '../../../services/msg-box.service';
import { StateService } from '../../../services/state.service'; import { StateService } from '../../../services/state.service';
import { ADIcon } from '../../../ui/alert-dlg/alert-dlg.model'; import { ADIcon } from '../../../ui/alert-dlg/alert-dlg.model';

View File

@ -5,7 +5,7 @@ import { ADButtons } from '../../../ui/alert-dlg/alert-dlg.model';
import { DrawingBag, DrawingItem } from '../massive-darkness2.model'; import { DrawingBag, DrawingItem } from '../massive-darkness2.model';
import { first } from 'rxjs/operators'; import { first } from 'rxjs/operators';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { MD2Service } from '../../../services/md2.service'; import { MD2Service } from '../../../services/MD2/md2.service';
import { StateService } from '../../../services/state.service'; import { StateService } from '../../../services/state.service';
import { MD2Base, MD2ComponentBase } from '../MD2Base'; import { MD2Base, MD2ComponentBase } from '../MD2Base';
import { SignalRMessage } from '../../../services/signal-r.service'; import { SignalRMessage } from '../../../services/signal-r.service';

View File

@ -0,0 +1,212 @@
import { environment } from "../../../../../environments/environment";
import { DefenseInfo, IMobFactory, MD2Icon, MobInfo } from "../../massive-darkness2.model";
import { MobSkill, MobSkillType } from "../../massive-darkness2.model.boss";
const MD2_IMG_URL = (id: string = null) => { return `${environment.apiUrl}/Files/Images/MD2/Mobs${(id ? `${encodeURI(id)}` : '')}` }
const CORE_GAME_MOB_LEVEL = [
{ name: 'Gargoyles', level: 1, hp: 2, rewardTokens: 1, defBlue: 1 },
{ name: 'Gargoyles', level: 3, hp: 3, rewardTokens: 1, defBlue: 2 },
{ name: 'Gargoyles', level: 5, hp: 6, rewardTokens: 2, defBlue: 3 },
{ name: 'Demons', level: 1, hp: 3, rewardTokens: 1, defBlue: 1 },
{ name: 'Demons', level: 3, hp: 4, rewardTokens: 1, defBlue: 2 },
{ name: 'Demons', level: 5, hp: 6, rewardTokens: 2, defBlue: 4 },
{ name: 'Undead', level: 1, hp: 4, rewardTokens: 1, defBlue: 1 },
{ name: 'Undead', level: 3, hp: 5, rewardTokens: 1, defBlue: 1 },
{ name: 'Undead', level: 5, hp: 8, rewardTokens: 2, defBlue: 1 },
{ name: 'Fire Entities', level: 1, hp: 3, rewardTokens: 1, defBlue: 1 },
{ name: 'Fire Entities', level: 3, hp: 4, rewardTokens: 1, defBlue: 2 },
{ name: 'Fire Entities', level: 5, hp: 7, rewardTokens: 2, defBlue: 3 },
{ name: 'Fallen Angels', level: 1, hp: 2, rewardTokens: 1, defBlue: 2 },
{ name: 'Fallen Angels', level: 3, hp: 3, rewardTokens: 1, defBlue: 3 },
{ name: 'Fallen Angels', level: 5, hp: 5, rewardTokens: 2, defBlue: 5 },
{ name: 'Infernal Imps', level: 1, hp: 3, rewardTokens: 1, defBlue: 1 },
{ name: 'Infernal Imps', level: 3, hp: 4, rewardTokens: 1, defBlue: 1 },
{ name: 'Infernal Imps', level: 5, hp: 5, rewardTokens: 2, defBlue: 3 },
{ name: 'Skeletons', level: 1, hp: 2, rewardTokens: 1, defBlue: 1 },
{ name: 'Skeletons', level: 3, hp: 3, rewardTokens: 1, defBlue: 2 },
{ name: 'Skeletons', level: 5, hp: 5, rewardTokens: 2, defBlue: 4 },
{ name: 'Satyrs', level: 1, hp: 3, rewardTokens: 1, defBlue: 1 },
{ name: 'Satyrs', level: 3, hp: 4, rewardTokens: 1, defBlue: 2 },
{ name: 'Satyrs', level: 5, hp: 6, rewardTokens: 2, defBlue: 4 }]
export abstract class MobFactory implements IMobFactory {
abstract generate(level: number): MobInfo
protected mob: MobInfo;
protected loadLevelInfo(mobName: string, level: number) {
let levelInfo = CORE_GAME_MOB_LEVEL.find(m => m.name == mobName && level >= m.level);
this.mob = new MobInfo({
name: mobName, hp: levelInfo.hp, level: level, rewardTokens: levelInfo.rewardTokens,
defenseInfo: new DefenseInfo(levelInfo.defBlue)
});
this.mob.leaderImgUrl = MD2_IMG_URL(`CoreGame/Mobs/${encodeURI(this.mob.name)}/Leader.png`);
this.mob.minionImgUrl = MD2_IMG_URL(`CoreGame/Mobs/${encodeURI(this.mob.name)}/Minion.png`);
}
iconHtml(icon: MD2Icon, cssClass = '') {
if (icon == MD2Icon.Fire) {
cssClass += ' g-color-google-plus ';
}
if (icon == MD2Icon.Frost || icon == MD2Icon.Mana) {
cssClass += ' g-color-aqua ';
}
if (icon < MD2Icon.RedDice) {
return `<span class='MD2Icon ${cssClass}'>${String.fromCharCode(65 + icon)}</span>`
}
else {
return `<span class='MD2Icon dice ${MD2Icon[icon].replace('Dice', '')} ${cssClass}'></span>`;
}
}
}
export class MobDemonsFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('Demons', level);
this.mob.combatSkill = new MobSkill(
{
description: `Attacking or defending Hero discards 1 ${this.iconHtml(MD2Icon.Mana)}`
}
)
return this.mob;
}
}
export class MobFallenAngelFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('FallenAngels', level);
this.mob.combatSkill = new MobSkill(
{
description: `Defender -${level == 1 ? 1 : 2} ${this.iconHtml(MD2Icon.Defense)}`,
type: MobSkillType.Attack
}
)
return this.mob;
}
}
export class MobFireEntitiesFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('Fire Entities', level);
this.mob.combatSkill = new MobSkill(
{
description: `Add 1 ${this.iconHtml(MD2Icon.Fire)} to the attacking or defending Hero.`,
type: MobSkillType.Combat
}
)
return this.mob;
}
}
export class MobGargoylesFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('Gargoyles', level);
this.mob.combatSkill = new MobSkill(
{
description: `+ ${level < 5 ? 1 : 2} ${this.iconHtml(MD2Icon.Defense)}`,
type: MobSkillType.Defense
}
)
return this.mob;
}
}
export class MobInfernalImpsFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('Infernal Imps', level);
let damage = 1;
switch (level) {
case 1:
case 2:
damage = 1;
break;
case 3:
case 4:
damage = 2;
break;
case 5:
damage = 3;
break;
default:
damage = 1;
break;
}
this.mob.combatSkill = new MobSkill(
{
description: `Kill 1 Imp, then deal ${damage} Wound to each Hero in the attacker's Zone(once per roll).`,
type: MobSkillType.Defense
}
)
return this.mob;
}
}
export class MobSatyrsFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('Satyrs', level);
this.mob.combatSkill = new MobSkill(
{
description: `+ ${level < 3 ? 1 : 2} ${this.iconHtml(MD2Icon.Attack)}`,
type: MobSkillType.Attack
}
)
return this.mob;
}
}
export class MobSkeletonsFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('Skeletons', level);
this.mob.combatSkill = new MobSkill(
{
description: `Add 1 minion to this Mob(if possible) unless the Hero discards ${level < 5 ? 1 : 2} ${this.iconHtml(MD2Icon.Mana)}.`,
type: MobSkillType.Defense,
skillRoll: 2
}
)
return this.mob;
}
}
export class MobUndeadFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('Undead', level);
let skillDesc = '';
if (level < 3) {
skillDesc = `+1 ${this.iconHtml(MD2Icon.YellowDice)}`;
} else if (level < 5) {
skillDesc = `+2 ${this.iconHtml(MD2Icon.YellowDice)}`;
} else {
skillDesc = `+1 ${this.iconHtml(MD2Icon.YellowDice)} 1 ${this.iconHtml(MD2Icon.OrangeDice)}`;
}
skillDesc += ' and this Mob takes 2 wounds';
this.mob.combatSkill = new MobSkill(
{
description: skillDesc,
type: MobSkillType.Attack
}
)
return this.mob;
}
}
export const CoreGameMobFactories = [
new MobDemonsFactory(),
new MobFallenAngelFactory(),
new MobFireEntitiesFactory(),
new MobGargoylesFactory(),
new MobInfernalImpsFactory(),
new MobSatyrsFactory(),
new MobSkeletonsFactory(),
new MobUndeadFactory(),
];

View File

@ -0,0 +1,212 @@
import { environment } from "../../../../../environments/environment";
import { DefenseInfo, IMobFactory, MD2Icon, MobInfo } from "../../massive-darkness2.model";
import { MobSkill, MobSkillType } from "../../massive-darkness2.model.boss";
const MD2_IMG_URL = (id: string = null) => { return `${environment.apiUrl}/Files/Images/MD2/Mobs${(id ? `${encodeURI(id)}` : '')}` }
const CORE_GAME_MOB_LEVEL = [
{ name: 'Gargoyles', level: 1, hp: 2, rewardTokens: 1, defBlue: 1 },
{ name: 'Gargoyles', level: 3, hp: 3, rewardTokens: 1, defBlue: 2 },
{ name: 'Gargoyles', level: 5, hp: 6, rewardTokens: 2, defBlue: 3 },
{ name: 'Demons', level: 1, hp: 3, rewardTokens: 1, defBlue: 1 },
{ name: 'Demons', level: 3, hp: 4, rewardTokens: 1, defBlue: 2 },
{ name: 'Demons', level: 5, hp: 6, rewardTokens: 2, defBlue: 4 },
{ name: 'Undead', level: 1, hp: 4, rewardTokens: 1, defBlue: 1 },
{ name: 'Undead', level: 3, hp: 5, rewardTokens: 1, defBlue: 1 },
{ name: 'Undead', level: 5, hp: 8, rewardTokens: 2, defBlue: 1 },
{ name: 'Fire Entities', level: 1, hp: 3, rewardTokens: 1, defBlue: 1 },
{ name: 'Fire Entities', level: 3, hp: 4, rewardTokens: 1, defBlue: 2 },
{ name: 'Fire Entities', level: 5, hp: 7, rewardTokens: 2, defBlue: 3 },
{ name: 'Fallen Angels', level: 1, hp: 2, rewardTokens: 1, defBlue: 2 },
{ name: 'Fallen Angels', level: 3, hp: 3, rewardTokens: 1, defBlue: 3 },
{ name: 'Fallen Angels', level: 5, hp: 5, rewardTokens: 2, defBlue: 5 },
{ name: 'Infernal Imps', level: 1, hp: 3, rewardTokens: 1, defBlue: 1 },
{ name: 'Infernal Imps', level: 3, hp: 4, rewardTokens: 1, defBlue: 1 },
{ name: 'Infernal Imps', level: 5, hp: 5, rewardTokens: 2, defBlue: 3 },
{ name: 'Skeletons', level: 1, hp: 2, rewardTokens: 1, defBlue: 1 },
{ name: 'Skeletons', level: 3, hp: 3, rewardTokens: 1, defBlue: 2 },
{ name: 'Skeletons', level: 5, hp: 5, rewardTokens: 2, defBlue: 4 },
{ name: 'Satyrs', level: 1, hp: 3, rewardTokens: 1, defBlue: 1 },
{ name: 'Satyrs', level: 3, hp: 4, rewardTokens: 1, defBlue: 2 },
{ name: 'Satyrs', level: 5, hp: 6, rewardTokens: 2, defBlue: 4 }]
export abstract class MobFactory implements IMobFactory {
abstract generate(level: number): MobInfo
protected mob: MobInfo;
protected loadLevelInfo(mobName: string, level: number) {
let levelInfo = CORE_GAME_MOB_LEVEL.find(m => m.name == mobName && level >= m.level);
this.mob = new MobInfo({
name: mobName, hp: levelInfo.hp, level: level, rewardTokens: levelInfo.rewardTokens,
defenseInfo: new DefenseInfo(levelInfo.defBlue)
});
this.mob.leaderImgUrl = MD2_IMG_URL(`CoreGame/Mobs/${encodeURI(this.mob.name)}/Leader.png`);
this.mob.minionImgUrl = MD2_IMG_URL(`CoreGame/Mobs/${encodeURI(this.mob.name)}/Minion.png`);
}
iconHtml(icon: MD2Icon, cssClass = '') {
if (icon == MD2Icon.Fire) {
cssClass += ' g-color-google-plus ';
}
if (icon == MD2Icon.Frost || icon == MD2Icon.Mana) {
cssClass += ' g-color-aqua ';
}
if (icon < MD2Icon.RedDice) {
return `<span class='MD2Icon ${cssClass}'>${String.fromCharCode(65 + icon)}</span>`
}
else {
return `<span class='MD2Icon dice ${MD2Icon[icon].replace('Dice', '')} ${cssClass}'></span>`;
}
}
}
export class MobDemonsFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('Demons', level);
this.mob.combatSkill = new MobSkill(
{
description: `Attacking or defending Hero discards 1 ${this.iconHtml(MD2Icon.Mana)}`
}
)
return this.mob;
}
}
export class MobFallenAngelFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('FallenAngels', level);
this.mob.combatSkill = new MobSkill(
{
description: `Defender -${level == 1 ? 1 : 2} ${this.iconHtml(MD2Icon.Defense)}`,
type: MobSkillType.Attack
}
)
return this.mob;
}
}
export class MobFireEntitiesFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('Fire Entities', level);
this.mob.combatSkill = new MobSkill(
{
description: `Add 1 ${this.iconHtml(MD2Icon.Fire)} to the attacking or defending Hero.`,
type: MobSkillType.Combat
}
)
return this.mob;
}
}
export class MobGargoylesFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('Gargoyles', level);
this.mob.combatSkill = new MobSkill(
{
description: `+ ${level < 5 ? 1 : 2} ${this.iconHtml(MD2Icon.Defense)}`,
type: MobSkillType.Defense
}
)
return this.mob;
}
}
export class MobInfernalImpsFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('Infernal Imps', level);
let damage = 1;
switch (level) {
case 1:
case 2:
damage = 1;
break;
case 3:
case 4:
damage = 2;
break;
case 5:
damage = 3;
break;
default:
damage = 1;
break;
}
this.mob.combatSkill = new MobSkill(
{
description: `Kill 1 Imp, then deal ${damage} Wound to each Hero in the attacker's Zone(once per roll).`,
type: MobSkillType.Defense
}
)
return this.mob;
}
}
export class MobSatyrsFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('Satyrs', level);
this.mob.combatSkill = new MobSkill(
{
description: `+ ${level < 3 ? 1 : 2} ${this.iconHtml(MD2Icon.Attack)}`,
type: MobSkillType.Attack
}
)
return this.mob;
}
}
export class MobSkeletonsFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('Skeletons', level);
this.mob.combatSkill = new MobSkill(
{
description: `Add 1 minion to this Mob(if possible) unless the Hero discards ${level < 5 ? 1 : 2} ${this.iconHtml(MD2Icon.Mana)}.`,
type: MobSkillType.Defense,
skillRoll: 2
}
)
return this.mob;
}
}
export class MobUndeadFactory extends MobFactory {
generate(level: number): MobInfo {
this.loadLevelInfo('Undead', level);
let skillDesc = '';
if (level < 3) {
skillDesc = `+1 ${this.iconHtml(MD2Icon.YellowDice)}`;
} else if (level < 5) {
skillDesc = `+2 ${this.iconHtml(MD2Icon.YellowDice)}`;
} else {
skillDesc = `+1 ${this.iconHtml(MD2Icon.YellowDice)} 1 ${this.iconHtml(MD2Icon.OrangeDice)}`;
}
skillDesc += ' and this Mob takes 2 wounds';
this.mob.combatSkill = new MobSkill(
{
description: skillDesc,
type: MobSkillType.Attack
}
)
return this.mob;
}
}
export const CoreGameMobFactories = [
new MobDemonsFactory(),
new MobFallenAngelFactory(),
new MobFireEntitiesFactory(),
new MobGargoylesFactory(),
new MobInfernalImpsFactory(),
new MobSatyrsFactory(),
new MobSkeletonsFactory(),
new MobUndeadFactory(),
];

View File

@ -4,7 +4,7 @@ import { ActivatedRoute } from '@angular/router';
import { first } from 'rxjs/operators'; import { first } from 'rxjs/operators';
import { DropDownOption } from '../../../entity/dropDownOption'; import { DropDownOption } from '../../../entity/dropDownOption';
import { GameRoomService } from '../../../services/game-room.service'; import { GameRoomService } from '../../../services/game-room.service';
import { MD2Service } from '../../../services/md2.service'; import { MD2Service } from '../../../services/MD2/md2.service';
import { MsgBoxService } from '../../../services/msg-box.service'; import { MsgBoxService } from '../../../services/msg-box.service';
import { StateService } from '../../../services/state.service'; import { StateService } from '../../../services/state.service';
import { ADButtonColor, ADButtons } from '../../../ui/alert-dlg/alert-dlg.model'; import { ADButtonColor, ADButtons } from '../../../ui/alert-dlg/alert-dlg.model';

View File

@ -6,7 +6,7 @@ import { ArrayUtils } from '../../utilities/array-utils';
import { ObjectUtils } from '../../utilities/object-utils'; import { ObjectUtils } from '../../utilities/object-utils';
import { first, map, take, takeUntil } from 'rxjs/operators'; import { first, map, take, takeUntil } from 'rxjs/operators';
import { TreasureType, DrawingBag, DrawingItem, HeroClass, MD2HeroInfo, RoundPhase, MobInfo, MobDlgType } from './massive-darkness2.model'; import { TreasureType, DrawingBag, DrawingItem, HeroClass, MD2HeroInfo, RoundPhase, MobInfo, MobDlgType } from './massive-darkness2.model';
import { MD2Service } from '../../services/md2.service'; import { MD2Service } from '../../services/MD2/md2.service';
import { GameRoomService } from '../../services/game-room.service'; import { GameRoomService } from '../../services/game-room.service';
import { MD2Base } from './MD2Base'; import { MD2Base } from './MD2Base';
import { StateService } from '../../services/state.service'; import { StateService } from '../../services/state.service';

View File

@ -1,9 +1,10 @@
import { stringify } from "querystring"
import { Subject } from "rxjs" import { Subject } from "rxjs"
import { first } from "rxjs/operators" import { first } from "rxjs/operators"
import { MD2Service } from "../../services/md2.service" import { MD2Service } from "../../services/MD2/md2.service"
import { StringUtils } from "../../utilities/string-utils" import { StringUtils } from "../../utilities/string-utils"
import { BossActivationComponent } from "./boss-fight/boss-activation/boss-activation.component" import { BossActivationComponent } from "./boss-fight/boss-activation/boss-activation.component"
import { TreasureType, AttackInfo, DefenseInfo, AttackType, MD2Icon, MD2HeroInfo, AttackTarget, MobInfo } from "./massive-darkness2.model" import { TreasureType, AttackInfo, DefenseInfo, AttackType, MD2Icon, MD2HeroInfo, AttackTarget, MobInfo, MobType } from "./massive-darkness2.model"
import { RollingBlackDice } from "./massive-darkness2.model.dice" import { RollingBlackDice } from "./massive-darkness2.model.dice"
@ -19,7 +20,6 @@ export interface IBossFight {
actionBlackDice: number actionBlackDice: number
imgUrl: string imgUrl: string
standUrl: string standUrl: string
combatInfo: MobSkill
activating(): boolean activating(): boolean
prepareForBossFight(): void prepareForBossFight(): void
nextRound(): void nextRound(): void
@ -27,25 +27,27 @@ export interface IBossFight {
export class BossMicheal implements IBossFight { export class BossMicheal implements IBossFight {
constructor(private md2Service: MD2Service) { constructor(private md2Service: MD2Service) {
this.corruptionTokenHtml = this.md2Service.stateService.imgHtml('Tokens/CorruptToken.png');
this.name = 'Michael - The Corrupted Archangel'; this.name = 'Michael - The Corrupted Archangel';
this.imgUrl = md2Service.imgUrl('/Boss/Michael - The Corrupted Archangel.jpg'); this.imgUrl = md2Service.stateService.imgUrl('/Boss/Michael - The Corrupted Archangel.jpg');
this.standUrl = md2Service.imgUrl('/Boss/Michael.png'); this.standUrl = md2Service.stateService.imgUrl('/Boss/Michael.png');
this.hpPerHero = 15; this.hpPerHero = 15;
this.info = new MobInfo({ this.info = new MobInfo({
isRoamingMonster: true, description: this.name,
type: MobType.Boss,
hp: this.md2Service.heros.length * this.hpPerHero, hp: this.md2Service.heros.length * this.hpPerHero,
level: 10 unitRemainHp: this.md2Service.heros.length * this.hpPerHero,
level: 10,
combatSkill: new MobSkill(`Combat 1 ${this.md2Service.stateService.iconHtml(MD2Icon.EnemySkill)}`,
`Deal 1 Wound for each ${this.corruptionTokenHtml} on the attacking or defending Hero. Discard the tokens afterwards(once per combat).`),
imageUrl: md2Service.stateService.imgUrl('/Boss/Michael.png')
}); });
this.info.defenseInfos = new DefenseInfo(5, 1); this.info.defenseInfo = new DefenseInfo(5, 1);
this.info.attackInfos = [new AttackInfo(MD2Icon.Melee, 2, 2, 0, 1)]; this.info.attackInfos = [new AttackInfo(MD2Icon.Melee, 2, 2, 0, 1)];
this.actions = 1; this.actions = 1;
this.rounds = 0; this.rounds = 0;
this.actionBlackDice = 2; 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 name: string
addTreasureToken: Subject<TreasureType> addTreasureToken: Subject<TreasureType>
@ -58,7 +60,6 @@ export class BossMicheal implements IBossFight {
actionBlackDice: number actionBlackDice: number
imgUrl: string imgUrl: string
standUrl: string standUrl: string
combatInfo: MobSkill
corruptionTokenHtml: string corruptionTokenHtml: string
activating(): boolean { activating(): boolean {
@ -71,20 +72,20 @@ export class BossMicheal implements IBossFight {
//Justice From Above //Justice From Above
beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.MostCorruption, true); beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.MostCorruption, true);
bossAction = new MobSkill('Justice From Above', bossAction = new MobSkill('Justice From Above',
`Place Michael in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.`, beenAttackedHero); `Place Michael in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.`);
break; break;
case 1: case 1:
//Lance Dash //Lance Dash
beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.LeastCorruption, true); beenAttackedHero = this.md2Service.getTargetHerosByFilter(AttackTarget.LeastCorruption, true);
bossAction = new MobSkill('Lance Dash', 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); `Move Michael and Place 1 ${this.corruptionTokenHtml} in the Zone at ${this.md2Service.getTargetHerosHtml(beenAttackedHero)} and attack Him/Her.`);
break; break;
case 2: case 2:
//Dark Blessing //Dark Blessing
bossAction = new MobSkill('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>` + `Place Michael on the central Zone and add 1 ${this.corruptionTokenHtml} to the Corruption Stone Zone with the least amount of ${this.corruptionTokenHtml}.<br>` +
`Deal <b>${this.darkBlessingCorruptionAmt}</b> Wounds per ${this.corruptionTokenHtml} to all Heros in each Tiles <b>distributed as they wish</b>.`, beenAttackedHero); `Deal <b>${this.darkBlessingCorruptionAmt}</b> Wounds per ${this.corruptionTokenHtml} to all Heros in each Tiles <b>distributed as they wish</b>.`);
break; break;
default: default:
@ -109,7 +110,7 @@ export class BossMicheal implements IBossFight {
break; break;
case 2: case 2:
case 4: case 4:
this.info.defenseInfos[0].black += 1; this.info.defenseInfo[0].black += 1;
this.info.attackInfos[0].black += 1; this.info.attackInfos[0].black += 1;
break; break;
// case 4: // case 4:
@ -122,14 +123,27 @@ export class BossMicheal implements IBossFight {
} }
} }
} }
export enum MobSkillType {
Attack,
Defense,
Combat
}
export class MobSkill { export class MobSkill {
constructor(skillName: string, skillDescription: string, targetHeros: MD2HeroInfo[] = []) { constructor(config: Partial<MobSkill> = {}) {
this.skillName = skillName let defaultConfig = {
this.skillDescription = skillDescription type: MobSkillType.Combat,
this.targetHeros = targetHeros skillRoll: 1
} as Partial<MobSkill>;
Object.assign(defaultConfig, config);
Object.assign(this, defaultConfig);
// if (StringUtils.isNullOrWhitespace(this.name)) {
// this.name=`${MobSkillType[this.type]} ${this.skillRoll} ${}`
// }
} }
skillName: string type: MobSkillType;
skillDescription: string clawRoll: number;
skillRoll: number;
name: string
description: string
targetHeros: MD2HeroInfo[] targetHeros: MD2HeroInfo[]
} }

View File

@ -1,8 +1,10 @@
import { Subject } from "rxjs"; import { Subject } from "rxjs";
import { environment } from "../../../environments/environment";
import { ObjectUtils } from "../../utilities/object-utils"; import { ObjectUtils } from "../../utilities/object-utils";
import { GamePlayer } from "../games.model"; import { GamePlayer } from "../games.model";
import { MobSkill } from "./massive-darkness2.model.boss"; import { MobSkill } from "./massive-darkness2.model.boss";
const MD2_IMG_URL = (id: string = null) => { return `${environment.apiUrl}/Files/Images/MD2/${(id ? `${encodeURI(id)}` : '')}` }
export enum MobDlgType { export enum MobDlgType {
Spawn, Spawn,
Activating, Activating,
@ -30,6 +32,12 @@ export enum HeroClass {
Shaman, Shaman,
Paladin, Paladin,
} }
export enum MobType {
Mob,
RoamingMonster,
Boss
}
export enum MD2Icon { export enum MD2Icon {
Attack, Attack,
Defense, Defense,
@ -95,7 +103,6 @@ export class AttackInfo {
red: number red: number
yellow: number yellow: number
black: number black: number
attackSkill: MobSkill
} }
export class DefenseInfo { export class DefenseInfo {
constructor(blue: number, black: number = 0) { constructor(blue: number, black: number = 0) {
@ -104,7 +111,6 @@ export class DefenseInfo {
} }
blue: number blue: number
black: number black: number
defenseSkill: MobSkill
} }
export class MD2LevelUpReward { export class MD2LevelUpReward {
constructor(config: Partial<MD2LevelUpReward>) { constructor(config: Partial<MD2LevelUpReward>) {
@ -207,7 +213,9 @@ export class DrawingBag<T extends IDrawingItem> {
this.removedItems = []; this.removedItems = [];
} }
} }
export interface IMobFactory {
generate(level: number): MobInfo;
}
export interface IDrawingItem { export interface IDrawingItem {
imageUrl: string imageUrl: string
name: string name: string
@ -231,7 +239,17 @@ export class DrawingItem implements IDrawingItem {
description: string description: string
drawingWeight: number drawingWeight: number
} }
export class TreasureItem extends DrawingItem {
constructor(type: TreasureType, itemAmount: number = 1) {
super(`${TreasureType[type]} Treasure`,
`It's a ${TreasureType[type]} Treasure!`,
MD2_IMG_URL(`TreasureToken/${TreasureType[type]}.png`), itemAmount);
this.type = type;
this.itemAmount = itemAmount;
}
type: TreasureType;
itemAmount: number;
}
export class MobInfo implements IDrawingItem { export class MobInfo implements IDrawingItem {
constructor( constructor(
config: Partial<MobInfo> = {} config: Partial<MobInfo> = {}
@ -241,8 +259,11 @@ export class MobInfo implements IDrawingItem {
this.drawingWeight = 1; this.drawingWeight = 1;
this.unitRemainHp = config.hp this.unitRemainHp = config.hp
} }
type: MobType = MobType.Mob;
imageUrl: string imageUrl: string
standUrl: string standUrl: string
leaderImgUrl: string
minionImgUrl: string
name: string name: string
description: string description: string
drawingWeight: number drawingWeight: number
@ -250,12 +271,14 @@ export class MobInfo implements IDrawingItem {
rewardTokens: number; rewardTokens: number;
hp: number; hp: number;
mobAmount: number; mobAmount: number;
carriedTreasure: DrawingItem[]; carriedTreasure: TreasureItem[];
fixedCarriedTreasure: DrawingItem[]; fixedCarriedTreasure: TreasureItem[];
unitRemainHp: number; unitRemainHp: number;
isRoamingMonster: boolean = false;
attackInfos: AttackInfo[]; attackInfos: AttackInfo[];
defenseInfos: DefenseInfo; defenseInfo: DefenseInfo;
combatSkill: MobSkill
fireToken: number = 0; fireToken: number = 0;
frozenToken: number = 0; frozenToken: number = 0;
corruptionToken: number = 0; corruptionToken: number = 0;
@ -268,44 +291,43 @@ export class MobInfo implements IDrawingItem {
public get carriedTreasureHtml(): string { public get carriedTreasureHtml(): string {
if (!this.carriedTreasure) return ''; if (!this.carriedTreasure) return '';
return this.carriedTreasure.map(i => `<img src="${i.imageUrl}" class='mr-1' width="40px">`) return this.carriedTreasure.map(i => `<img src="${i.imageUrl}" class='mr-1' width="40px">`)
.concat(this.fixedCarriedTreasure?.map(i => `<img src="${i.imageUrl}" class='mr-1' width="40px">`)).join(); .concat(this.fixedCarriedTreasure?.map(i => `<img src="${i.imageUrl}" class='mr-1' width="40px">`.repeat(i.drawingWeight))).join();
} }
public get totalHp(): number { public get totalHp(): number {
return this.isRoamingMonster ? this.unitRemainHp : (this.mobAmount - 1) * this.hp + this.unitRemainHp; switch (this.type) {
case MobType.Mob:
return (this.mobAmount - 1) * this.hp + this.unitRemainHp;
case MobType.RoamingMonster:
return this.unitRemainHp;
case MobType.Boss:
default:
return this.unitRemainHp;
}
} }
public get minionAmount(): number { public get minionAmount(): number {
switch (this.type) {
case MobType.Mob:
return (this.mobAmount - 1); return (this.mobAmount - 1);
case MobType.RoamingMonster:
case MobType.Boss:
default:
return 0;
}
} }
public get leaderExp(): number { public get leaderExp(): number {
return this.isRoamingMonster ? 4 : 2; switch (this.type) {
case MobType.Mob:
return 2;
case MobType.RoamingMonster:
return 4;
case MobType.Boss:
default:
return 0;
}
} }
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 { export class MD2HeroInfo {
@ -344,7 +366,7 @@ export class MD2Rules {
public static CoreGameLevelBoard = [ public static CoreGameLevelBoard = [
new MD2LevelUpReward({ level: 2, needExp: 5, extraHp: 1, extraMp: 0, extraRareToken: 1 }), 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: 3, needExp: 10, extraHp: 1, extraMp: 1, extraEpicToken: 1 }),
new MD2LevelUpReward({ level: 4, needExp: 12, extraHp: 2, extraMp: 2, extraEpicToken: 1 }), new MD2LevelUpReward({ level: 4, needExp: 12, extraHp: 2, extraMp: 1, extraEpicToken: 1 }),
new MD2LevelUpReward({ level: 5, needExp: 18, extraHp: 2, extraMp: 2, extraEpicToken: 1 }), new MD2LevelUpReward({ level: 5, needExp: 18, extraHp: 2, extraMp: 2, extraEpicToken: 1 }),
]; ];
public static checkCoreGameLevelup(currentLevel: number, currentExp: number): MD2LevelUpReward { public static checkCoreGameLevelup(currentLevel: number, currentExp: number): MD2LevelUpReward {

View File

@ -3,7 +3,7 @@ import { ControlValueAccessor, Validator, AbstractControl, ValidationErrors, NG_
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { DropDownOption } from '../../../entity/dropDownOption'; import { DropDownOption } from '../../../entity/dropDownOption';
import { MD2Service } from '../../../services/md2.service'; import { MD2Service } from '../../../services/MD2/md2.service';
import { ArrayUtils } from '../../../utilities/array-utils'; import { ArrayUtils } from '../../../utilities/array-utils';
import { HeroClass, MD2HeroInfo } from '../massive-darkness2.model'; import { HeroClass, MD2HeroInfo } from '../massive-darkness2.model';

View File

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

View File

@ -8,14 +8,33 @@ import { MD2Icon } from '../massive-darkness2.model';
}) })
export class MD2IconComponent implements OnInit { export class MD2IconComponent implements OnInit {
@Input() iconClass: string = ''; @Input() iconClass: string = 'mr-1';
@Input("icon") icon: MD2Icon; @Input("icon") icon: string;
@Input() size: string = 'sm';
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {
} }
public get sizeClass(): string {
switch (this.size) {
case 'sm':
return 'g-font-size-18'
break;
case 'med':
return 'g-font-size-30'
break;
case 'lg':
return 'g-font-size-50'
break;
default:
return 'g-font-size-' + this.size;
break;
}
}
} }

View File

@ -1,7 +1,7 @@
<label class='label'>Weapon Info</label> <label class='label'>Weapon Info</label>
<div class="g-brd-3 g-brd-bottom--dashed g-brd-gray-light-v2 mb-3 mt-2 row" *ngFor="let info of mob.attackInfos"> <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"> <div class="col-md-4">
<span class=" g-font-size-50" [innerHtml]="iconHtml(info.type)"></span> <md2-icon icon="attack" size="lg"></md2-icon>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<div *ngIf="info.yellow" class="g-height-45"> <div *ngIf="info.yellow" class="g-height-45">
@ -19,5 +19,10 @@
<span class="MD2text diceAmount">x{{info.red}}</span> <span class="MD2text diceAmount">x{{info.red}}</span>
</span> </span>
</div> </div>
<div *ngIf="mob.defenseInfo.black" class="g-height-45 mt-1">
<span class="MD2Icon Black dice g-font-size-50">
<span class="MD2text diceAmount">x{{mob.defenseInfo.black}}</span>
</span>
</div>
</div> </div>
</div> </div>

View File

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

View File

@ -1,8 +1,8 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { MD2Icon, MobInfo } from '../../../massive-darkness2.model'; import { MD2Icon, MobDlgType, MobInfo } from '../../../massive-darkness2.model';
@Component({ @Component({
selector: 'ngx-mob-attack-info', selector: 'md2-mob-attack-info',
templateUrl: './mob-attack-info.component.html', templateUrl: './mob-attack-info.component.html',
styleUrls: ['./mob-attack-info.component.scss'] styleUrls: ['./mob-attack-info.component.scss']
}) })
@ -20,6 +20,7 @@ export class MobAttackInfoComponent implements OnInit {
} }
} }
@Input() mode: MobDlgType = MobDlgType.PreView;
constructor() { } constructor() { }

View File

@ -1 +1,5 @@
<p>mob-combat-info works!</p> <div class='form-group row' *ngIf="showSkill">
<label class='label col-sm-3 form-control-label MD2text g-font-size-30'
[innerHtml]="mob.combatSkill.skillName"></label>
<div class='col-sm' [innerHtml]="mob.combatSkill.skillDescription"></div>
</div>

View File

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

View File

@ -1,15 +1,47 @@
import { Component, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { MD2Icon, MobDlgType, MobInfo } from '../../../massive-darkness2.model';
import { MobSkillType } from '../../../massive-darkness2.model.boss';
@Component({ @Component({
selector: 'ngx-mob-combat-info', selector: 'md2-mob-combat-info',
templateUrl: './mob-combat-info.component.html', templateUrl: './mob-combat-info.component.html',
styleUrls: ['./mob-combat-info.component.scss'] styleUrls: ['./mob-combat-info.component.scss']
}) })
export class MobCombatInfoComponent implements OnInit { export class MobCombatInfoComponent implements OnInit {
MD2Icon = MD2Icon;
private _mob: MobInfo;
public get mob(): MobInfo {
return this._mob;
}
@Input() public set mob(v: MobInfo) {
if (this._mob != v) {
this._mob = v;
}
}
@Input() mode: MobDlgType = MobDlgType.PreView;
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {
if (this.mob.combatSkill) {
switch (this.mode) {
case MobDlgType.Activating:
this.showSkill = [MobSkillType.Combat, MobSkillType.Attack].includes(this.mob.combatSkill.type);
break;
case MobDlgType.BeenAttacked:
this.showSkill = [MobSkillType.Combat, MobSkillType.Defense].includes(this.mob.combatSkill.type);
break;
case MobDlgType.PreView:
case MobDlgType.Spawn:
default:
this.showSkill = true;
break;
}
} }
}
showSkill: boolean = false;;
} }

View File

@ -1 +1,18 @@
<p>mob-def-info works!</p> <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">
<md2-icon icon="defense" size="lg"></md2-icon>
</div>
<div class="col-md-8">
<div *ngIf="mob.defenseInfo.blue" class="g-height-45">
<span class="MD2Icon Blue dice g-font-size-50">
<span class="MD2text diceAmount">x{{mob.defenseInfo.blue}}</span>
</span>
</div>
<div *ngIf="mob.defenseInfo.black" class="g-height-45 mt-1">
<span class="MD2Icon Black dice g-font-size-50">
<span class="MD2text diceAmount">x{{mob.defenseInfo.black}}</span>
</span>
</div>
</div>
</div>

View File

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

View File

@ -1,14 +1,27 @@
import { Component, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { MD2Icon, MobInfo, MobDlgType } from '../../../massive-darkness2.model';
@Component({ @Component({
selector: 'ngx-mob-def-info', selector: 'md2-mob-def-info',
templateUrl: './mob-def-info.component.html', templateUrl: './mob-def-info.component.html',
styleUrls: ['./mob-def-info.component.scss'] styleUrls: ['./mob-def-info.component.scss']
}) })
export class MobDefInfoComponent implements OnInit { export class MobDefInfoComponent implements OnInit {
constructor() { } MD2Icon = MD2Icon;
private _mob: MobInfo;
public get mob(): MobInfo {
return this._mob;
}
@Input() public set mob(v: MobInfo) {
if (this._mob != v) {
this._mob = v;
}
}
@Input() mode: MobDlgType = MobDlgType.PreView;
constructor() { }
ngOnInit(): void { ngOnInit(): void {
} }

View File

@ -10,35 +10,14 @@
<label class='label'>Carried Treasure</label><br> <label class='label'>Carried Treasure</label><br>
<div [innerHtml]="mob.carriedTreasureHtml"></div> <div [innerHtml]="mob.carriedTreasureHtml"></div>
</ng-container> </ng-container>
<md2-mob-attack-info [mob]="mob" [mode]="mode" *ngIf="mob.attackInfos&&mob.attackInfos.length>0&& !hideWeaponInfo">
<ng-container *ngIf="mob.attackInfos&&mob.attackInfos.length>0&& !hideWeaponInfo"> </md2-mob-attack-info>
<md2-mob-def-info [mob]="mob" [mode]="mode" *ngIf="mob.defenseInfo&& !hideWeaponInfo"></md2-mob-def-info>
</ng-container> <md2-mob-combat-info [mob]="mob" [mode]="mode"></md2-mob-combat-info>
<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"> <div *ngIf="showBlackDice" class="row">
<!-- <md2-icon></md2-icon> --> <!-- <md2-icon></md2-icon> -->
<div class="col-md-4"> <div class="col-md-4">
<span class=" g-font-size-50" [innerHtml]="iconHtml(MD2Icon.EnemySkill)"></span> <md2-icon icon="enemySkill" size="lg"></md2-icon>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<span class="MD2Icon Black dice g-font-size-50"> <span class="MD2Icon Black dice g-font-size-50">

View File

@ -1,6 +1,6 @@
import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { MD2Service } from '../../../../services/md2.service'; import { MD2Service } from '../../../../services/MD2/md2.service';
import { StateService } from '../../../../services/state.service'; import { StateService } from '../../../../services/state.service';
import { MD2Icon, MobDlgType, MobInfo } from '../../massive-darkness2.model'; import { MD2Icon, MobDlgType, MobInfo } from '../../massive-darkness2.model';
import { MD2ComponentBase } from '../../MD2Base'; import { MD2ComponentBase } from '../../MD2Base';

View File

@ -3,7 +3,7 @@ import { ActivatedRoute } from '@angular/router';
import { NbDialogService } from '@nebular/theme'; import { NbDialogService } from '@nebular/theme';
import { first } from 'rxjs/operators'; import { first } from 'rxjs/operators';
import { FileService } from '../../../services/file.service'; import { FileService } from '../../../services/file.service';
import { MD2Service } from '../../../services/md2.service'; import { MD2Service } from '../../../services/MD2/md2.service';
import { MsgBoxService } from '../../../services/msg-box.service'; import { MsgBoxService } from '../../../services/msg-box.service';
import { StateService } from '../../../services/state.service'; import { StateService } from '../../../services/state.service';
import { ADIcon } from '../../../ui/alert-dlg/alert-dlg.model'; import { ADIcon } from '../../../ui/alert-dlg/alert-dlg.model';
@ -71,20 +71,14 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
}); });
} }
spawnMob() { spawnMob() {
let result = this.md2Service.drawMob(this.isRoamingMonster); let result = this.md2Service.spawnMob(this.isRoamingMonster);
if (result.exitingMob) { let titleText = result.exitingMob == null ? `${result.mob.description} Shows Up` : `${result.mob.description} Activate One Action Now!`;
this.dlgService.open(SpawnMobDlgComponent, { context: { title: `${result.mob.description} Activate One Action Now!`, mode: MobDlgType.Activating, mob: result.mob } }) let actType = result.exitingMob == null ? MobDlgType.Spawn : MobDlgType.Activating;
.onClose.pipe(first()).subscribe(result => {
this.afterSpawn() this.dlgService.open(SpawnMobDlgComponent, { context: { title: titleText, mode: actType, mob: result.mob } })
});
} 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 => { .onClose.pipe(first()).subscribe(result => {
this.afterSpawn(); this.afterSpawn();
}); });
}
this.cdRef.detectChanges(); this.cdRef.detectChanges();
} }
afterSpawn() { afterSpawn() {
@ -214,7 +208,6 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
.onClose.pipe(first()).subscribe(result => { .onClose.pipe(first()).subscribe(result => {
}); });
//this.msgBoxService.show('', { text: mob.mobInfoHtml.replace('g-height-50vh', 'g-height-70vh') });
} }
weaponHtml(mob: MobInfo) { weaponHtml(mob: MobInfo) {
let html = '<br>'; let html = '<br>';

View File

@ -1,6 +1,6 @@
<nb-card status="{{headerStatus}}" class="mobCard" size="giant"> <nb-card status="{{headerStatus}}" class="mobCard" size="giant">
<nb-card-header> <nb-card-header>
<img src="{{imgUrl('Mobs/MobToken.png')}}" width="40px"> {{(mob.isRoamingMonster?'Roaming Monster':'Mob')}} <img src="{{imgUrl('Mobs/MobToken.png')}}" width="40px"> {{cardTitle}}
<span [innerHtml]="titleHtml"></span> <span [innerHtml]="titleHtml"></span>
</nb-card-header> </nb-card-header>
<nb-card-body> <nb-card-body>
@ -12,7 +12,7 @@
<md2-mob-detail-info [mob]="mob" [mode]="mode"> <md2-mob-detail-info [mob]="mob" [mode]="mode">
</md2-mob-detail-info> </md2-mob-detail-info>
<ng-container *ngIf="mode==MobDlgType.Spawn&&!mob.isRoamingMonster"> <ng-container *ngIf="mode==MobDlgType.Spawn&&mob.type==MobType.Mob">
<div class="row form-group mt-2"> <div class="row form-group mt-2">
<div class="col-md-2"> <div class="col-md-2">
<span class="MD2Icon melee g-font-size-40"></span> <span class="MD2Icon melee g-font-size-40"></span>

View File

@ -2,11 +2,11 @@ import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { NbDialogRef } from '@nebular/theme'; import { NbDialogRef } from '@nebular/theme';
import { FileService } from '../../../../services/file.service'; import { FileService } from '../../../../services/file.service';
import { MD2Service } from '../../../../services/md2.service'; import { MD2Service } from '../../../../services/MD2/md2.service';
import { MsgBoxService } from '../../../../services/msg-box.service'; import { MsgBoxService } from '../../../../services/msg-box.service';
import { StateService } from '../../../../services/state.service'; import { StateService } from '../../../../services/state.service';
import { StringUtils } from '../../../../utilities/string-utils'; import { StringUtils } from '../../../../utilities/string-utils';
import { AttackInfo, AttackTarget, AttackType, MD2HeroInfo, MD2Icon, MobDlgType, MobInfo } from '../../massive-darkness2.model'; import { AttackInfo, AttackTarget, AttackType, MD2HeroInfo, MD2Icon, MobDlgType, MobInfo, MobType } from '../../massive-darkness2.model';
import { MD2ComponentBase } from '../../MD2Base'; import { MD2ComponentBase } from '../../MD2Base';
@Component({ @Component({
@ -16,8 +16,9 @@ import { MD2ComponentBase } from '../../MD2Base';
}) })
export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit { export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit {
MobDlgType = MobDlgType; MobDlgType = MobDlgType;
MobType = MobType;
mode: MobDlgType; mode: MobDlgType;
cardTitle: string;
title: string; title: string;
titleHtml: string; titleHtml: string;
MD2Icon = MD2Icon; MD2Icon = MD2Icon;
@ -38,28 +39,8 @@ export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
//this.mob = new MobInfo(this.mob); //this.mob = new MobInfo(this.mob);
if (this.mode == MobDlgType.Spawn) { if (this.mode == MobDlgType.Spawn && this.mob.type == MobType.Mob) {
this.mob.attackInfos = [ this.mob.attackInfos = [new AttackInfo(MD2Icon.Melee), new AttackInfo(MD2Icon.Range), new AttackInfo(MD2Icon.Magic)];
{
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.uiWounds = 0;
this.mob.uiFrozenTokens = 0; this.mob.uiFrozenTokens = 0;
@ -93,6 +74,9 @@ export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit {
this.dlgRef.close(); this.dlgRef.close();
} }
initTitleHtml() { initTitleHtml() {
this.cardTitle = MobType[this.mob.type];
let htmlText = ''; let htmlText = '';
if (this.mode == MobDlgType.Spawn) { if (this.mode == MobDlgType.Spawn) {
htmlText = `${this.mob.description} Shows Up`; htmlText = `${this.mob.description} Shows Up`;

View File

@ -3,7 +3,7 @@ import { ActivatedRoute } from '@angular/router';
import { Subject } from 'rxjs-compat'; import { Subject } from 'rxjs-compat';
import { first, takeUntil } from 'rxjs/operators'; import { first, takeUntil } from 'rxjs/operators';
import { FileService } from '../../../services/file.service'; import { FileService } from '../../../services/file.service';
import { MD2Service } from '../../../services/md2.service'; import { MD2Service } from '../../../services/MD2/md2.service';
import { MsgBoxService } from '../../../services/msg-box.service'; import { MsgBoxService } from '../../../services/msg-box.service';
import { StateService } from '../../../services/state.service'; import { StateService } from '../../../services/state.service';
import { ADButtons, ADIcon } from '../../../ui/alert-dlg/alert-dlg.model'; import { ADButtons, ADIcon } from '../../../ui/alert-dlg/alert-dlg.model';

View File

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

View File

@ -0,0 +1,103 @@
import { Injectable } from '@angular/core';
import { NbDialogService } from '@nebular/theme';
import { first } from 'rxjs/operators';
import { MD2HeroInfo } from '../../games/massive-darkness2/massive-darkness2.model';
import { FileService } from '../file.service';
import { GameRoomService } from '../game-room.service';
import { LoginUserService } from '../login-user.service';
import { SignalRService, SignalRSession, SignalRMessage } from '../signal-r.service';
import { MD2StateService } from './md2-state.service';
@Injectable({
providedIn: 'root'
})
export class MD2BroadcastService {
constructor(
public fileService: FileService,
private gameRoomService: GameRoomService,
private loginUserService: LoginUserService,
public stateService: MD2StateService,
public signalRService: SignalRService,
public dlgService: NbDialogService
) { }
public get playerHero(): MD2HeroInfo {
return this.stateService.info.heros.find(h => h.playerInfo.signalRClientId == this.loginUserService.userAccess.signalRSessionId);
}
public broadcastAllHeroInfoToAll() {
this.stateService.info.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.stateService.info);
this.broadcastMessage('GameRoom', 'update', parameters);
}
public broadcastMobsInfo() {
let parameters = {};
parameters['roamingMonsters'] = JSON.stringify(this.stateService.info.roamingMonsters);
parameters['mobs'] = JSON.stringify(this.stateService.info.mobs);
this.broadcastMessage('mobs', 'update', parameters);
}
public broadcastMyHeroInfo() {
this.broadcastHeroInfoToAll(this.playerHero);
}
}

View File

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

View File

@ -0,0 +1,38 @@
import { Injectable } from '@angular/core';
import { MD2Icon, TreasureType } from '../../games/massive-darkness2/massive-darkness2.model';
import { FileService } from '../file.service';
import { MD2GameInfo } from './md2.service';
@Injectable({
providedIn: 'root'
})
export class MD2StateService {
private _highestPlayerLevel: number = 1;
private _playerAmount: number = 2;
public info: MD2GameInfo;
constructor(
public fileService: FileService,
) { }
public iconHtml(icon: MD2Icon, cssClass = '') {
if (icon < MD2Icon.RedDice) {
return `<span class='MD2Icon ${cssClass}'>${String.fromCharCode(65 + icon)}</span>`
} else {
return `<span class='MD2Icon dice ${MD2Icon[icon].replace('Dice', '')} ${cssClass}'></span>`;
}
}
public imgHtml(imgPath: string, cssClass = 'g-height-25 mr-1') {
return `<img src='${this.imgUrl(imgPath)}' class='${cssClass}'>`;
}
public imgUrl(imgPath: string) {
return this.fileService.ImageUrl('MD2/' + imgPath);
}
public treasureImage(type: TreasureType) {
return this.imgUrl(`TreasureToken/${TreasureType[type]}.png`);
}
}

View File

@ -1,19 +1,21 @@
import { Injectable } from '@angular/core'; 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 { AttackInfo, AttackTarget, CoreGameDarknessPhaseRule, DefenseInfo, DrawingBag, DrawingItem, HeroClass, IDarknessPhaseRule, MD2HeroInfo, MD2Icon, MD2Rules, MobInfo, MobType, RoundPhase, TreasureItem, TreasureType } from '../../games/massive-darkness2/massive-darkness2.model';
import { first, map, reduce } from "rxjs/operators"; import { 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 { NbDialogService } from '@nebular/theme';
import { IBossFight } from '../games/massive-darkness2/massive-darkness2.model.boss'; import { Subject } from 'rxjs';
import { IBossFight } from '../../games/massive-darkness2/massive-darkness2.model.boss';
import { ADIcon, MessageBoxConfig } from '../../ui/alert-dlg/alert-dlg.model';
import { NumberUtils } from '../../utilities/number-utils';
import { StringUtils } from '../../utilities/string-utils';
import { FileService } from '../file.service';
import { GameRoomService } from '../game-room.service';
import { LoginUserService } from '../login-user.service';
import { MsgBoxService } from '../msg-box.service';
import { SignalRService, SignalRSession, SignalRMessage } from '../signal-r.service';
import { MD2StateService } from './md2-state.service';
import { MD2BroadcastService } from './md2-broadcast.service';
import { CoreGameMobFactories } from '../../games/massive-darkness2/factorys/mobs/CoreGame';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -21,11 +23,6 @@ import { IBossFight } from '../games/massive-darkness2/massive-darkness2.model.b
export class MD2Service { export class MD2Service {
// #region Properties (24) // #region Properties (24)
private _highestPlayerLevel: number = 1;
private _playerAmount: number = 2;
public info: MD2GameInfo;
public darknessPhaseRule: IDarknessPhaseRule; public darknessPhaseRule: IDarknessPhaseRule;
public enemyPhaseMobs: MobInfo[]; public enemyPhaseMobs: MobInfo[];
public enemyPhaseSubject = new Subject<MobInfo>(); public enemyPhaseSubject = new Subject<MobInfo>();
@ -33,20 +30,28 @@ export class MD2Service {
public mobBeenKilledSubject = new Subject<MobInfo>(); public mobBeenKilledSubject = new Subject<MobInfo>();
public mobDeck: DrawingBag<MobInfo>; public mobDeck: DrawingBag<MobInfo>;
public roamingMobDeck: DrawingBag<MobInfo>; public roamingMobDeck: DrawingBag<MobInfo>;
public treasureBag: DrawingBag<DrawingItem> = new DrawingBag<DrawingItem>(); public treasureBag: DrawingBag<TreasureItem> = new DrawingBag<TreasureItem>();
public refreshUI$: Subject<void> = new Subject<void>(); public refreshUI$: Subject<void> = new Subject<void>();
public refreshTreasureBagSubject = new Subject<DrawingBag<DrawingItem>>(); public refreshTreasureBagSubject = new Subject<DrawingBag<TreasureItem>>();
public get heros() { public get heros() {
return this.info.heros; return this.stateService.info.heros;
} }
public get roamingMonsters() { public get roamingMonsters() {
return this.info.roamingMonsters; return this.stateService.info.roamingMonsters;
} }
public get mobs() { public get mobs() {
return this.info.mobs; return this.stateService.info.mobs;
}
public get info(): MD2GameInfo {
return this.stateService.info;
}
public set info(v: MD2GameInfo) {
this.stateService.info = v;
} }
// #endregion Properties (24) // #endregion Properties (24)
@ -58,11 +63,13 @@ export class MD2Service {
public msgBoxService: MsgBoxService, public msgBoxService: MsgBoxService,
private gameRoomService: GameRoomService, private gameRoomService: GameRoomService,
private loginUserService: LoginUserService, private loginUserService: LoginUserService,
public stateService: MD2StateService,
public signalRService: SignalRService, public signalRService: SignalRService,
public dlgService: NbDialogService public dlgService: NbDialogService,
public broadcastService: MD2BroadcastService
) { ) {
this.darknessPhaseRule = new CoreGameDarknessPhaseRule(); this.darknessPhaseRule = new CoreGameDarknessPhaseRule();
this.info = new MD2GameInfo(); this.stateService.info = new MD2GameInfo();
this.darknessPhaseRule.addTreasureToken.subscribe(treasureType => { this.darknessPhaseRule.addTreasureToken.subscribe(treasureType => {
this.addTreasure(treasureType, 1); this.addTreasure(treasureType, 1);
}); });
@ -71,56 +78,64 @@ export class MD2Service {
// #endregion Constructors (1) // #endregion Constructors (1)
private initCoreGameRoamingMonsters() { private initCoreGameRoamingMonsters() {
let mobs = []; 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: 5, level: 1, rewardTokens: 2, type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Rare)] }));
this.roamingMobDeck.AddItem(new MobInfo({ name: 'Andra', hp: 7, level: 3, rewardTokens: 2, isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic)] })); this.roamingMobDeck.AddItem(new MobInfo({ name: 'Andra', hp: 7, level: 3, rewardTokens: 2, type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic)] }));
this.roamingMobDeck.AddItem(new MobInfo({ name: 'Andra', hp: 5, level: 5, rewardTokens: 0, isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic)] })); this.roamingMobDeck.AddItem(new MobInfo({ name: 'Andra', hp: 5, level: 5, rewardTokens: 0, type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic, 3)] }));
this.roamingMobDeck.AddItem(new MobInfo({ this.roamingMobDeck.AddItem(new MobInfo({
name: 'Ytheria, Undead Queen', hp: 4, level: 1, rewardTokens: 2, name: 'Ytheria, Undead Queen', hp: 4, level: 1, rewardTokens: 2,
attackInfos: [new AttackInfo(MD2Icon.Melee, 1), new AttackInfo(MD2Icon.Rage, 2, 0, 0, 1)], attackInfos: [new AttackInfo(MD2Icon.Melee, 1), new AttackInfo(MD2Icon.Rage, 2, 0, 0, 1)],
defenseInfos: new DefenseInfo(1, 1), defenseInfo: new DefenseInfo(1, 1),
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Rare)] type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Rare)]
})); }));
this.roamingMobDeck.AddItem(new MobInfo({ this.roamingMobDeck.AddItem(new MobInfo({
name: 'Ytheria, Undead Queen', hp: 6, level: 3, rewardTokens: 2, 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)], attackInfos: [new AttackInfo(MD2Icon.Melee, 0, 1), new AttackInfo(MD2Icon.Rage, 1, 1, 0, 1)],
defenseInfos: new DefenseInfo(2, 1), defenseInfo: new DefenseInfo(2, 1),
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic)] type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic)]
})); }));
this.roamingMobDeck.AddItem(new MobInfo({ this.roamingMobDeck.AddItem(new MobInfo({
name: 'Ytheria, Undead Queen', hp: 8, level: 5, rewardTokens: 0, 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)], attackInfos: [new AttackInfo(MD2Icon.Melee, 2, 1), new AttackInfo(MD2Icon.Rage, 2, 1, 1, 1)],
defenseInfos: new DefenseInfo(4, 1), defenseInfo: new DefenseInfo(4, 1),
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic)] type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic, 3)]
})); }));
this.roamingMobDeck.AddItem(new MobInfo({ this.roamingMobDeck.AddItem(new MobInfo({
name: 'Lyidan, Incubus Lord', hp: 7, level: 1, rewardTokens: 2, name: 'Lyidan, Incubus Lord', hp: 7, level: 1, rewardTokens: 2,
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Rare)] type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Rare)]
})); }));
this.roamingMobDeck.AddItem(new MobInfo({ this.roamingMobDeck.AddItem(new MobInfo({
name: 'Lyidan, Incubus Lord', hp: 10, level: 3, rewardTokens: 2, name: 'Lyidan, Incubus Lord', hp: 10, level: 3, rewardTokens: 2,
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic)] type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic)]
})); }));
this.roamingMobDeck.AddItem(new MobInfo({ this.roamingMobDeck.AddItem(new MobInfo({
name: 'Lyidan, Incubus Lord', hp: 7, level: 5, rewardTokens: 0, 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)] type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic, 3)]
})); }));
this.roamingMobDeck.AddItem(new MobInfo({ this.roamingMobDeck.AddItem(new MobInfo({
name: 'The Ghoul', hp: 5, level: 1, rewardTokens: 2, name: 'The Ghoul', hp: 5, level: 1, rewardTokens: 2,
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Rare)] type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Rare)]
})); }));
this.roamingMobDeck.AddItem(new MobInfo({ this.roamingMobDeck.AddItem(new MobInfo({
name: 'The Ghoul', hp: 8, level: 3, rewardTokens: 2, name: 'The Ghoul', hp: 8, level: 3, rewardTokens: 2,
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic)] type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic)]
})); }));
this.roamingMobDeck.AddItem(new MobInfo({ this.roamingMobDeck.AddItem(new MobInfo({
name: 'The Ghoul', hp: 5, level: 5, rewardTokens: 0, name: 'The Ghoul', hp: 5, level: 5, rewardTokens: 0,
isRoamingMonster: true, fixedCarriedTreasure: [this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic), this.getTreasureDrawingItem(TreasureType.Epic)] type: MobType.RoamingMonster, fixedCarriedTreasure: [new TreasureItem(TreasureType.Epic, 3)]
})); }));
}
private initCoreGameMobs() {
CoreGameMobFactories.forEach(factory => {
for (let i = 1; i <= 5; i++) {
this.mobDeck.AddItem(factory.generate(i));
i++;
}
});
} }
// #region Public Getters And Setters (5) // #region Public Getters And Setters (5)
@ -128,26 +143,18 @@ export class MD2Service {
if (this.heros.length > 0) { if (this.heros.length > 0) {
return Math.max(...this.heros.map(h => h.level)); return Math.max(...this.heros.map(h => h.level));
} else { } else {
return this._highestPlayerLevel; return 1;
} }
} }
public set highestPlayerLevel(v: number) {
this._highestPlayerLevel = v;
}
public get playerAmount(): number { public get playerAmount(): number {
if (this.heros.length > 0) { if (this.heros.length > 0) {
return this.heros.length; return this.heros.length;
} else { } else {
return this._playerAmount; return 1;
} }
} }
public set playerAmount(v: number) {
this._playerAmount = v;
}
public get playerHero(): MD2HeroInfo { public get playerHero(): MD2HeroInfo {
return this.heros.find(h => h.playerInfo.signalRClientId == this.loginUserService.userAccess.signalRSessionId); return this.heros.find(h => h.playerInfo.signalRClientId == this.loginUserService.userAccess.signalRSessionId);
} }
@ -157,87 +164,10 @@ export class MD2Service {
// #region Public Methods (27) // #region Public Methods (27)
public addTreasure(type: TreasureType, amount: number = 1) { public addTreasure(type: TreasureType, amount: number = 1) {
let item = this.getTreasureDrawingItem(type, amount); this.treasureBag.AddItem(new TreasureItem(type, amount));
this.treasureBag.AddItem(item);
this.refreshTreasureBagSubject.next(this.treasureBag); 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() { public darknessPhase() {
this.heros.forEach(hero => { this.heros.forEach(hero => {
hero.remainActions = 3; hero.remainActions = 3;
@ -246,15 +176,15 @@ export class MD2Service {
hero.frozenToken = remainFrozenToken; hero.frozenToken = remainFrozenToken;
this.broadcastHeroInfoToOwner(hero); this.broadcastHeroInfoToOwner(hero);
}); });
this.info.roundPhase = RoundPhase.HeroPhase; this.stateService.info.roundPhase = RoundPhase.HeroPhase;
this.info.round++; this.stateService.info.round++;
if (this.darknessPhaseRule.runDarknessPhase()) { if (this.darknessPhaseRule.runDarknessPhase()) {
this.msgBoxService.show(`${NumberUtils.Ordinal(this.info.round)} Hero Phase`, { icon: ADIcon.INFO }); this.msgBoxService.show(`${NumberUtils.Ordinal(this.stateService.info.round)} Hero Phase`, { icon: ADIcon.INFO });
} }
//this.runNextPhase(); //this.runNextPhase();
} }
public drawMob(isRoamingMonster: boolean) { public spawnMob(isRoamingMonster: boolean) {
let mobDeck = null as DrawingBag<MobInfo>; let mobDeck = null as DrawingBag<MobInfo>;
let level = 1; let level = 1;
if (this.highestPlayerLevel < 3) { if (this.highestPlayerLevel < 3) {
@ -277,7 +207,7 @@ export class MD2Service {
let newSpawnMob = new MobInfo(mobDeck.DrawAndRemove(1, m => m.level == level)[0]); let newSpawnMob = new MobInfo(mobDeck.DrawAndRemove(1, m => m.level == level)[0]);
if (isRoamingMonster) { if (isRoamingMonster) {
newSpawnMob.unitRemainHp = newSpawnMob.hp * this.playerAmount; newSpawnMob.unitRemainHp = newSpawnMob.hp * this.playerAmount;
newSpawnMob.mobAmount = 1; newSpawnMob.mobAmount = 0;
} else { } else {
newSpawnMob.mobAmount = this.playerAmount + 1; newSpawnMob.mobAmount = this.playerAmount + 1;
} }
@ -292,6 +222,7 @@ export class MD2Service {
} else { } else {
this.mobs.push(newSpawnMob); this.mobs.push(newSpawnMob);
newSpawnMob.carriedTreasure = this.treasureBag.DrawAndRemove(newSpawnMob.rewardTokens); newSpawnMob.carriedTreasure = this.treasureBag.DrawAndRemove(newSpawnMob.rewardTokens);
this.refreshTreasureBagSubject.next(this.treasureBag);
} }
@ -323,61 +254,16 @@ export class MD2Service {
return this.fileService.FileList('Images/MD2/' + folderPath); 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) { public heroFullName(hero: MD2HeroInfo) {
if (!hero) return ''; if (!hero) return '';
return `${hero.playerInfo.name} (${HeroClass[hero.class]} - ${hero.name})` 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() { public initMobDecks() {
this.mobDeck = new DrawingBag(); this.mobDeck = new DrawingBag();
this.roamingMobDeck = new DrawingBag(); this.roamingMobDeck = new DrawingBag();
this.mobDeck.AddItem(new MobInfo({ name: 'Gargoyles', hp: 2, level: 1, rewardTokens: 1 })); this.initCoreGameMobs();
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(); this.initCoreGameRoamingMonsters();
} }
@ -398,7 +284,7 @@ export class MD2Service {
hero.mp += levelUpInfo.extraMp; hero.mp += levelUpInfo.extraMp;
hero.mpMaximum += levelUpInfo.extraMp; hero.mpMaximum += levelUpInfo.extraMp;
hero.exp = levelUpInfo.currentExp; hero.exp = levelUpInfo.currentExp;
this.broadcastHeroInfoToOwner(hero); this.broadcastService.broadcastHeroInfoToOwner(hero);
this.sendMsgboxMsg(hero.playerInfo.signalRClientId, { title: 'Level Up', text: 'Please do a skill level up!', icon: ADIcon.INFO }); this.sendMsgboxMsg(hero.playerInfo.signalRClientId, { title: 'Level Up', text: 'Please do a skill level up!', icon: ADIcon.INFO });
levelUpInfo = MD2Rules.checkCoreGameLevelup(hero.level, hero.exp); levelUpInfo = MD2Rules.checkCoreGameLevelup(hero.level, hero.exp);
} }
@ -416,9 +302,9 @@ export class MD2Service {
level = 3; level = 3;
} }
if (isRoamingMonsters) { if (isRoamingMonsters) {
return this.imgUrl(`Mobs/CoreGame/RoamingMonsters/${name}/${level}.png`); return this.stateService.imgUrl(`Mobs/CoreGame/RoamingMonsters/${name}/${level}.png`);
} else { } else {
return this.imgUrl(`Mobs/CoreGame/Mobs/${name}/${level}.png`); return this.stateService.imgUrl(`Mobs/CoreGame/Mobs/${name}/${level}.png`);
} }
} }
@ -440,39 +326,17 @@ export class MD2Service {
}); });
} }
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() { public runNextPhase() {
this.info.roundPhase++; this.stateService.info.roundPhase++;
switch (this.info.roundPhase) { switch (this.stateService.info.roundPhase) {
case RoundPhase.HeroPhase: case RoundPhase.HeroPhase:
this.heros.forEach(hero => { this.heros.forEach(hero => {
hero.remainActions = 3; hero.remainActions = 3;
let remainFrozenToken = Math.max(0, hero.frozenToken - hero.remainActions); let remainFrozenToken = Math.max(0, hero.frozenToken - hero.remainActions);
hero.remainActions = Math.max(0, hero.remainActions - hero.frozenToken); hero.remainActions = Math.max(0, hero.remainActions - hero.frozenToken);
hero.frozenToken = remainFrozenToken; hero.frozenToken = remainFrozenToken;
this.broadcastHeroInfoToOwner(hero); this.broadcastService.broadcastHeroInfoToOwner(hero);
}); });
break; break;
case RoundPhase.EnemyPhase: case RoundPhase.EnemyPhase:
@ -487,8 +351,8 @@ export class MD2Service {
default: break; default: break;
} }
let parameters = {}; let parameters = {};
parameters['phase'] = this.info.roundPhase; parameters['phase'] = this.stateService.info.roundPhase;
this.broadcastMessage('roundPhase', '', parameters); this.broadcastService.broadcastMessage('roundPhase', '', parameters);
} }
public sendMsgboxMsg(playerSessionId: string, msg: Partial<MessageBoxConfig>) { public sendMsgboxMsg(playerSessionId: string, msg: Partial<MessageBoxConfig>) {
@ -503,9 +367,6 @@ export class MD2Service {
}); });
} }
public treasureImage(type: TreasureType) {
return this.imgUrl(`TreasureToken/${TreasureType[type]}.png`);
}
public getTargetHerosByFilter(targetType: AttackTarget, onlyOne: boolean = false) { public getTargetHerosByFilter(targetType: AttackTarget, onlyOne: boolean = false) {
let beenAttackedHero = [] as MD2HeroInfo[]; let beenAttackedHero = [] as MD2HeroInfo[];
switch (targetType) { switch (targetType) {

View File

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

View File

@ -19,10 +19,10 @@
font-family: "Massive Darkness 2", sans-serif !important; font-family: "Massive Darkness 2", sans-serif !important;
//font-size: 50px; //font-size: 50px;
&.atk::before { &.attack::before {
content: "A"; content: "A";
} }
&.def::before { &.defense::before {
content: "B"; content: "B";
} }
&.mana::before { &.mana::before {
@ -34,7 +34,7 @@
&.enemySkill::before { &.enemySkill::before {
content: "E"; content: "E";
} }
&.enemyAtk::before { &.enemyAttack::before {
content: "F"; content: "F";
} }
&.reroll::before { &.reroll::before {