mirror of
https://github.com/motajs/template.git
synced 2026-05-18 15:41:09 +08:00
refactor: Layer2 数据拆分至 data-system
This commit is contained in:
parent
80b73938b4
commit
9ada0796b3
39
dev.md
39
dev.md
@ -33,6 +33,13 @@
|
||||
| `pnpm type` | 对仓库执行类型检查 |
|
||||
| `pnpm check:circular` | 对仓库执行循环引用检查 |
|
||||
|
||||
## 术语统一
|
||||
|
||||
- 方法:一般指挂载到接口 `interface` 或类 `class` 上的函数。
|
||||
- 函数:一般指在文件顶层定义的函数,有时也会指方法,需要根据语境判断。
|
||||
- 接口:有时指 `interface`,有时也会指方法、成员等内容,需要根据语境判断。
|
||||
- 成员/属性:一般指接口 `interface`、类 `class`、对象 `object` 上的字段。
|
||||
|
||||
## 开发原则
|
||||
|
||||
### 模块原则
|
||||
@ -97,11 +104,29 @@
|
||||
- **数据端**:可在 `node` 环境中单独运行,可直接用于录像验证,不负责任何渲染逻辑。
|
||||
- **渲染端**:仅负责向数据端发送消息,不负责任何逻辑运算。
|
||||
|
||||
| 包 | 层级 | 说明 |
|
||||
| ---------------------- | ------------ | ------------------------------------ |
|
||||
| `@user/data-base` | 数据端系统层 | 负责数据端核心系统 |
|
||||
| `@user/data-state` | 数据端实现层 | 依赖系统层实现完整的游戏实例 |
|
||||
| `@user/client-base` | 渲染端系统层 | 负责渲染端核心系统 |
|
||||
| `@user/client-modules` | 渲染端实现层 | 依赖系统层实现客户端的渲染与用户交互 |
|
||||
|
||||
数据端允许调用渲染端代码,但必须使用全局接口 `Mota.r(() => {})` 包裹。除非必要,否则不建议在数据端调用渲染端代码。
|
||||
|
||||
### 渲染端
|
||||
|
||||
渲染端目前已基本制作完成,分为两层:
|
||||
|
||||
| 包 | 层级 | 说明 |
|
||||
| ---------------------- | ------ | ------------------------------------ |
|
||||
| `@user/client-base` | 系统层 | 负责渲染端核心系统 |
|
||||
| `@user/client-modules` | 实现层 | 依赖系统层实现客户端的渲染与用户交互 |
|
||||
|
||||
### 数据端
|
||||
|
||||
数据端目前正在从旧引擎进行彻底性重构,分为三层:
|
||||
|
||||
**Layer 0 — 公共层**:包含公共接口、工具函数等内容,不依赖任何外部游戏逻辑,可被任意高层直接引用。内容较少,与 Layer 1 共同放在 `@user/data-base` 中,不单独开包。
|
||||
|
||||
**Layer 1 — 数据层**:包含所有会影响游戏存档与流程的数据内容,如地图、怪物、玩家属性等。本层通过统一接口 `IStateBase` 对外暴露数据访问能力,各类数据模块均以此接口为核心组织。
|
||||
|
||||
**Layer 2 — 执行层**:直接引用 Layer 1,负责产生影响游戏进程的动作,如玩家控制、战斗计算等。本层内容不会进入存档,仅通过修改 Layer 1 的数据来影响游戏状态,并通过统一接口 `ICoreState` 对外暴露执行能力。
|
||||
|
||||
| 包 | 层级 | 说明 |
|
||||
| ------------------- | ----------------- | ------------------------------------------------------------------------------- |
|
||||
| `@user/data-base` | Layer 0 / Layer 1 | 公共层与数据层,定义 `IStateBase` 及各类游戏数据(地图、怪物、玩家属性等) |
|
||||
| `@user/data-state` | — | 数据端的顶层模块,指导 Layer 2 的执行行为,不直接参与执行 |
|
||||
| `@user/data-system` | Layer 2 | 执行层,定义 `ICoreState`,依赖数据层实现玩家控制、战斗计算等影响游戏进程的动作 |
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
export * from './face';
|
||||
export * from './faceManager';
|
||||
export * from './indexer';
|
||||
export * from './mover';
|
||||
export * from './types';
|
||||
export * from './utils';
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
export * from './combat';
|
||||
export * from './common';
|
||||
export * from './enemy';
|
||||
export * from './flag';
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
"@motajs/types": "workspace:*",
|
||||
"@motajs/common": "workspace:*",
|
||||
"@user/data-base": "workspace:*",
|
||||
"@user/data-system": "workspace:*",
|
||||
"@user/data-utils": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,10 +14,9 @@ import {
|
||||
IEnemyContext,
|
||||
IEnemySpecialModifier,
|
||||
IEnemyView,
|
||||
IReadonlyEnemyHandler,
|
||||
IReadonlyEnemy,
|
||||
ISpecial
|
||||
} from '@user/data-base';
|
||||
IReadonlyEnemyHandler
|
||||
} from '@user/data-system';
|
||||
import { IReadonlyEnemy, ISpecial } from '@user/data-base';
|
||||
import { IHaloValue } from './special';
|
||||
import { IEnemyAttr } from './types';
|
||||
import { IHeroAttr } from '../hero';
|
||||
|
||||
@ -3,7 +3,7 @@ import {
|
||||
IDamageCalculator,
|
||||
IEnemyDamageInfo,
|
||||
IReadonlyEnemyHandler
|
||||
} from '@user/data-base';
|
||||
} from '@user/data-system';
|
||||
import { IEnemyAttr } from './types';
|
||||
import { IVampireValue } from './special';
|
||||
import { IHeroAttr } from '../hero';
|
||||
@ -90,7 +90,8 @@ export class MainDamageCalculator implements IDamageCalculator<
|
||||
const extraInfo = this.calculate({
|
||||
enemy: guard.getComputedEnemy(),
|
||||
locator,
|
||||
hero
|
||||
hero,
|
||||
data: handler.data
|
||||
});
|
||||
turn += extraInfo.turn;
|
||||
damage += extraInfo.damage;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { IEnemyFinalEffect, IEnemyHandler } from '@user/data-base';
|
||||
import { IEnemyFinalEffect, IEnemyHandler } from '@user/data-system';
|
||||
import { IEnemyAttr } from './types';
|
||||
import { IHeroAttr } from '../hero';
|
||||
|
||||
|
||||
@ -16,11 +16,13 @@ import {
|
||||
IMapDamageInfoExtra,
|
||||
IMapDamageReducer,
|
||||
IReadonlyEnemyHandler,
|
||||
IMapDamageView
|
||||
} from '@user/data-system';
|
||||
import {
|
||||
IFaceHandler,
|
||||
ISpecial,
|
||||
IMapDamageView,
|
||||
IReadonlyHeroAttribute,
|
||||
IReadonlyEnemy,
|
||||
IFaceHandler,
|
||||
InternalFaceGroup
|
||||
} from '@user/data-base';
|
||||
import { IZoneValue } from './special';
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { IEnemyView } from '@user/data-base';
|
||||
import { IEnemyView } from '@user/data-system';
|
||||
|
||||
export interface IEnemyAttr {
|
||||
/** 怪物生命值 */
|
||||
|
||||
7
packages-user/data-system/package.json
Normal file
7
packages-user/data-system/package.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "@user/data-system",
|
||||
"dependencies": {
|
||||
"@motajs/common": "workspace:*",
|
||||
"@user/data-base": "workspace:*"
|
||||
}
|
||||
}
|
||||
@ -14,11 +14,16 @@ import {
|
||||
IMapDamage,
|
||||
IReadonlyEnemyHandler
|
||||
} from './types';
|
||||
import { IReadonlyHeroAttribute } from '../hero';
|
||||
import { IEnemy, IReadonlyEnemy, ISpecial } from '../enemy';
|
||||
import {
|
||||
IReadonlyHeroAttribute,
|
||||
IEnemy,
|
||||
IReadonlyEnemy,
|
||||
ISpecial,
|
||||
IStateBase,
|
||||
ILocationIndexer,
|
||||
MapLocIndexer
|
||||
} from '@user/data-base';
|
||||
import { EnemyView } from './enemy';
|
||||
import { ILocationIndexer, MapLocIndexer } from '../common/indexer';
|
||||
import { IStateBase } from '../types';
|
||||
|
||||
export class EnemyContext<TEnemy, THero> implements IEnemyContext<
|
||||
TEnemy,
|
||||
@ -11,9 +11,12 @@ import {
|
||||
IReadonlyEnemyHandler,
|
||||
IEnemyView
|
||||
} from './types';
|
||||
import { IHeroAttribute, IReadonlyHeroAttribute } from '../hero';
|
||||
import { IReadonlyEnemy } from '../enemy';
|
||||
import { IStateBase } from '../types';
|
||||
import {
|
||||
IHeroAttribute,
|
||||
IReadonlyHeroAttribute,
|
||||
IReadonlyEnemy,
|
||||
IStateBase
|
||||
} from '@user/data-base';
|
||||
|
||||
interface ICriticalSearchResult {
|
||||
/** 此临界点的属性值 */
|
||||
@ -1,4 +1,4 @@
|
||||
import { IEnemy, IReadonlyEnemy } from '../enemy';
|
||||
import { IEnemy, IReadonlyEnemy } from '@user/data-base';
|
||||
import { IEnemyView, IEnemyContext } from './types';
|
||||
|
||||
export class EnemyView<TAttr> implements IEnemyView<TAttr> {
|
||||
@ -9,8 +9,7 @@ import {
|
||||
IMapDamageReducer,
|
||||
IMapDamageView
|
||||
} from './types';
|
||||
import { ILocationHelper } from '../common/indexer';
|
||||
import { IStateBase } from '../types';
|
||||
import { ILocationHelper, IStateBase } from '@user/data-base';
|
||||
|
||||
interface IPointInfo {
|
||||
/** 该点所有的地图伤害 */
|
||||
@ -1,8 +1,13 @@
|
||||
import { ITileLocator, IRange } from '@motajs/common';
|
||||
import { IEnemy, IReadonlyEnemy, ISpecial } from '../enemy';
|
||||
import { IReadonlyHeroAttribute, IHeroAttribute } from '../hero';
|
||||
import { ILocationHelper } from '../common/indexer';
|
||||
import { IStateBase } from '../types';
|
||||
import {
|
||||
IEnemy,
|
||||
IReadonlyEnemy,
|
||||
ISpecial,
|
||||
IReadonlyHeroAttribute,
|
||||
IHeroAttribute,
|
||||
IStateBase,
|
||||
ILocationHelper
|
||||
} from '@user/data-base';
|
||||
|
||||
//#region 辅助接口
|
||||
|
||||
1
packages-user/data-system/src/index.ts
Normal file
1
packages-user/data-system/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './combat';
|
||||
@ -1,22 +1,3 @@
|
||||
/**
|
||||
* 滑动数组
|
||||
* @param arr
|
||||
* @param delta
|
||||
*/
|
||||
export function slide<T>(arr: T[], delta: number): T[] {
|
||||
if (delta === 0) return arr;
|
||||
delta %= arr.length;
|
||||
if (delta > 0) {
|
||||
arr.unshift(...arr.splice(arr.length - delta, delta));
|
||||
return arr;
|
||||
}
|
||||
if (delta < 0) {
|
||||
arr.push(...arr.splice(0, -delta));
|
||||
return arr;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
const backDirMap: Record<Dir2, Dir2> = {
|
||||
up: 'down',
|
||||
down: 'up',
|
||||
@ -38,23 +19,8 @@ export function has<T>(v: T): v is NonNullable<T> {
|
||||
return v !== null && v !== void 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
|
||||
);
|
||||
core.resize();
|
||||
}
|
||||
}
|
||||
|
||||
export function ensureArray<T>(arr: T): T extends any[] ? T : T[] {
|
||||
// @ts-ignore
|
||||
// @ts-expect-error 需要弃用
|
||||
return arr instanceof Array ? arr : [arr];
|
||||
}
|
||||
|
||||
@ -70,23 +36,6 @@ export function manhattan(x1: number, y1: number, x2: number, y2: number) {
|
||||
return Math.abs(x1 - x2) + Math.abs(y1 - y2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查一个点是否在当前超大地图 v2 优化范围内
|
||||
*/
|
||||
export function checkV2(x?: number, y?: number) {
|
||||
return (
|
||||
has(x) &&
|
||||
has(y) &&
|
||||
!(
|
||||
core.bigmap.v2 &&
|
||||
(x < core.bigmap.posX - core.bigmap.extend ||
|
||||
x > core.bigmap.posX + core._WIDTH_ + core.bigmap.extend ||
|
||||
y < core.bigmap.posY - core.bigmap.extend ||
|
||||
y > core.bigmap.posY + core._HEIGHT_ + core.bigmap.extend)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function formatDamage(damage: number): DamageString {
|
||||
let dam = '';
|
||||
let color = '';
|
||||
@ -105,64 +54,6 @@ export function formatDamage(damage: number): DamageString {
|
||||
return { damage: dam, color: color as Color };
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断一个数组的数值是否全部相等
|
||||
* @param arr 要判断的数组
|
||||
*/
|
||||
export function equal(arr: number[]): boolean;
|
||||
/**
|
||||
* 判断一个数组的元素的某个属性的数值是否全部相等
|
||||
* @param arr 要判断的数组
|
||||
* @param key 要判断的属性名
|
||||
*/
|
||||
export function equal<T>(arr: T[], key: keyof T): boolean;
|
||||
export function equal(arr: any, key?: any) {
|
||||
if (has(key)) {
|
||||
for (let i = 1; i < arr.length; i++) {
|
||||
if (arr[i][key] !== arr[0][key]) return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
for (let i = 1; i < arr.length; i++) {
|
||||
if (arr[i] !== arr[0]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得一个数组的数值的最大值和最小值
|
||||
* @param arr 要获得的数组
|
||||
*/
|
||||
export function boundary(arr: number[]): [number, number];
|
||||
/**
|
||||
* 获得一个数组的元素的某个属性的数值的最大值和最小值
|
||||
* @param arr 要获得的数组
|
||||
* @param key 要获得的属性名
|
||||
*/
|
||||
export function boundary<T>(arr: T[], key: keyof T): [number, number];
|
||||
export function boundary(arr: any, key?: any) {
|
||||
if (has(key)) {
|
||||
let min = arr[0][key];
|
||||
let max = arr[0][key];
|
||||
for (let i = 1; i < arr.length; i++) {
|
||||
const ele = arr[i][key];
|
||||
if (ele < min) min = ele;
|
||||
if (ele > max) max = ele;
|
||||
}
|
||||
return [min, max];
|
||||
} else {
|
||||
let min = arr[0];
|
||||
let max = arr[0];
|
||||
for (let i = 1; i < arr.length; i++) {
|
||||
const ele = arr[i];
|
||||
if (ele < min) min = ele;
|
||||
if (ele > max) max = ele;
|
||||
}
|
||||
return [min, max];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取两个坐标的相对方向
|
||||
* @param from 初始坐标
|
||||
|
||||
@ -315,10 +315,22 @@ importers:
|
||||
'@user/data-base':
|
||||
specifier: workspace:*
|
||||
version: link:../data-base
|
||||
'@user/data-system':
|
||||
specifier: workspace:*
|
||||
version: link:../data-system
|
||||
'@user/data-utils':
|
||||
specifier: workspace:*
|
||||
version: link:../data-utils
|
||||
|
||||
packages-user/data-system:
|
||||
dependencies:
|
||||
'@motajs/common':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/common
|
||||
'@user/data-base':
|
||||
specifier: workspace:*
|
||||
version: link:../data-base
|
||||
|
||||
packages-user/data-utils:
|
||||
dependencies:
|
||||
'@user/data-base':
|
||||
@ -409,8 +421,6 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../data-utils
|
||||
|
||||
packages-user/types: {}
|
||||
|
||||
packages/animate: {}
|
||||
|
||||
packages/audio:
|
||||
@ -447,12 +457,6 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../common
|
||||
|
||||
packages/legacy-data:
|
||||
dependencies:
|
||||
'@motajs/common':
|
||||
specifier: workspace:*
|
||||
version: link:../common
|
||||
|
||||
packages/legacy-system:
|
||||
dependencies:
|
||||
'@motajs/system':
|
||||
|
||||
22
prompt.md
22
prompt.md
@ -38,7 +38,23 @@
|
||||
```md
|
||||
# 需求综述
|
||||
|
||||
描述清楚需求的内容与目的。
|
||||
描述清楚需求的内容、动机与目的。
|
||||
|
||||
# 接口设计与预期
|
||||
|
||||
这是相当重要的一章。需要按接口逐一列出其方法与成员,分析每个成员和方法的预期使用频率(高 / 中 / 低)并说明判断依据;对于中频和高频成员,还需列出至少一个典型使用场景。
|
||||
|
||||
**命名长度原则**:频率越高,成员名应越短。高频成员以一个单词为宜,最多不超过两个单词;中频成员不超过三个单词;低频成员可以稍长,但不宜过长。
|
||||
|
||||
**频率的定义**:此处频率指**用户编写此调用的可能性或频率**,而非运行时的调用频率。例如某成员每秒被调用千次,但整个游戏中只在一处出现过,仍属低频。
|
||||
|
||||
示例如下:
|
||||
|
||||
## IObjectMover
|
||||
|
||||
- `IObjectMover.forward()`:预期频率**高频**。向前移动一格是地图行走、动画演出等场景的核心需求,在逻辑与演出中都会频繁出现,故为高频。典型使用场景:演出中玩家或 NPC 沿某方向连续移动。
|
||||
- `IObjectMover.speed()`:预期频率**中频**。移动中修改移速有一定使用场景,但远不及 `forward`、`step` 等移动接口的频率,通常只在特殊演出或逻辑中出现,故为中频。典型使用场景:NPC 逃离怪物时先定在原地,随后逐渐加速逃跑。
|
||||
- `IObjectMover.stepFace()`:预期频率**低频**。移动方向与朝向不同的常见场景(后退)已由 `backward` 覆盖,只有极特殊情况才需要此接口(如角色朝向固定但沿垂直方向平移),相当罕见,故为低频。
|
||||
|
||||
# 实现思路
|
||||
|
||||
@ -52,10 +68,6 @@
|
||||
|
||||
...
|
||||
|
||||
# 接口预期
|
||||
|
||||
这里列出每个接口,然后分析它们的预期使用频率(高中低),对于中频率和高频率,列出至少一个可能的使用场景。需要注意,使用频率越高,其接口名长度理应越短,对于高频接口,应该尽量不超过两个单词,以一个单词为宜;对于中频接口,应该尽量不超过三个单词;低频接口长度可以长一些,但不宜过长。
|
||||
|
||||
# 涉及文件
|
||||
|
||||
## 需要引用的文件
|
||||
|
||||
Loading…
Reference in New Issue
Block a user