mirror of
				https://github.com/unanmed/HumanBreak.git
				synced 2025-10-31 04:02:59 +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