import { GameUI } from '@/core/system'; import { computed, defineComponent, ref, watch } from 'vue'; import { SetupComponentOptions, TextContent } from '../components'; import { DefaultProps, ElementLocator, Sprite } from '@/core/render'; import { transitionedColor } from '../use'; import { linear } from 'mutate-animate'; import { Scroll } from '../components/scroll'; import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d'; import { getArea, MinimapDrawer } from '@/plugin/ui/fly'; import { NumpadToolbar, PlayingToolbar, ReplayingStatus, ReplayingToolbar } from './toolbar'; export interface ILeftHeroStatus { hp: number; atk: number; def: number; mdef: number; money: number; exp: number; yellowKey: number; blueKey: number; redKey: number; floor: FloorIds; lv: string; /** 生命回复 */ regen: number; /** 额外攻击 */ exAtk: number; /** 魔法防御 */ magicDef: number; } interface StatusBarProps extends DefaultProps { loc: ElementLocator; status: T; hidden: boolean; } const statusBarProps = { props: ['loc', 'status', 'hidden'] } satisfies SetupComponentOptions>; export const LeftStatusBar = defineComponent>( p => { const hpIcon = core.material.images.images['hp.png']; const atkIcon = core.material.images.images['atk.png']; const defIcon = core.material.images.images['def.png']; const mdefIcon = core.material.images.images['IQ.png']; const moneyIcon = core.material.images.images['money.png']; const expIcon = core.material.images.images['exp.png']; const s = p.status; const f = core.formatBigNumber; const floorName = computed(() => core.floors[s.floor]?.title ?? ''); const key = (num: number) => { return num.toString().padStart(2, '0'); }; const font1 = '18px normal'; const font2 = 'bold 18px normal'; const font3 = 'bold 14px normal'; const iconLoc = (n: number): ElementLocator => { return [16, 76 + 44 * n, 32, 32]; }; const textLoc = (n: number): ElementLocator => { return [60, 92 + 44 * n, void 0, void 0, 0, 0.5]; }; const central = (y: number): ElementLocator => { const width = p.loc[2] ?? 200; return [width / 2, y, void 0, void 0, 0.5, 0.5]; }; const right = (y: number): ElementLocator => { const width = p.loc[2] ?? 200; return [width - 16, y, void 0, void 0, 1, 0.5]; }; const keyCount = 3; const keyY = 92 + 44 * 6; const keyLoc = (n: number): ElementLocator => { const width = p.loc[2] ?? 200; const per = width / (keyCount + 1); return [per * (n + 1), keyY, void 0, void 0, 0.5, 0.5]; }; return () => { return ( ); }; }, statusBarProps ); interface RightStatusBarMisc { name: string; value: string; nameColor: string; valueColor: string; } export interface IRightHeroStatus { /** 自动切换技能 */ autoSkill: boolean; /** 当前开启的技能 */ skillName: string; /** 技能描述 */ skillDesc: string; /** 跳跃剩余次数,-1 表示未开启,-2表示当前楼层不能跳 */ jumpCount: number; /** 治愈之泉剩余次数,-1 表示未开启 */ springCount: number; /** 当前楼层 */ floor: FloorIds; /** 是否正在录像播放 */ replaying: boolean; /** 录像播放状态 */ replayStatus: ReplayingStatus; } export const RightStatusBar = defineComponent>( p => { const font1 = '18px normal'; const font2 = '16px normal'; const minimap = ref(); const inNumpad = ref(false); const onNumpad = () => { inNumpad.value = !inNumpad.value; }; const s = p.status; const skill = computed(() => s.autoSkill ? '已开启自动切换' : s.skillName ); const skillDesc = computed(() => s.autoSkill ? '自动切换技能时,会自动选择最优技能' : s.skillDesc ); const skillColor = transitionedColor('#284', 200, linear())!; watch( () => s.autoSkill, value => { skillColor.set(value ? '#284' : '#824'); } ); const miscData = computed(() => { const data: RightStatusBarMisc[] = []; if (s.jumpCount !== -1) { const text = s.jumpCount === -2 ? '不可跳跃' : s.jumpCount.toString(); data.push({ name: '跳跃剩余', nameColor: '#fff', value: text, valueColor: '#fff' }); } if (s.springCount >= 0) { data.push({ name: '治愈之泉', nameColor: '#a7ffa7', value: s.springCount.toString(), valueColor: '#a7ffa7' }); } return data; }); const central = (y: number): ElementLocator => { const width = p.loc[2] ?? 200; return [width / 2, y, void 0, void 0, 0.5, 0.5]; }; const middle = (x: number, y: number): ElementLocator => { return [x, y, void 0, void 0, 0, 0.5]; }; const changeAutoSkill = () => { const { HeroSkill } = Mota.require('module', 'Mechanism'); const auto = !s.autoSkill; HeroSkill.setAutoSkill(auto); core.status.route.push(`set:autoSkill:${auto}`); core.updateStatusBar(); }; const area = getArea(); const minimapDrawer = new MinimapDrawer( document.createElement('canvas') ); minimapDrawer.noBorder = true; minimapDrawer.scale = 4; minimapDrawer.showInfo = true; let linked = false; const drawMinimap = (canvas: MotaOffscreenCanvas2D) => { const ctx = canvas.ctx; ctx.save(); ctx.scale( 1 / core.domStyle.scale / devicePixelRatio, 1 / core.domStyle.scale / devicePixelRatio ); if (!linked) { minimapDrawer.link(canvas.canvas); linked = true; } if (minimapDrawer.nowFloor !== s.floor) { minimapDrawer.drawedThumbnail = {}; } else { minimapDrawer.drawToTarget(); ctx.restore(); return; } minimapDrawer.nowFloor = s.floor; minimapDrawer.nowArea = Object.keys(area).find(v => area[v].includes(core.status.floorId) ) ?? ''; minimapDrawer.locateMap(minimapDrawer.nowFloor); minimapDrawer.drawMap(); ctx.restore(); }; watch( () => s.floor, () => { minimap.value?.update(); } ); return () => { return ( ); }; }, statusBarProps ); export const leftStatusBarUI = new GameUI('left-status-bar', LeftStatusBar); export const rightStatusBarUI = new GameUI('right-status-bar', RightStatusBar);