feat: add big fish runtime rules entry
This commit is contained in:
33
docs/technical/BIG_FISH_RUNTIME_RULE_ENTRY_2026-04-26.md
Normal file
33
docs/technical/BIG_FISH_RUNTIME_RULE_ENTRY_2026-04-26.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# 大鱼吃小鱼运行页规则入口说明 2026-04-26
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
大鱼吃小鱼玩法规则已经在 PRD 与运行态技术方案中定义,但网站运行页没有给玩家查看规则的入口。玩家进入 `/big-fish` 或正式运行页后,只能看到当前等级、状态和事件日志,无法在游玩前快速理解吞噬、合成、胜负条件。
|
||||||
|
|
||||||
|
## 设计结论
|
||||||
|
|
||||||
|
1. 规则入口放在运行页顶部操作区,使用 `CircleHelp` 图标按钮。
|
||||||
|
2. 默认界面不直接铺规则长文案,点击按钮后打开独立模态窗口。
|
||||||
|
3. 模态窗口只保留玩家决策所需的核心规则:
|
||||||
|
- 拖动方向控制移动。
|
||||||
|
- 吃掉低级或同级野生实体并收编。
|
||||||
|
- 碰到更高级野生实体时,己方实体会被吃掉。
|
||||||
|
- 3 个同级己方实体自动合成更高一级。
|
||||||
|
- 拥有最高等级后通关,己方实体归零后失败。
|
||||||
|
4. 入口必须在移动端单手可点,不遮挡舞台主体。
|
||||||
|
5. 规则内容只做说明,不参与任何前端裁决;真实规则仍以后端运行快照为准。
|
||||||
|
|
||||||
|
## 落地范围
|
||||||
|
|
||||||
|
1. `src/components/big-fish-runtime/BigFishRuntimeShell.tsx`
|
||||||
|
- 增加规则按钮与规则模态窗口。
|
||||||
|
- 复用 `UnifiedModal`,避免在当前玩法舞台内容流里展开说明。
|
||||||
|
2. `src/components/big-fish-runtime/BigFishRuntimeShell.test.tsx`
|
||||||
|
- 覆盖规则入口打开与关闭。
|
||||||
|
|
||||||
|
## 验收口径
|
||||||
|
|
||||||
|
1. 进入大鱼吃小鱼运行页后,右上角可看到规则图标入口。
|
||||||
|
2. 点击规则入口后出现独立弹窗。
|
||||||
|
3. 弹窗能展示核心吞噬、合成、通关与失败规则。
|
||||||
|
4. 关闭弹窗后回到玩法舞台,不改变当前运行快照。
|
||||||
@@ -77,4 +77,23 @@ describe('BigFishRuntimeShell', () => {
|
|||||||
fireEvent.click(screen.getByRole('button', { name: '退出' }));
|
fireEvent.click(screen.getByRole('button', { name: '退出' }));
|
||||||
expect(onBack).toHaveBeenCalledTimes(1);
|
expect(onBack).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('opens and closes the runtime rule modal', () => {
|
||||||
|
render(
|
||||||
|
<BigFishRuntimeShell
|
||||||
|
run={createRun('running')}
|
||||||
|
onBack={() => {}}
|
||||||
|
onSubmitInput={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '查看规则' }));
|
||||||
|
|
||||||
|
expect(screen.getByRole('dialog', { name: '玩法规则' })).toBeTruthy();
|
||||||
|
expect(screen.getByText('低级或同级野生实体会被收编。')).toBeTruthy();
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '关闭' }));
|
||||||
|
|
||||||
|
expect(screen.queryByRole('dialog', { name: '玩法规则' })).toBeNull();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ArrowLeft, Loader2, RotateCcw } from 'lucide-react';
|
import { ArrowLeft, CircleHelp, Loader2, RotateCcw } from 'lucide-react';
|
||||||
import { type PointerEvent, useEffect, useRef, useState } from 'react';
|
import { type PointerEvent, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
@@ -7,6 +7,7 @@ import type {
|
|||||||
BigFishRuntimeSnapshotResponse,
|
BigFishRuntimeSnapshotResponse,
|
||||||
SubmitBigFishInputRequest,
|
SubmitBigFishInputRequest,
|
||||||
} from '../../../packages/shared/src/contracts/bigFish';
|
} from '../../../packages/shared/src/contracts/bigFish';
|
||||||
|
import { UnifiedModal } from '../common/UnifiedModal';
|
||||||
import { ResolvedAssetImage } from '../ResolvedAssetImage';
|
import { ResolvedAssetImage } from '../ResolvedAssetImage';
|
||||||
|
|
||||||
type TouchOrigin = {
|
type TouchOrigin = {
|
||||||
@@ -127,6 +128,38 @@ function resolveSettlementCopy(run: BigFishRuntimeSnapshotResponse) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function BigFishRuleModal({
|
||||||
|
open,
|
||||||
|
onClose,
|
||||||
|
}: {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<UnifiedModal
|
||||||
|
open={open}
|
||||||
|
title="玩法规则"
|
||||||
|
onClose={onClose}
|
||||||
|
size="sm"
|
||||||
|
zIndexClassName="z-[140]"
|
||||||
|
panelClassName="rounded-[1.25rem]"
|
||||||
|
bodyClassName="px-4 py-3 sm:px-5 sm:py-4"
|
||||||
|
>
|
||||||
|
<div className="space-y-3 text-sm leading-6 text-[var(--platform-text-base)]">
|
||||||
|
<div className="rounded-2xl bg-cyan-50 px-4 py-3 text-cyan-950">
|
||||||
|
拖动屏幕控制方向,角色会按固定速度移动。
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<div>低级或同级野生实体会被收编。</div>
|
||||||
|
<div>更高级野生实体会吃掉碰到的己方实体。</div>
|
||||||
|
<div>3 个同级己方实体会自动合成更高一级。</div>
|
||||||
|
<div>拥有最高等级实体后通关,己方实体归零后失败。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</UnifiedModal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function BigFishEntityDot({
|
function BigFishEntityDot({
|
||||||
entity,
|
entity,
|
||||||
run,
|
run,
|
||||||
@@ -194,6 +227,7 @@ export function BigFishRuntimeShell({
|
|||||||
}: BigFishRuntimeShellProps) {
|
}: BigFishRuntimeShellProps) {
|
||||||
const stageRef = useRef<HTMLDivElement | null>(null);
|
const stageRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [touchOrigin, setTouchOrigin] = useState<TouchOrigin | null>(null);
|
const [touchOrigin, setTouchOrigin] = useState<TouchOrigin | null>(null);
|
||||||
|
const [isRuleModalOpen, setIsRuleModalOpen] = useState(false);
|
||||||
const [stick, setStick] = useState({ x: 0, y: 0 });
|
const [stick, setStick] = useState({ x: 0, y: 0 });
|
||||||
const stickRef = useRef(stick);
|
const stickRef = useRef(stick);
|
||||||
|
|
||||||
@@ -297,8 +331,19 @@ export function BigFishRuntimeShell({
|
|||||||
>
|
>
|
||||||
<ArrowLeft className="h-4 w-4" />
|
<ArrowLeft className="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
<div className="rounded-full bg-black/28 px-4 py-2 text-xs font-bold backdrop-blur">
|
<div className="flex items-center gap-2">
|
||||||
Lv.{run.playerLevel}/{run.winLevel} · {statusLabel}
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label="查看规则"
|
||||||
|
title="查看规则"
|
||||||
|
onClick={() => setIsRuleModalOpen(true)}
|
||||||
|
className="pointer-events-auto inline-flex h-10 w-10 items-center justify-center rounded-full bg-black/28 text-white backdrop-blur"
|
||||||
|
>
|
||||||
|
<CircleHelp className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
<div className="rounded-full bg-black/28 px-4 py-2 text-xs font-bold backdrop-blur">
|
||||||
|
Lv.{run.playerLevel}/{run.winLevel} · {statusLabel}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -370,6 +415,10 @@ export function BigFishRuntimeShell({
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<BigFishRuleModal
|
||||||
|
open={isRuleModalOpen}
|
||||||
|
onClose={() => setIsRuleModalOpen(false)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user