mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-18 11:59:26 +08:00
范围类
This commit is contained in:
parent
2e7e64b399
commit
068dca14f9
11
README.md
11
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
|
||||
|
||||
以下内容修改后会自动刷新页面
|
||||
|
||||
|
@ -1,9 +1,148 @@
|
||||
///<reference path="../../../src/types/core.d.ts" />
|
||||
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<T extends EnemyIds = EnemyIds> = (enemy: Enemy<T>) => Enemy<T>;
|
||||
|
||||
export const haloSpecials: number[] = [21, 25, 26, 27];
|
||||
|
||||
export class EnemyCollection implements RangeCollection<DamageEnemy> {
|
||||
floorId: FloorIds;
|
||||
list: DamageEnemy[] = [];
|
||||
|
||||
range: Range<DamageEnemy> = 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<K extends keyof HaloType>(
|
||||
type: K,
|
||||
data: HaloType[K],
|
||||
halo: HaloFn | HaloFn[],
|
||||
recursion: boolean = false
|
||||
) {
|
||||
const arr = ensureArray(halo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预平衡光环
|
||||
*/
|
||||
preBalanceHalo() {}
|
||||
}
|
||||
|
||||
export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
id: T;
|
||||
x?: number;
|
||||
y?: number;
|
||||
floorId?: FloorIds;
|
||||
enemy: Enemy<T>;
|
||||
|
||||
/** 计算特殊属性但不计算光环的属性 */
|
||||
noHaloInfo?: Enemy<T>;
|
||||
/** 既计算特殊属性又计算光环的属性 */
|
||||
realInfo?: Enemy<T>;
|
||||
/** 向其他怪提供过的光环 */
|
||||
providedHalo: number[] = [];
|
||||
|
||||
constructor(enemy: Enemy<T>, 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<T>) {}
|
||||
|
||||
calDamage() {}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface PluginDeclaration {
|
||||
damage: {
|
||||
Enemy: typeof DamageEnemy;
|
||||
Collection: typeof EnemyCollection;
|
||||
};
|
||||
}
|
||||
|
||||
interface Floor {
|
||||
enemy: EnemyCollection;
|
||||
}
|
||||
}
|
||||
|
||||
core.plugin.damage = {
|
||||
Enemy
|
||||
Enemy: DamageEnemy,
|
||||
Collection: EnemyCollection
|
||||
};
|
||||
|
@ -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 {
|
||||
|
100
src/plugin/game/range.ts
Normal file
100
src/plugin/game/range.ts
Normal file
@ -0,0 +1,100 @@
|
||||
type RangeScanFn<C extends Partial<Loc>> = (
|
||||
collection: Range<C>,
|
||||
data: any
|
||||
) => C[];
|
||||
type InRangeFn<C extends Partial<Loc>> = (
|
||||
collection: Range<C>,
|
||||
data: any,
|
||||
item: Partial<Loc>
|
||||
) => boolean;
|
||||
|
||||
interface RangeType<C extends Partial<Loc>> {
|
||||
scan: RangeScanFn<C>;
|
||||
inRange: InRangeFn<C>;
|
||||
}
|
||||
|
||||
export interface RangeCollection<I extends Partial<Loc>> {
|
||||
list: I[];
|
||||
range: Range<I>;
|
||||
}
|
||||
|
||||
export class Range<C extends Partial<Loc>> {
|
||||
collection: RangeCollection<C>;
|
||||
cache: Record<string, any> = {};
|
||||
|
||||
static rangeType: Record<string, RangeType<Partial<Loc>>> = {};
|
||||
|
||||
constructor(collection: RangeCollection<C>) {
|
||||
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<Loc>) {
|
||||
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<Partial<Loc>>,
|
||||
inRange: InRangeFn<Partial<Loc>>
|
||||
) {
|
||||
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
|
||||
);
|
||||
}
|
||||
);
|
@ -2,10 +2,10 @@
|
||||
|
||||
/**
|
||||
* 滑动数组
|
||||
* @param {any[]} arr
|
||||
* @param {number} delta
|
||||
* @param arr
|
||||
* @param delta
|
||||
*/
|
||||
export function slide(arr, delta) {
|
||||
export function slide<T>(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<T>(v: T): v is NonNullable<T> {
|
||||
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<T>(arr: T): T extends any[] ? T : T[] {
|
||||
// @ts-ignore
|
||||
return arr instanceof Array ? arr : [arr];
|
||||
}
|
||||
|
||||
core.plugin.utils = {
|
||||
slide,
|
||||
backDir,
|
11
src/types/plugin.d.ts
vendored
11
src/types/plugin.d.ts
vendored
@ -30,7 +30,6 @@ interface PluginDeclaration
|
||||
hero: GamePluginHeroRealStatus;
|
||||
replay: PluginReplay;
|
||||
chase: PluginChase;
|
||||
damage: PluginDamage;
|
||||
|
||||
skills: Record<Chapter, Skill[]>;
|
||||
skillEffects: SkillEffects;
|
||||
@ -94,6 +93,12 @@ interface PluginDeclaration
|
||||
* 重置变量的设置信息
|
||||
*/
|
||||
resetFlagSettings(): void;
|
||||
|
||||
/**
|
||||
* 判定一个值是否不是undefined或null
|
||||
* @param value 要判断的值
|
||||
*/
|
||||
has<T>(value: T): value is NonNullable<T>;
|
||||
}
|
||||
|
||||
interface GamePluginUtils {
|
||||
@ -457,10 +462,6 @@ interface Skill {
|
||||
effect: string[];
|
||||
}
|
||||
|
||||
interface PluginDamage {
|
||||
Enemy: new () => DamageEnemy;
|
||||
}
|
||||
|
||||
interface DamageEnemy {}
|
||||
|
||||
type Forward<T> = {
|
||||
|
Loading…
Reference in New Issue
Block a user