mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-19 04:19:30 +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、特效等与游戏进程无关的插件所在目录。其中包含以下内容:
|
`src`: 与 ui、特效等与游戏进程无关的插件所在目录。其中包含以下内容:
|
||||||
|
|
||||||
@ -45,7 +45,7 @@
|
|||||||
|
|
||||||
1. 运行`vue-tsc`检查类型是否正确
|
1. 运行`vue-tsc`检查类型是否正确
|
||||||
2. 运行`vite`的构建工具,打包除`public`外的内容
|
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`热重载
|
1. `vite`热重载
|
||||||
2. 楼层热重载
|
2. 楼层热重载
|
||||||
3. 插件热重载
|
3. 脚本编辑热重载
|
||||||
4. 脚本编辑热重载
|
4. 道具、怪物、图块属性热重载
|
||||||
5. 道具、怪物、图块属性热重载
|
5. styles.css
|
||||||
6. 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 = {
|
core.plugin.damage = {
|
||||||
Enemy
|
Enemy: DamageEnemy,
|
||||||
|
Collection: EnemyCollection
|
||||||
};
|
};
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
import './fiveLayer.js';
|
import './fiveLayer';
|
||||||
import './heroFourFrames.js';
|
import './heroFourFrames';
|
||||||
import './hotReload.js';
|
import './hotReload';
|
||||||
import './itemDetail.js';
|
import './itemDetail';
|
||||||
import './popup.js';
|
import './popup';
|
||||||
import './replay.js';
|
import './replay';
|
||||||
import './ui.js';
|
import './ui';
|
||||||
import * as halo from './halo.js';
|
import * as halo from './halo';
|
||||||
import * as hero from './hero.js';
|
import * as hero from './hero';
|
||||||
import * as loopMap from './loopMap.js';
|
import * as loopMap from './loopMap';
|
||||||
import * as remainEnemy from './remainEnemy.js';
|
import * as remainEnemy from './remainEnemy';
|
||||||
import * as removeMap from './removeMap.js';
|
import * as removeMap from './removeMap';
|
||||||
import * as shop from './shop.js';
|
import * as shop from './shop';
|
||||||
import * as skill from './skills.js';
|
import * as skill from './skills';
|
||||||
import * as skillTree from './skillTree.js';
|
import * as skillTree from './skillTree';
|
||||||
import * as study from './study.js';
|
import * as study from './study';
|
||||||
import * as towerBoss from './towerBoss.js';
|
import * as towerBoss from './towerBoss';
|
||||||
import * as utils from './utils.js';
|
import * as utils from './utils';
|
||||||
import * as chase from './chase.js';
|
import * as chase from './chase';
|
||||||
import * as damage from './damage';
|
import * as damage from './damage';
|
||||||
|
|
||||||
export {
|
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 arr
|
||||||
* @param {number} delta
|
* @param delta
|
||||||
*/
|
*/
|
||||||
export function slide(arr, delta) {
|
export function slide<T>(arr: T[], delta: number): T[] {
|
||||||
if (delta === 0) return arr;
|
if (delta === 0) return arr;
|
||||||
delta %= arr.length;
|
delta %= arr.length;
|
||||||
if (delta > 0) {
|
if (delta > 0) {
|
||||||
@ -16,27 +16,29 @@ export function slide(arr, delta) {
|
|||||||
arr.push(...arr.splice(0, -delta));
|
arr.push(...arr.splice(0, -delta));
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function backDir(dir) {
|
export function backDir(dir: Dir): Dir {
|
||||||
return {
|
return {
|
||||||
up: 'down',
|
up: 'down',
|
||||||
down: 'up',
|
down: 'up',
|
||||||
left: 'right',
|
left: 'right',
|
||||||
right: 'left'
|
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;
|
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);
|
const index = core.domStyle.availableScale.indexOf(core.domStyle.scale);
|
||||||
core.control.setDisplayScale(
|
core.control.setDisplayScale(
|
||||||
core.domStyle.availableScale.length - 1 - index - n
|
core.domStyle.availableScale.length - 1 - index - n
|
||||||
);
|
);
|
||||||
if (!core.isPlaying() && core.flags.enableHDCanvas) {
|
if (!core.isPlaying() && core.flags.enableHDCanvas) {
|
||||||
|
// @ts-ignore
|
||||||
core.domStyle.ratio = Math.max(
|
core.domStyle.ratio = Math.max(
|
||||||
window.devicePixelRatio || 1,
|
window.devicePixelRatio || 1,
|
||||||
core.domStyle.scale
|
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 = {
|
core.plugin.utils = {
|
||||||
slide,
|
slide,
|
||||||
backDir,
|
backDir,
|
11
src/types/plugin.d.ts
vendored
11
src/types/plugin.d.ts
vendored
@ -30,7 +30,6 @@ interface PluginDeclaration
|
|||||||
hero: GamePluginHeroRealStatus;
|
hero: GamePluginHeroRealStatus;
|
||||||
replay: PluginReplay;
|
replay: PluginReplay;
|
||||||
chase: PluginChase;
|
chase: PluginChase;
|
||||||
damage: PluginDamage;
|
|
||||||
|
|
||||||
skills: Record<Chapter, Skill[]>;
|
skills: Record<Chapter, Skill[]>;
|
||||||
skillEffects: SkillEffects;
|
skillEffects: SkillEffects;
|
||||||
@ -94,6 +93,12 @@ interface PluginDeclaration
|
|||||||
* 重置变量的设置信息
|
* 重置变量的设置信息
|
||||||
*/
|
*/
|
||||||
resetFlagSettings(): void;
|
resetFlagSettings(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判定一个值是否不是undefined或null
|
||||||
|
* @param value 要判断的值
|
||||||
|
*/
|
||||||
|
has<T>(value: T): value is NonNullable<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GamePluginUtils {
|
interface GamePluginUtils {
|
||||||
@ -457,10 +462,6 @@ interface Skill {
|
|||||||
effect: string[];
|
effect: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PluginDamage {
|
|
||||||
Enemy: new () => DamageEnemy;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DamageEnemy {}
|
interface DamageEnemy {}
|
||||||
|
|
||||||
type Forward<T> = {
|
type Forward<T> = {
|
||||||
|
Loading…
Reference in New Issue
Block a user