import { LayerShadowExtends } from '../legacy/shadow'; import { Props, Font, IActionEvent, MotaOffscreenCanvas2D, Sprite, onTick, transformCanvas } from '@motajs/render'; import { WeatherController } from '../weather'; import { defineComponent, onMounted, onUnmounted, reactive, ref, shallowRef } from 'vue'; import { Textbox, Tip } from '../components'; import { GameUI } from '@motajs/system-ui'; import { ENABLE_RIGHT_STATUS_BAR, MAIN_HEIGHT, MAIN_WIDTH, MAP_HEIGHT, MAP_WIDTH, RIGHT_STATUS_POS, STATUS_BAR_HEIGHT, STATUS_BAR_WIDTH } from '../shared'; import { ILeftHeroStatus, IRightHeroStatus, LeftStatusBar, RightStatusBar } from './statusBar'; import { ReplayingStatus } from './toolbar'; import { getHeroStatusOn } from '@user/data-state'; 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, MAP_HEIGHT - 150, MAP_WIDTH, 150], zIndex: 30, fillStyle: '#fff', titleFill: 'gold', font: new Font('normal'), titleFont: new Font('normal', 20, 'px', 700), winskin: 'winskin2.png', interval: 30, lineHeight: 4, width: MAP_WIDTH }; const map = shallowRef(); const hideStatus = ref(false); const locked = ref(false); const weather = new WeatherController(); weather.extern('main'); onMounted(() => { if (map.value) { weather.bind(map.value); } }); const replayStatus: ReplayingStatus = reactive({ replaying: false, playing: false, speed: 1, played: 0, total: 0 }); 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: '', replay: replayStatus }); const rightStatus: IRightHeroStatus = reactive({ exampleHard: 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; 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); const { pausing, speed, toReplay, totalList } = core.status.replay; replayStatus.replaying = core.isReplaying(); replayStatus.playing = !pausing; replayStatus.speed = speed; replayStatus.played = totalList.length - toReplay.length; replayStatus.total = totalList.length; rightStatus.exampleHard = flags.hard; }; 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; const camera = map.value?.camera; if (!step || !camera) return; transformCanvas(canvas, camera); const ctx = canvas.ctx; 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; } } }); }; //#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 () => ( ); }); export const MainSceneUI = new GameUI('main-scene', MainScene);