mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-07-18 11:01:48 +08:00
341 lines
11 KiB
TypeScript
341 lines
11 KiB
TypeScript
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<typeof Textbox> = {
|
|
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<LayerGroup>();
|
|
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<Sprite>();
|
|
|
|
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 () => (
|
|
<container id="main-scene" width={MAIN_WIDTH} height={MAIN_HEIGHT}>
|
|
{loaded.value && (
|
|
<LeftStatusBar
|
|
loc={[0, 0, STATUS_BAR_WIDTH, STATUS_BAR_HEIGHT]}
|
|
status={leftStatus}
|
|
hidden={hideStatus.value}
|
|
></LeftStatusBar>
|
|
)}
|
|
<g-line line={[180, 0, 180, 480]} lineWidth={1} />
|
|
<container
|
|
id="map-draw"
|
|
loc={[180, 0, 480, 480]}
|
|
zIndex={10}
|
|
onClick={clickMap}
|
|
onDown={downMap}
|
|
onMove={moveMap}
|
|
>
|
|
<layer-group id="layer-main" ex={layerGroupExtends} ref={map}>
|
|
<layer layer="bg" zIndex={10}></layer>
|
|
<layer layer="bg2" zIndex={20}></layer>
|
|
<layer layer="event" zIndex={30} ex={eventExtends}></layer>
|
|
<layer layer="fg" zIndex={40}></layer>
|
|
<layer layer="fg2" zIndex={50}></layer>
|
|
<PopText id="pop-main" zIndex={80}></PopText>
|
|
</layer-group>
|
|
<Textbox id="main-textbox" {...mainTextboxProps}></Textbox>
|
|
<FloorChange id="floor-change" zIndex={50}></FloorChange>
|
|
<Tip
|
|
id="main-tip"
|
|
zIndex={80}
|
|
loc={[8, 8, 200, 32]}
|
|
pad={[12, 6]}
|
|
corner={16}
|
|
/>
|
|
<sprite
|
|
noevent
|
|
loc={[0, 0, 480, 480]}
|
|
ref={mapMiscSprite}
|
|
zIndex={170}
|
|
render={renderMapMisc}
|
|
/>
|
|
</container>
|
|
<g-line line={[180 + 480, 0, 180 + 480, 480]} lineWidth={1} />
|
|
{loaded.value && (
|
|
<RightStatusBar
|
|
loc={[480 + 180, 0, STATUS_BAR_WIDTH, STATUS_BAR_HEIGHT]}
|
|
status={rightStatus}
|
|
hidden={hideStatus.value}
|
|
></RightStatusBar>
|
|
)}
|
|
<container
|
|
loc={[0, 0, MAIN_WIDTH, MAIN_HEIGHT]}
|
|
hidden={!mainUIController.active.value}
|
|
zIndex={200}
|
|
>
|
|
{mainUIController.render()}
|
|
</container>
|
|
<g-rect
|
|
loc={[0, 0, MAIN_WIDTH, MAIN_HEIGHT]}
|
|
hidden={hideStatus.value}
|
|
zIndex={100}
|
|
stroke
|
|
noevent
|
|
></g-rect>
|
|
<g-line
|
|
line={[180, 0, 480 + 180, 0]}
|
|
hidden={!hideStatus.value}
|
|
zIndex={100}
|
|
/>
|
|
<g-line
|
|
line={[180, 480, 480 + 180, 480]}
|
|
hidden={!hideStatus.value}
|
|
zIndex={100}
|
|
/>
|
|
</container>
|
|
);
|
|
});
|
|
|
|
export const MainSceneUI = new GameUI('main-scene', MainScene);
|