This commit is contained in:
Chris Chen
2025-11-06 21:47:04 -08:00
parent 349510db56
commit d8db9f650b
11 changed files with 166 additions and 152 deletions
@@ -8,99 +8,6 @@ import { MobSkillType } from "../../massive-darkness2.model.boss";
import { MD2DiceSet, MD2MobSkill } from "../../massive-darkness2.db.model";
const MD2_IMG_URL = (id: string = null) => { return `${environment.apiUrl}/Files/Images/MD2/Mobs${(id ? `${encodeURI(id)}` : '')}` }
const CORE_GAME_MOB_LEVEL = [
new MobInfo({
name: 'Andra', level: 1, hp: 5,
attackInfos: [new AttackInfo(MD2Icon.Melee, 1, 0, 0, 1), new AttackInfo(MD2Icon.Range, 1, 0, 0, 1)],
defenseInfo: { blue: 2, black: 1 } as MD2DiceSet,
}),
new MobInfo({
name: 'Andra', level: 3, hp: 7,
attackInfos: [new AttackInfo(MD2Icon.Melee, 1, 1, 0, 1), new AttackInfo(MD2Icon.Range, 1, 1, 0, 1)],
defenseInfo: { blue: 3, black: 1 } as MD2DiceSet,
}),
new MobInfo({
name: 'Andra', level: 5, hp: 5,
attackInfos: [new AttackInfo(MD2Icon.Melee, 1, 2, 0, 1), new AttackInfo(MD2Icon.Range, 1, 2, 0, 1)],
defenseInfo: { blue: 5, black: 1 } as MD2DiceSet,
}),
new MobInfo({
name: 'Ytheria, Undead Queen', level: 1, hp: 4,
attackInfos: [new AttackInfo(MD2Icon.Melee, 1), new AttackInfo(MD2Icon.Range, 2, 0, 0, 1)],
defenseInfo: { blue: 1, black: 1 } as MD2DiceSet,
}),
new MobInfo({
name: 'Ytheria, Undead Queen', level: 3, hp: 6,
attackInfos: [new AttackInfo(MD2Icon.Melee, 0, 1), new AttackInfo(MD2Icon.Range, 1, 1, 0, 1)],
defenseInfo: { blue: 2, black: 1 } as MD2DiceSet,
}),
new MobInfo({
name: 'Ytheria, Undead Queen', level: 5, hp: 8,
attackInfos: [new AttackInfo(MD2Icon.Melee, 2, 1), new AttackInfo(MD2Icon.Range, 2, 1, 0, 1)],
defenseInfo: { blue: 4, black: 1 } as MD2DiceSet,
}),
new MobInfo({
name: 'Lyidan, Incubus Lord', level: 1, hp: 7,
attackInfos: [new AttackInfo(MD2Icon.Melee, 0, 1, 0, 2)],
defenseInfo: { blue: 2, black: 1 } as MD2DiceSet,
}),
new MobInfo({
name: 'Lyidan, Incubus Lord', level: 3, hp: 10,
attackInfos: [new AttackInfo(MD2Icon.Melee, 0, 2, 0, 1)],
defenseInfo: { blue: 2, black: 1 } as MD2DiceSet,
}),
new MobInfo({
name: 'Lyidan, Incubus Lord', level: 5, hp: 12,
attackInfos: [new AttackInfo(MD2Icon.Melee, 2, 2, 0, 1)],
defenseInfo: { blue: 4, black: 1 } as MD2DiceSet,
}),
new MobInfo({
name: 'The Ghoul', level: 1, hp: 5,
attackInfos: [new AttackInfo(MD2Icon.Melee, 0, 1, 0, 1)],
defenseInfo: { blue: 2, black: 1 } as MD2DiceSet,
}),
new MobInfo({
name: 'The Ghoul', level: 3, hp: 8,
attackInfos: [new AttackInfo(MD2Icon.Melee, 0, 2, 0, 2)],
defenseInfo: { blue: 3, black: 1 } as MD2DiceSet,
}),
new MobInfo({
name: 'The Ghoul', level: 5, hp: 10,
attackInfos: [new AttackInfo(MD2Icon.Melee, 0, 3, 0, 3)],
defenseInfo: { blue: 4, black: 1 } as MD2DiceSet,
}),
new MobInfo({
name: 'Balrog', level: 1, hp: 5,
attackInfos: [
new AttackInfo(MD2Icon.Magic, 0, 1, 0, 2),
],
defenseInfo: { blue: 2, black: 1 } as MD2DiceSet,
}),
new MobInfo({
name: 'Balrog', level: 3, hp: 8,
attackInfos: [
new AttackInfo(MD2Icon.Magic, 0, 2, 0, 2),
],
defenseInfo: { blue: 3, black: 1 } as MD2DiceSet,
}),
new MobInfo({
name: 'Balrog', level: 5, hp: 10,
attackInfos: [
new AttackInfo(MD2Icon.Magic, 0, 3, 0, 2),
],
defenseInfo: { blue: 4, black: 1 } as MD2DiceSet,
}),
]
export abstract class CoreGameRMFactory implements IMobFactory {
abstract mobName: string;
@@ -192,7 +99,7 @@ export class RMUndeadQueenFactory extends CoreGameRMFactory {
}
});
}
});;
});
}
this.mob.skills = [
@@ -377,7 +377,6 @@ export class MobInfo implements IDrawingItem {
return 0;
}
}
public actionSubject: Subject<string>;
public activateFunction?: (mob: MobInfo, msgBoxService: MsgBoxService, heros: MD2HeroInfo[]) => void;
}
export interface MD2HeroProfile {
@@ -21,7 +21,7 @@
</md2-icon>
</label> -->
<div *ngFor="let skill of mob.skills" class=" g-brd-bottom--dashed g-brd-gray-light-v2">
<div *ngIf="showAllSkill || skill.uiDisplay">
<div *ngIf="skill.uiDisplay">
<label for='' class='MD2text g-font-size-22 label mb-2'>
{{MobSkillType[skill.type]}} {{skill.skillRoll}} <md2-icon icon="enemySkill" size="md"></md2-icon>
</label>
@@ -39,7 +39,8 @@ export class MobCombatInfoComponent implements OnInit {
element.uiDisplay = [MobSkillType.Combat, MobSkillType.Defense].includes(element.type);
break;
case MobDlgType.PreView:
element.uiDisplay = true;
case MobDlgType.Dashboard:
element.uiDisplay = [MobSkillType.Combat, MobSkillType.Attack, MobSkillType.Defense].includes(element.type);
break;
case MobDlgType.Spawn:
element.uiDisplay = false;
@@ -34,11 +34,21 @@
<ng-container *ngIf="showAdjustment">
<div class="row">
<div class="col-md-6">
<adj-number-input name="mob{{mob.name}}" [ngModel]="mob.unitRemainHp" [maximum]="mob.hp" minimum="1"
title="Target Unit HP" (hitChange)="adjustMobHp($event)" (hitMinimum)="adjustMobHp(-1)"
(hitMaximum)="adjustMobHp(1)">
</adj-number-input>
<adj-number-input name="mob{{mob.name}}" [ngModel]="mob.unitRemainHp" [maximum]="mob.hp" minimum="1"
title="Target Unit HP" (hitChange)="adjustMobHp($event)" (hitMinimum)="adjustMobHp(-1)"
(hitMaximum)="adjustMobHp(1)">
</adj-number-input>
</div>
<div class="col-md-6" *ngIf="mode==MobDlgType.Activating">
<adj-number-input name="mob{{mob.name}}Actions" [(ngModel)]="mob.actions" title="Remaining Actions"
hideIncreaseBtn>
</adj-number-input>
</div>
</div>
</ng-container>
@@ -11,7 +11,6 @@ import { StateService } from '../../../services/state.service';
import { ADButtons, ADIcon } from '../../../ui/alert-dlg/alert-dlg.model';
import { NumberUtils } from '../../../utilities/number-utils';
import { StringUtils } from '../../../utilities/string-utils';
import { CoreGameRMFactories } from '../factorys/roamingMonsters/CoreGame';
import { DrawingBag, DrawingItem, MD2Icon, MobDlgType, MobInfo, TreasureType } from '../massive-darkness2.model';
import { MD2Base, MD2ComponentBase } from '../MD2Base';
import { SpawnMobDlgComponent } from './spawn-mob-dlg/spawn-mob-dlg.component';
@@ -89,7 +88,7 @@ export class MobsComponent extends MD2ComponentBase implements OnInit {
}
spawnSpecificMob() {
let mobOptions = this.isRoamingMonster ? CoreGameRMFactories.map(f => new DropDownOption(f.mobName, f.mobName)) : this.md2Service.allMobInfos.map(f => new DropDownOption(f.name, f.name));
let mobOptions = this.isRoamingMonster ? this.md2Service.allRoamingMonsterInfos.map(f => new DropDownOption(f.name, f.name)) : this.md2Service.allMobInfos.map(f => new DropDownOption(f.name, f.name));
this.msgBoxService.showInputbox('Spawn', '',
{
inputType: 'dropdown', dropDownOptions: mobOptions,
@@ -5,20 +5,21 @@
</nb-card-header>
<nb-card-body>
<div class="row no-gutters">
<div class="col-md-7 g-height-90vh">
<div class="col-md-7">
<!-- <img src="{{mob.imageUrl}}" class="g-width-90x"> -->
<md2-mob-stand-info [mob]="mob" [mode]="mode"></md2-mob-stand-info>
<div *ngIf="activeSkill">
<div class="alert alert-warning" role="alert">
<h4>{{activeSkill.name}}</h4>
<div [innerHtml]="activeSkill.description"></div>
</div>
</div>
</div>
<div class="col-md-5">
<md2-mob-detail-info [mob]="mob" [mode]="mode">
</md2-mob-detail-info>
<div *ngIf="actionInfoHtml">
<div class="alert alert-warning" role="alert" [innerHtml]="actionInfoHtml">
</div>
</div>
<ng-container *ngIf="mode==MobDlgType.Spawn&&mob.type==MobType.Mob">
<div class="row form-group mt-2">
@@ -1,7 +1,7 @@
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { NbDialogRef } from '@nebular/theme';
import { first } from 'rxjs/operators';
import { concatMap, find, defaultIfEmpty, first, map, switchMap } from 'rxjs/operators';
import { FileService } from '../../../../services/file.service';
import { MD2Service } from '../../../../services/MD2/md2.service';
import { MsgBoxService } from '../../../../services/msg-box.service';
@@ -9,6 +9,12 @@ import { StateService } from '../../../../services/state.service';
import { StringUtils } from '../../../../utilities/string-utils';
import { AttackInfo, AttackTarget, AttackType, MD2HeroInfo, MD2Icon, MobDlgType, MobInfo, MobType } from '../../massive-darkness2.model';
import { MD2ComponentBase } from '../../MD2Base';
import { ADButtons, ADIcon } from '../../../../ui/alert-dlg/alert-dlg.model';
import { MobSkillType } from '../../massive-darkness2.model.boss';
import { Observable, from, of } from 'rxjs';
import { MD2MobSkill } from '../../massive-darkness2.db.model';
type SkillResolutionResult = { result: boolean; skill: MD2MobSkill | null };
@Component({
selector: 'ngx-spawn-mob-dlg',
@@ -38,16 +44,34 @@ export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit {
) {
super(md2Service, stateService, route, cdRef);
}
activeSkill: MD2MobSkill = null;;
ngOnInit(): void {
//this.mob = new MobInfo(this.mob);
if (this.mode == MobDlgType.Spawn && this.mob.type == MobType.Mob) {
this.mob.attackInfos = [new AttackInfo(MD2Icon.Melee), new AttackInfo(MD2Icon.Range), new AttackInfo(MD2Icon.Magic)];
} else if (this.mode == MobDlgType.Activating) {
if (this.mob.actionSubject) {
this.mob.actionSubject.pipe(first()).subscribe(result => {
this.actionInfoHtml = result;
if (this.mob.skills) {
//this.mobActivate(this.mob, this.md2Service.info.heros);
//this.mob.activateFunction(this.mob, this.md2Service.msgBoxService, this.md2Service.info.heros);
this.mobActivate(this.mob, this.md2Service.info.heros).pipe(first()).subscribe(result => {
if (result && result.skill) {
this.activeSkill = result.skill;
//this.actionInfoHtml = `<h4>${result.skill.name}</h4><div>${result.skill.description}</div>`;
}
if (this.mob.type == MobType.Mob) {
this.mob.actions = 2;
} else {
if (result && result.skill) {
this.mob.actions = 1;
} else {
this.mob.actions = 2;
this.activeSkill = { name: 'Normal Action', description: '${this.mob.name} Gains 2 Actions.' } as MD2MobSkill;
//this.actionInfoHtml = `${this.mob.name} Gains 2 Actions.`;
}
}
});
this.mob.activateFunction(this.mob, this.md2Service.msgBoxService, this.md2Service.info.heros);
}
}
this.mob.uiWounds = 0;
@@ -55,6 +79,52 @@ export class SpawnMobDlgComponent extends MD2ComponentBase implements OnInit {
this.mob.uiFrozenTokens = 0;
this.initTitleHtml();
}
mobActivate(mob: MobInfo, heros: MD2HeroInfo[]): Observable<SkillResolutionResult> {
switch (mob.type) {
case MobType.Mob:
return of({ result: false, skill: null } as SkillResolutionResult);
case MobType.RoamingMonster:
if (!mob.skills || mob.skills.length === 0) {
return of({ result: false, skill: null } as SkillResolutionResult);
}
const orderedSkills = mob.skills
.filter(s => [MobSkillType.ConditionalSkill, MobSkillType.OtherWiseSkill].includes(s.type))
.sort((a, b) => (a.seq ?? Number.MAX_SAFE_INTEGER) - (b.seq ?? Number.MAX_SAFE_INTEGER));
if (orderedSkills.length === 0) {
return of({ result: false, skill: null } as SkillResolutionResult);
}
return from(orderedSkills).pipe(
concatMap(skill => this.resolveSkillPrompt(skill)),
find(resolution => resolution.result === true),
defaultIfEmpty({ result: false, skill: null } as SkillResolutionResult)
);
case MobType.Boss:
default:
return of({ result: false, skill: null } as SkillResolutionResult);
}
}
private resolveSkillPrompt(skill: MD2MobSkill): Observable<SkillResolutionResult> {
if (skill.type === MobSkillType.OtherWiseSkill) {
return of({ result: true, skill } as SkillResolutionResult);
}
const title = skill.name || 'Resolve Skill?';
const prompt = skill.skillCondition || skill.description || 'Resolve this skill?';
return this.msgBoxService.show(title, {
text: prompt,
icon: ADIcon.QUESTION,
buttons: ADButtons.YesNo
}).pipe(
map(answer => ({ result: !!answer, skill } as SkillResolutionResult))
);
}
submit() {
if (this.mode == MobDlgType.Spawn) {