Files
noname-shuying/mengsan/runtime/monster.js
T

97 lines
4.9 KiB
JavaScript

import { createCardData } from "./card-data.js";
// 梦三怪物规则只作用于敌方单位;子类可覆盖构造参数或 createDeck()。
const TIER_RULES = Object.freeze({
normal: Object.freeze({ draw: 1, handLimit: 3, energy: 2 }),
elite: Object.freeze({ draw: 2, handLimit: 5, energy: 3 }),
boss: Object.freeze({ draw: 3, handLimit: 7, energy: 4 }),
});
const BASE_DECKS = Object.freeze({
normal: Object.freeze(["sha", "sha", "sha", "sha", "shan", "shan", "tao", "jiu", "guohe", "sha"]),
elite: Object.freeze(["sha", "sha", "sha", "sha", "shan", "shan", "shan", "tao", "jiu", "juedou", "wuzhong", "guohe"]),
boss: Object.freeze(["sha", "sha", "sha", "sha", "sha", "shan", "shan", "shan", "tao", "tao", "jiu", "jiu", "juedou", "wuzhong", "guohe"]),
});
const HEALTH = Object.freeze({
mengsan_soldier_shuying: 24,
mengsan_flyconid_shuying: 48,
mengsan_raider_brute_shuying: 32,
mengsan_raider_assassin_shuying: 21,
mengsan_raider_axe_shuying: 21,
mengsan_raider_crossbow_shuying: 20,
mengsan_raider_tracker_shuying: 23,
re_xiahoudun: 30, re_zhangliao: 28, re_xuzhu: 34, re_lvbu: 120,
re_ganning: 39, re_zhangfei: 42, re_huangzhong: 37, re_weiyan: 40,
re_guanyu: 65, dianwei: 62, shen_guanyu: 165,
re_simayi: 47, re_sunben: 49, dongzhuo: 55, re_yuanshao: 52,
shen_zhaoyun: 76, shen_simayi: 80, shen_caocao: 210,
});
const validDeckCard = entry => typeof entry === "string" ? Boolean(entry) : Boolean(entry &&
typeof entry.name === "string" && entry.name &&
(entry.suit == null || ["spade", "heart", "club", "diamond"].includes(entry.suit)) &&
(entry.number == null || Number.isInteger(entry.number) && entry.number >= 1 && entry.number <= 13));
export class Monster {
constructor({ character, tier = "normal", hp, deck, draw, handLimit, energy } = {}) {
if (!TIER_RULES[tier]) throw new Error("梦三怪物阶级无效:" + tier);
if (typeof character !== "string" || !character) throw new Error("梦三怪物缺少武将ID");
const defaults = TIER_RULES[tier];
this.character = character;
this.tier = tier;
this.hp = hp ?? HEALTH[character] ?? (tier === "boss" ? 110 : tier === "elite" ? 60 : 32);
this.draw = draw ?? defaults.draw;
this.handLimit = handLimit ?? defaults.handLimit;
this.energy = energy ?? defaults.energy;
this.deck = deck || BASE_DECKS[tier];
if (![this.hp, this.draw, this.handLimit, this.energy].every(value => Number.isInteger(value) && value > 0)) throw new Error("梦三怪物数值无效");
if (!Array.isArray(this.deck) || !this.deck.length || this.deck.some(entry => !validDeckCard(entry))) throw new Error("梦三怪物牌堆无效");
}
createDeck(prefix) {
return this.deck.map((entry, index) => {
const card = typeof entry === "string" ? { name: entry } : entry;
return createCardData({
id: `${prefix}_${index}`, name: card.name,
suit: card.suit || ["spade", "heart", "club", "diamond"][index % 4],
number: card.number || index % 13 + 1,
nature: card.nature || null, affixes: Array.isArray(card.affixes) ? card.affixes.slice() : [], upgrade: card.upgrade || 0,
}, { enemy: true });
});
}
}
// 专属怪物示例:以后可在子类中覆写任意默认行为。
export class OpeningSoldier extends Monster {
constructor(options = {}) { super({ character: "mengsan_soldier_shuying", tier: "normal", hp: 24, ...options }); }
}
export class Flyconid extends Monster {
constructor(options = {}) { super({ ...options, character: "mengsan_flyconid_shuying", tier: "normal", hp: 48 }); }
}
const raiderClass = (character, hp) => class extends Monster {
constructor(options = {}) { super({ ...options, character, tier: "normal", hp }); }
};
export const RaiderBrute = raiderClass("mengsan_raider_brute_shuying", 32);
export const RaiderAssassin = raiderClass("mengsan_raider_assassin_shuying", 21);
export const RaiderAxe = raiderClass("mengsan_raider_axe_shuying", 21);
export const RaiderCrossbow = raiderClass("mengsan_raider_crossbow_shuying", 20);
export const RaiderTracker = raiderClass("mengsan_raider_tracker_shuying", 23);
// 新怪物子类在这里注册,未注册的角色使用 Monster 的阶级默认值。
export const MONSTER_TYPES = Object.freeze({
mengsan_soldier_shuying: OpeningSoldier,
mengsan_flyconid_shuying: Flyconid,
mengsan_raider_brute_shuying: RaiderBrute,
mengsan_raider_assassin_shuying: RaiderAssassin,
mengsan_raider_axe_shuying: RaiderAxe,
mengsan_raider_crossbow_shuying: RaiderCrossbow,
mengsan_raider_tracker_shuying: RaiderTracker,
});
export function createMonster(spec) {
const Type = Object.hasOwn(MONSTER_TYPES, spec.character) ? MONSTER_TYPES[spec.character] : Monster;
return new Type(spec);
}
export { TIER_RULES, BASE_DECKS };