import { LayerShadowExtends } from '../legacy/shadow'; import { Props, Font, IActionEvent, MotaOffscreenCanvas2D, Sprite, onTick } from '@motajs/render'; import { WeatherController } from '../../weather'; import { defineComponent, onMounted, onUnmounted, reactive, ref } from 'vue'; import { Textbox, Tip } from '../components'; import { GameUI } from '@motajs/system-ui'; import { MAIN_HEIGHT, MAIN_WIDTH, STATUS_BAR_HEIGHT, STATUS_BAR_WIDTH } from '../shared'; import { ILeftHeroStatus, IRightHeroStatus, LeftStatusBar, RightStatusBar } from './statusBar'; import { onLoaded } from '../use'; import { ReplayingStatus } from './toolbar'; import { getHeroStatusOn, HeroSkill, NightSpecial } from '@user/data-state'; import { jumpIgnoreFloor } from '@user/legacy-plugin-data'; import { hook } from '@user/data-base'; import { FloorDamageExtends, FloorItemDetail } from '../elements'; import { LayerGroupPortal } from '../legacy/portal'; import { LayerGroupFilter } from '../legacy/gameCanvas'; import { LayerGroupHalo } from '../legacy/halo'; import { FloorChange } from '../legacy/fallback'; import { PopText } from '../legacy/pop'; import { mainUIController } from './controller'; import { ILayerGroupRenderExtends, LayerGroupAnimate, FloorViewport, ILayerRenderExtends, HeroRenderer, LayerDoorAnimate, LayerGroup } from '../elements'; import { isNil } from 'lodash-es'; const MainScene = defineComponent(() => { //#region 基本定义 const layerGroupExtends: ILayerGroupRenderExtends[] = [ new FloorDamageExtends(), new FloorItemDetail(), new LayerGroupFilter(), new LayerGroupPortal(), new LayerGroupHalo(), new LayerGroupAnimate(), new FloorViewport() ]; const eventExtends: ILayerRenderExtends[] = [ new HeroRenderer(), new LayerDoorAnimate(), new LayerShadowExtends() ]; const mainTextboxProps: Props = { text: '', hidden: true, loc: [0, 330, 480, 150], zIndex: 30, fillStyle: '#fff', titleFill: 'gold', font: new Font('normal'), titleFont: new Font('normal', 20, 'px', 700), winskin: 'winskin2.png', interval: 100, lineHeight: 4, width: 480 }; const map = ref(); const hideStatus = ref(false); const locked = ref(false); const weather = new WeatherController('main'); const loaded = ref(true); onLoaded(() => { loaded.value = true; }); onMounted(() => { weather.bind(map.value); }); const leftStatus: ILeftHeroStatus = reactive({ hp: 0, atk: 0, def: 0, mdef: 0, money: 0, exp: 0, yellowKey: 0, blueKey: 0, redKey: 0, floor: 'MT0', lv: '', regen: 0, exAtk: 0, magicDef: 0 }); const replayStatus: ReplayingStatus = reactive({ playing: false, speed: 1, played: 0, total: 0 }); const rightStatus: IRightHeroStatus = reactive({ autoSkill: false, skillName: '', skillDesc: '', jumpCount: 0, springCount: 0, floor: 'MT0', replaying: false, replayStatus, night: 0 }); //#region 状态更新 const updateStatus = () => { if (!core.status || !core.status.hero || !core.status.floorId) return; hideStatus.value = core.getFlag('hideStatusBar', false); const hero = core.status.hero; const floor = core.status.floorId; leftStatus.atk = getHeroStatusOn('atk'); leftStatus.hp = getHeroStatusOn('hp'); leftStatus.def = getHeroStatusOn('def'); leftStatus.mdef = getHeroStatusOn('mdef'); leftStatus.money = getHeroStatusOn('money'); leftStatus.exp = core.getNextLvUpNeed() ?? 0; leftStatus.yellowKey = core.itemCount('yellowKey'); leftStatus.blueKey = core.itemCount('blueKey'); leftStatus.redKey = core.itemCount('redKey'); leftStatus.floor = core.status.floorId; leftStatus.lv = core.getLvName(hero.lv); leftStatus.regen = getHeroStatusOn('hpmax'); leftStatus.exAtk = getHeroStatusOn('mana'); leftStatus.magicDef = getHeroStatusOn('magicDef'); rightStatus.autoSkill = HeroSkill.getAutoSkill(); rightStatus.skillName = HeroSkill.getSkillName(); rightStatus.skillDesc = HeroSkill.getSkillDesc(); rightStatus.night = NightSpecial.getNight(floor); rightStatus.floor = floor; rightStatus.replaying = core.isReplaying(); const { pausing, speed, toReplay, totalList } = core.status.replay; replayStatus.playing = !pausing; replayStatus.speed = speed; replayStatus.played = totalList.length - toReplay.length; replayStatus.total = totalList.length; if (HeroSkill.learnedSkill(HeroSkill.Jump)) { if (jumpIgnoreFloor.has(floor)) { rightStatus.jumpCount = -2; } else { rightStatus.jumpCount = 3 - (flags[`jump_${floor}`] ?? 0); } } else { rightStatus.jumpCount = -1; } if (core.hasFlag('spring')) { rightStatus.springCount = 50 - (flags.springCount ?? 0); } else { rightStatus.springCount = -1; } }; const updateDataFallback = () => { // 更新 locked 状态 locked.value = core.status.lockControl; }; // 监听状态栏更新事件 hook.on('statusBarUpdate', updateStatus); hook.on('statusBarUpdate', updateDataFallback); onUnmounted(() => { hook.off('statusBarUpdate', updateStatus); hook.off('statusBarUpdate', updateDataFallback); }); //#region sprite 渲染 let lastLength = 0; onTick(() => { const len = core.status.stepPostfix?.length ?? 0; if (len !== lastLength) { mapMiscSprite.value?.update(); lastLength = len; } }); const mapMiscSprite = ref(); const renderMapMisc = (canvas: MotaOffscreenCanvas2D) => { const step = core.status.stepPostfix; if (!step) return; const ctx = canvas.ctx; ctx.save(); ctx.fillStyle = '#fff'; step.forEach(({ x, y, direction }) => { ctx.fillRect(x * 32 + 12, y * 32 + 12, 8, 8); if (!isNil(direction)) { switch (direction) { case 'down': ctx.fillRect(x * 32 + 12, y * 32 + 20, 8, 12); break; case 'left': ctx.fillRect(x * 32, y * 32 + 12, 12, 8); break; case 'right': ctx.fillRect(x * 32 + 20, y * 32 + 12, 12, 8); break; case 'up': ctx.fillRect(x * 32 + 12, y * 32, 8, 12); break; } } }); ctx.restore(); }; //#region 交互监听 /** * 对于 registerAction 的 fallback */ const clickMap = (ev: IActionEvent) => { const bx = Math.floor(ev.offsetX / 32); const by = Math.floor(ev.offsetY / 32); core.doRegisteredAction('onup', bx, by, ev.offsetX, ev.offsetY); }; /** * 对于 registerAction 的 fallback */ const downMap = (ev: IActionEvent) => { const bx = Math.floor(ev.offsetX / 32); const by = Math.floor(ev.offsetY / 32); core.doRegisteredAction('ondown', bx, by, ev.offsetX, ev.offsetY); }; /** * 对于 registerAction 的 fallback */ const moveMap = (ev: IActionEvent) => { const bx = Math.floor(ev.offsetX / 32); const by = Math.floor(ev.offsetY / 32); core.doRegisteredAction('onmove', bx, by, ev.offsetX, ev.offsetY); }; return () => ( {loaded.value && ( )} {loaded.value && ( )} ); }); export const MainSceneUI = new GameUI('main-scene', MainScene);