范围类

This commit is contained in:
unanmed 2023-05-04 16:03:23 +08:00
parent 2e7e64b399
commit 068dca14f9
6 changed files with 287 additions and 41 deletions

View File

@ -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
以下内容修改后会自动刷新页面

View File

@ -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
};

View File

@ -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
View 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
);
}
);

View File

@ -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
View File

@ -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> = {