From 068dca14f9c1225f596fdfa70db1e754541704f4 Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Thu, 4 May 2023 16:03:23 +0800 Subject: [PATCH] =?UTF-8?q?=E8=8C=83=E5=9B=B4=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 11 +- src/plugin/game/damage.ts | 147 ++++++++++++++++++++++++- src/plugin/game/index.js | 38 +++---- src/plugin/game/range.ts | 100 +++++++++++++++++ src/plugin/game/{utils.js => utils.ts} | 21 ++-- src/types/plugin.d.ts | 11 +- 6 files changed, 287 insertions(+), 41 deletions(-) create mode 100644 src/plugin/game/range.ts rename src/plugin/game/{utils.js => utils.ts} (69%) diff --git a/README.md b/README.md index fd463c5..a9db5e5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## 项目结构 -`public`: mota-js 样板所在目录,该塔对样板的目录进行了一定的魔改,其中插件全部移动到`project/plugin`文件夹中,并使用了`es模块化` +`public`: mota-js 样板所在目录,该塔对样板的目录进行了一定的魔改,其中插件全部移动到`src/plugin/game`文件夹中,并使用了`es模块化` `src`: 与 ui、特效等与游戏进程无关的插件所在目录。其中包含以下内容: @@ -45,7 +45,7 @@ 1. 运行`vue-tsc`检查类型是否正确 2. 运行`vite`的构建工具,打包除`public`外的内容 -3. 运行`script/build.ts`,首先去除未使用的文件(即全塔属性中未注册的文件),然后压缩字体,再用`rollup`及`babel`压缩插件与`main.js` +3. 运行`script/build.ts`,首先去除未使用的文件(即全塔属性中未注册的文件),然后压缩字体,再用`rollup` `terser`及`babel`压缩插件与`main.js` ## 热重载说明 @@ -53,10 +53,9 @@ 1. `vite`热重载 2. 楼层热重载 -3. 插件热重载 -4. 脚本编辑热重载 -5. 道具、怪物、图块属性热重载 -6. styles.css +3. 脚本编辑热重载 +4. 道具、怪物、图块属性热重载 +5. styles.css 以下内容修改后会自动刷新页面 diff --git a/src/plugin/game/damage.ts b/src/plugin/game/damage.ts index 70ad5d3..1b458a8 100644 --- a/src/plugin/game/damage.ts +++ b/src/plugin/game/damage.ts @@ -1,9 +1,148 @@ -/// +import { Range, RangeCollection } from './range'; +import { ensureArray } from './utils'; -export class EnemyCollection {} +interface HaloType { + square: { + x: number; + y: number; + d: number; + }; +} -export class Enemy {} +type HaloFn = (enemy: Enemy) => Enemy; + +export const haloSpecials: number[] = [21, 25, 26, 27]; + +export class EnemyCollection implements RangeCollection { + floorId: FloorIds; + list: DamageEnemy[] = []; + + range: Range = new Range(this); + + constructor(floorId: FloorIds) { + this.floorId = floorId; + } + + extract() { + core.extractBlocks(this.floorId); + core.status.maps[this.floorId].blocks.forEach(v => { + if (v.event.cls !== 'enemy48' && v.event.cls !== 'enemys') return; + const enemy = core.material.enemys[v.event.id as EnemyIds]; + this.list.push(new DamageEnemy(enemy)); + }); + } + + calAttribute(noCache: boolean = false) {} + + calDamage(noCache: boolean = false) {} + + /** + * 向怪物施加光环 + * @param type 光环的范围类型 + * @param data 光环范围信息 + * @param halo 光环效果函数 + * @param recursion 是否递归施加,一般只有在光环预平衡阶段会使用到 + */ + applyHalo( + type: K, + data: HaloType[K], + halo: HaloFn | HaloFn[], + recursion: boolean = false + ) { + const arr = ensureArray(halo); + } + + /** + * 预平衡光环 + */ + preBalanceHalo() {} +} + +export class DamageEnemy { + id: T; + x?: number; + y?: number; + floorId?: FloorIds; + enemy: Enemy; + + /** 计算特殊属性但不计算光环的属性 */ + noHaloInfo?: Enemy; + /** 既计算特殊属性又计算光环的属性 */ + realInfo?: Enemy; + /** 向其他怪提供过的光环 */ + providedHalo: number[] = []; + + constructor(enemy: Enemy, x?: number, y?: number, floorId?: FloorIds) { + this.id = enemy.id; + this.enemy = enemy; + this.x = x; + this.y = y; + this.floorId = floorId; + } + + reset() { + delete this.noHaloInfo; + delete this.realInfo; + } + + calAttribute() {} + + getHaloSpecials(): number[] { + if (!this.floorId) return []; + if (!core.has(this.x) || !core.has(this.y)) return []; + const special = this.realInfo?.special ?? this.enemy.special; + const filter = special.filter(v => { + return haloSpecials.includes(v) && !this.providedHalo.includes(v); + }); + if (filter.length === 0) return []; + const collection = core.status.maps[this.floorId].enemy; + if (!collection) { + throw new Error( + `Unexpected undefined of enemy collection in floor ${this.floorId}.` + ); + } + return filter; + } + + /** + * 光环预提供,用于平衡所有怪的光环属性,避免出现不同情况下光环效果不一致的现象 + */ + preProvideHalo() {} + + /** + * 向其他怪提供光环 + */ + provideHalo() { + const speical = this.getHaloSpecials(); + + const square7: HaloFn[] = []; + + if (speical.includes(21)) { + } + } + + /** + * 接受其他怪的光环 + */ + injectHalo(halo: HaloFn) {} + + calDamage() {} +} + +declare global { + interface PluginDeclaration { + damage: { + Enemy: typeof DamageEnemy; + Collection: typeof EnemyCollection; + }; + } + + interface Floor { + enemy: EnemyCollection; + } +} core.plugin.damage = { - Enemy + Enemy: DamageEnemy, + Collection: EnemyCollection }; diff --git a/src/plugin/game/index.js b/src/plugin/game/index.js index 85507c6..b30632e 100644 --- a/src/plugin/game/index.js +++ b/src/plugin/game/index.js @@ -1,22 +1,22 @@ -import './fiveLayer.js'; -import './heroFourFrames.js'; -import './hotReload.js'; -import './itemDetail.js'; -import './popup.js'; -import './replay.js'; -import './ui.js'; -import * as halo from './halo.js'; -import * as hero from './hero.js'; -import * as loopMap from './loopMap.js'; -import * as remainEnemy from './remainEnemy.js'; -import * as removeMap from './removeMap.js'; -import * as shop from './shop.js'; -import * as skill from './skills.js'; -import * as skillTree from './skillTree.js'; -import * as study from './study.js'; -import * as towerBoss from './towerBoss.js'; -import * as utils from './utils.js'; -import * as chase from './chase.js'; +import './fiveLayer'; +import './heroFourFrames'; +import './hotReload'; +import './itemDetail'; +import './popup'; +import './replay'; +import './ui'; +import * as halo from './halo'; +import * as hero from './hero'; +import * as loopMap from './loopMap'; +import * as remainEnemy from './remainEnemy'; +import * as removeMap from './removeMap'; +import * as shop from './shop'; +import * as skill from './skills'; +import * as skillTree from './skillTree'; +import * as study from './study'; +import * as towerBoss from './towerBoss'; +import * as utils from './utils'; +import * as chase from './chase'; import * as damage from './damage'; export { diff --git a/src/plugin/game/range.ts b/src/plugin/game/range.ts new file mode 100644 index 0000000..8ef58fe --- /dev/null +++ b/src/plugin/game/range.ts @@ -0,0 +1,100 @@ +type RangeScanFn> = ( + collection: Range, + data: any +) => C[]; +type InRangeFn> = ( + collection: Range, + data: any, + item: Partial +) => boolean; + +interface RangeType> { + scan: RangeScanFn; + inRange: InRangeFn; +} + +export interface RangeCollection> { + list: I[]; + range: Range; +} + +export class Range> { + collection: RangeCollection; + cache: Record = {}; + + static rangeType: Record>> = {}; + + constructor(collection: RangeCollection) { + this.collection = collection; + } + + /** + * 扫描 collection 中在范围内的物品 + * @param type 范围类型 + * @param data 范围数据 + * @returns 在范围内的物品列表 + */ + scan(type: string, data: any): C[] { + const t = Range.rangeType[type]; + if (!t) { + throw new Error(`Unknown range type: ${type}.`); + } + return t.scan(this, data) as C[]; + } + + inRange(type: string, data: any, item: Partial) { + const t = Range.rangeType[type]; + if (!t) { + throw new Error(`Unknown range type: ${type}.`); + } + return t.inRange(this, data, item); + } + + clearCache() { + this.cache = {}; + } + + static registerRangeType( + type: string, + scan: RangeScanFn>, + inRange: InRangeFn> + ) { + Range.rangeType[type] = { + scan, + inRange + }; + } +} + +// ----- 默认的范围类型 + +// 方形区域 +Range.registerRangeType( + 'square', + (col, { x, y, d }) => { + const cache = (col.cache.square ??= {}); + const index = `${x},${y},${d}`; + if (index in cache) return cache[index]; + const list = col.collection.list; + + const r = Math.floor(d); + + return (cache[index] = list.filter(v => { + return ( + core.has(v.x) && + core.has(v.y) && + Math.abs(v.x - x) <= r && + Math.abs(v.y - y) <= r + ); + })); + }, + (col, { x, y, d }, item) => { + const r = Math.floor(d / 2); + return ( + core.has(item.x) && + core.has(item.y) && + Math.abs(item.x - x) <= r && + Math.abs(item.y - y) <= r + ); + } +); diff --git a/src/plugin/game/utils.js b/src/plugin/game/utils.ts similarity index 69% rename from src/plugin/game/utils.js rename to src/plugin/game/utils.ts index 5d813f7..b9e8b52 100644 --- a/src/plugin/game/utils.js +++ b/src/plugin/game/utils.ts @@ -2,10 +2,10 @@ /** * 滑动数组 - * @param {any[]} arr - * @param {number} delta + * @param arr + * @param delta */ -export function slide(arr, delta) { +export function slide(arr: T[], delta: number): T[] { if (delta === 0) return arr; delta %= arr.length; if (delta > 0) { @@ -16,27 +16,29 @@ export function slide(arr, delta) { arr.push(...arr.splice(0, -delta)); return arr; } + return arr; } -export function backDir(dir) { +export function backDir(dir: Dir): Dir { return { up: 'down', down: 'up', left: 'right', right: 'left' - }[dir]; + }[dir] as Dir; } -export function has(v) { +export function has(v: T): v is NonNullable { return v !== null && v !== void 0; } -export function maxGameScale(n = 0) { +export function maxGameScale(n: number = 0) { const index = core.domStyle.availableScale.indexOf(core.domStyle.scale); core.control.setDisplayScale( core.domStyle.availableScale.length - 1 - index - n ); if (!core.isPlaying() && core.flags.enableHDCanvas) { + // @ts-ignore core.domStyle.ratio = Math.max( window.devicePixelRatio || 1, core.domStyle.scale @@ -45,6 +47,11 @@ export function maxGameScale(n = 0) { } } +export function ensureArray(arr: T): T extends any[] ? T : T[] { + // @ts-ignore + return arr instanceof Array ? arr : [arr]; +} + core.plugin.utils = { slide, backDir, diff --git a/src/types/plugin.d.ts b/src/types/plugin.d.ts index e81ca88..37d3d11 100644 --- a/src/types/plugin.d.ts +++ b/src/types/plugin.d.ts @@ -30,7 +30,6 @@ interface PluginDeclaration hero: GamePluginHeroRealStatus; replay: PluginReplay; chase: PluginChase; - damage: PluginDamage; skills: Record; skillEffects: SkillEffects; @@ -94,6 +93,12 @@ interface PluginDeclaration * 重置变量的设置信息 */ resetFlagSettings(): void; + + /** + * 判定一个值是否不是undefined或null + * @param value 要判断的值 + */ + has(value: T): value is NonNullable; } interface GamePluginUtils { @@ -457,10 +462,6 @@ interface Skill { effect: string[]; } -interface PluginDamage { - Enemy: new () => DamageEnemy; -} - interface DamageEnemy {} type Forward = {