feat: 默认状态栏

This commit is contained in:
unanmed 2025-09-28 23:37:28 +08:00
parent 015f959c1d
commit c0782169e2
6 changed files with 413 additions and 113 deletions

View File

@ -29,7 +29,7 @@ export const STATUS_BAR_HEIGHT = 32 * MAP_BLOCK_HEIGHT;
/** 右侧状态栏的横坐标 */ /** 右侧状态栏的横坐标 */
export const RIGHT_STATUS_POS = STATUS_BAR_WIDTH + MAP_WIDTH; export const RIGHT_STATUS_POS = STATUS_BAR_WIDTH + MAP_WIDTH;
/** 是否启用右侧状态栏 */ /** 是否启用右侧状态栏 */
export const ENABLE_RIGHT_STATUS_BAR = true; export const ENABLE_RIGHT_STATUS_BAR = false;
/** 状态栏数量,启用右侧状态栏为两个,不启用为一个 */ /** 状态栏数量,启用右侧状态栏为两个,不启用为一个 */
export const STATUS_BAR_COUNT = ENABLE_RIGHT_STATUS_BAR ? 2 : 1; export const STATUS_BAR_COUNT = ENABLE_RIGHT_STATUS_BAR ? 2 : 1;
/** 状态栏宽度的一半 */ /** 状态栏宽度的一半 */

View File

@ -108,14 +108,25 @@ const MainScene = defineComponent(() => {
}); });
const leftStatus: ILeftHeroStatus = reactive({ const leftStatus: ILeftHeroStatus = reactive({
hp: 0, hp: 0,
hpmax: 0,
mana: 0,
manamax: 0,
atk: 0, atk: 0,
def: 0, def: 0,
mdef: 0, mdef: 0,
money: 0, money: 0,
exp: 0, exp: 0,
up: 0,
yellowKey: 0, yellowKey: 0,
blueKey: 0, blueKey: 0,
redKey: 0, redKey: 0,
greenKey: 0,
pickaxe: 0,
bomb: 0,
centerFly: 0,
poison: false,
weak: false,
curse: false,
floor: 'MT0', floor: 'MT0',
lv: '', lv: '',
replay: replayStatus replay: replayStatus
@ -132,13 +143,24 @@ const MainScene = defineComponent(() => {
const hero = core.status.hero; const hero = core.status.hero;
leftStatus.atk = getHeroStatusOn('atk'); leftStatus.atk = getHeroStatusOn('atk');
leftStatus.hp = getHeroStatusOn('hp'); leftStatus.hp = getHeroStatusOn('hp');
leftStatus.hpmax = getHeroStatusOn('hpmax');
leftStatus.mana = getHeroStatusOn('mana');
leftStatus.manamax = getHeroStatusOn('manamax');
leftStatus.def = getHeroStatusOn('def'); leftStatus.def = getHeroStatusOn('def');
leftStatus.mdef = getHeroStatusOn('mdef'); leftStatus.mdef = getHeroStatusOn('mdef');
leftStatus.money = getHeroStatusOn('money'); leftStatus.money = getHeroStatusOn('money');
leftStatus.exp = core.getNextLvUpNeed() ?? 0; leftStatus.exp = getHeroStatusOn('exp');
leftStatus.up = core.getNextLvUpNeed() ?? 0;
leftStatus.yellowKey = core.itemCount('yellowKey'); leftStatus.yellowKey = core.itemCount('yellowKey');
leftStatus.blueKey = core.itemCount('blueKey'); leftStatus.blueKey = core.itemCount('blueKey');
leftStatus.redKey = core.itemCount('redKey'); leftStatus.redKey = core.itemCount('redKey');
leftStatus.greenKey = core.itemCount('greenKey');
leftStatus.pickaxe = core.itemCount('pickaxe');
leftStatus.bomb = core.itemCount('bomb');
leftStatus.centerFly = core.itemCount('centerFly');
leftStatus.poison = core.getFlag('poison', true);
leftStatus.weak = core.getFlag('weak', true);
leftStatus.curse = core.getFlag('curse', true);
leftStatus.floor = core.status.floorId; leftStatus.floor = core.status.floorId;
leftStatus.lv = core.getLvName(hero.lv); leftStatus.lv = core.getLvName(hero.lv);

View File

@ -1,29 +1,68 @@
import { GameUI, SetupComponentOptions } from '@motajs/system-ui'; import { GameUI, SetupComponentOptions } from '@motajs/system-ui';
import { computed, defineComponent, ref } from 'vue'; import { computed, ComputedRef, defineComponent, shallowReactive } from 'vue';
import { TextContent } from '../components'; import { TextContent } from '../components';
import { DefaultProps, ElementLocator, Font } from '@motajs/render';
import { import {
NumpadToolbar, DefaultProps,
PlayingToolbar, ElementLocator,
ReplayingStatus, Font,
ReplayingToolbar SizedCanvasImageSource
} from './toolbar'; } from '@motajs/render';
import { MixedToolbar, ReplayingStatus } from './toolbar';
import { openViewMap } from './viewmap'; import { openViewMap } from './viewmap';
import { mainUIController } from './controller'; import { mainUIController } from './controller';
import { MAIN_HEIGHT, STATUS_BAR_WIDTH } from '../shared'; import {
MAIN_HEIGHT,
MAIN_WIDTH,
STATUS_BAR_HEIGHT,
STATUS_BAR_WIDTH
} from '../shared';
export interface ILeftHeroStatus { export interface ILeftHeroStatus {
hp: number; /** 楼层 id */
atk: number;
def: number;
mdef: number;
money: number;
exp: number;
yellowKey: number;
blueKey: number;
redKey: number;
floor: FloorIds; floor: FloorIds;
/** 等级名称 */
lv: string; lv: string;
/** 生命值 */
hp: number;
/** 生命上限 */
hpmax: number;
/** 魔力值 */
mana: number;
/** 魔力上限 */
manamax: number;
/** 攻击力 */
atk: number;
/** 防御力 */
def: number;
/** 魔防(护盾) */
mdef: number;
/** 金币 */
money: number;
/** 经验值 */
exp: number;
/** 距离升级剩余经验 */
up: number;
/** 黄钥匙数量 */
yellowKey: number;
/** 蓝钥匙数量 */
blueKey: number;
/** 红钥匙数量 */
redKey: number;
/** 绿钥匙数量 */
greenKey: number;
/** 破数量 */
pickaxe: number;
/** 炸数量 */
bomb: number;
/** 飞数量 */
centerFly: number;
/** 是否中毒 */
poison: boolean;
/** 是否中衰 */
weak: boolean;
/** 是否中咒 */
curse: boolean;
/** 录像状态 */
replay: ReplayingStatus; replay: ReplayingStatus;
} }
@ -32,6 +71,31 @@ export interface IRightHeroStatus {
exampleHard: number; exampleHard: number;
} }
interface StatusInfo {
/** 图标 */
icon: SizedCanvasImageSource;
/** 属性值,经过格式化 */
value: ComputedRef<string>;
/** 字体 */
font: Font;
/** 文字颜色 */
color: CanvasStyle;
}
interface KeyLikeItem {
/** 属性值,经过格式化 */
value: ComputedRef<string>;
/** 字体 */
font: Font;
/** 文字颜色 */
color: CanvasStyle;
}
interface KeyLikeInfo {
/** 这一行包含的内容 */
items: KeyLikeItem[];
}
interface StatusBarProps<T> extends DefaultProps { interface StatusBarProps<T> extends DefaultProps {
loc: ElementLocator; loc: ElementLocator;
status: T; status: T;
@ -44,96 +108,336 @@ const statusBarProps = {
export const LeftStatusBar = defineComponent<StatusBarProps<ILeftHeroStatus>>( export const LeftStatusBar = defineComponent<StatusBarProps<ILeftHeroStatus>>(
p => { p => {
const hpIcon = core.material.images.images['hp.png']; //#region 参数定义
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 TEXT_X = 54;
const moneyIcon = core.material.images.images['money.png']; /** 图标的横坐标 */
const expIcon = core.material.images.images['exp.png']; const STATUS_PAD = 8;
/** 楼层名称的高度 */
const TITLE_HEIGHT = 36;
/** 状态属性的开始纵坐标 */
const STATUS_Y = TITLE_HEIGHT + STATUS_PAD;
// 可以换成 core.material.images.images['xxx.png'] 来使用全塔属性注册的图片
const hpIcon = core.statusBar.icons.hp;
const atkIcon = core.statusBar.icons.atk;
const defIcon = core.statusBar.icons.def;
const mdefIcon = core.statusBar.icons.mdef;
const moneyIcon = core.statusBar.icons.money;
const expIcon = core.statusBar.icons.exp;
const manaIcon = core.statusBar.icons.mana;
const lvIcon = core.statusBar.icons.lv;
const s = p.status; const s = p.status;
const f = core.formatBigNumber;
const inNumpad = ref(false); /** 常规字体 */
const font1 = Font.defaults({ size: 18 });
/** 加粗字体 */
const font2 = Font.defaults({ size: 18, weight: 700 });
/** 楼层名 */
const floorName = computed(() => core.floors[s.floor]?.title ?? ''); const floorName = computed(() => core.floors[s.floor]?.title ?? '');
/** 钥匙显示文字 */
const key = (num: number) => { const key = (num: number) => {
return num.toString().padStart(2, '0'); return num.toString().padStart(2, '0');
}; };
const onNumpad = () => { //#region 属性显示
inNumpad.value = !inNumpad.value;
};
const font1 = Font.defaults({ size: 18 }); /** 一般属性 */
const font2 = Font.defaults({ size: 18, weight: 700 }); const statusInfo: StatusInfo[] = shallowReactive([]);
/** 钥匙属性 */
const keyLike: KeyLikeInfo[] = shallowReactive([]);
const iconLoc = (n: number): ElementLocator => { // 根据全塔属性配置显示属性
return [16, 76 + 44 * n, 32, 32]; // 如果你想修改状态栏显示,不建议修改这里的内容,而建议直接新增标签
}; // 这里的内容是为使用样板默认状态栏的人准备的,对于新增属性来说过于复杂,而且功能有限,对于新手来说也难以理解
// 可以参考说明文档中的常见需求指南中的新增状态教程
const textLoc = (n: number): ElementLocator => { const list = core.flags.statusBarItems;
return [60, 92 + 44 * n, void 0, void 0, 0, 0.5];
};
// 等级
if (list.includes('enableLv')) {
statusInfo.push({
icon: lvIcon,
value: computed(() => s.lv),
font: font1,
color: '#fff'
});
}
// 生命值
if (list.includes('enableHP')) {
if (list.includes('enableHPMax')) {
// 如果启用血限
statusInfo.push({
icon: hpIcon,
value: computed(() => {
const hp = core.formatBigNumber(s.hp);
const hpmax = core.formatBigNumber(s.hpmax);
return `${hp} / ${hpmax}`;
}),
font: font1,
color: '#fff'
});
} else {
// 如果禁用血限
statusInfo.push({
icon: hpIcon,
value: computed(() => core.formatBigNumber(s.hp)),
font: font1,
color: '#fff'
});
}
}
// 魔力
if (list.includes('enableMana')) {
statusInfo.push({
icon: manaIcon,
value: computed(() => {
const mana = core.formatBigNumber(s.mana);
const manamax = core.formatBigNumber(s.manamax);
if (s.manamax > 0) {
// 如果启用魔力上限
return `${mana} / ${manamax}`;
} else {
// 如果禁用魔力上限
return mana;
}
}),
font: font1,
color: '#fff'
});
}
// 攻击力
if (list.includes('enableAtk')) {
statusInfo.push({
icon: atkIcon,
value: computed(() => core.formatBigNumber(s.atk)),
font: font1,
color: '#fff'
});
}
// 防御力
if (list.includes('enableDef')) {
statusInfo.push({
icon: defIcon,
value: computed(() => core.formatBigNumber(s.def)),
font: font1,
color: '#fff'
});
}
// 魔防(护盾)
if (list.includes('enableMdef')) {
statusInfo.push({
icon: mdefIcon,
value: computed(() => core.formatBigNumber(s.mdef)),
font: font1,
color: '#fff'
});
}
// 金币
if (list.includes('enableMoney')) {
statusInfo.push({
icon: moneyIcon,
value: computed(() => core.formatBigNumber(s.money)),
font: font1,
color: '#fff'
});
}
// 经验值
if (list.includes('enableExp')) {
if (list.includes('enableLevelUp')) {
// 升级模式
statusInfo.push({
icon: expIcon,
value: computed(() => core.formatBigNumber(s.up)),
font: font1,
color: '#fff'
});
} else {
// 非升级模式
statusInfo.push({
icon: expIcon,
value: computed(() => core.formatBigNumber(s.exp)),
font: font1,
color: '#fff'
});
}
}
// 钥匙
if (list.includes('enableKeys')) {
const keys: KeyLikeItem[] = [];
keyLike.push({ items: keys });
// 黄钥匙
keys.push({
value: computed(() => key(s.yellowKey)),
font: font2,
color: '#fca'
});
// 蓝钥匙
keys.push({
value: computed(() => key(s.blueKey)),
font: font2,
color: '#aad'
});
// 红钥匙
keys.push({
value: computed(() => key(s.redKey)),
font: font2,
color: '#f88'
});
// 绿钥匙
if (list.includes('enableGreenKey')) {
keys.push({
value: computed(() => key(s.greenKey)),
font: font2,
color: '#8f8'
});
}
}
// 破炸飞
if (list.includes('enablePZF')) {
const items: KeyLikeItem[] = [];
keyLike.push({ items });
items.push({
value: computed(() => `${s.pickaxe}`),
font: font1,
color: '#bc6e27'
});
items.push({
value: computed(() => `${s.bomb}`),
font: font1,
color: '#fa14b9'
});
items.push({
value: computed(() => `${s.centerFly}`),
font: font1,
color: '#8db600'
});
}
// 毒衰咒
if (list.includes('enableDebuff')) {
const debuffs: KeyLikeItem[] = [];
keyLike.push({ items: debuffs });
debuffs.push({
value: computed(() => (s.poison ? '毒' : '')),
font: font1,
color: '#affca8'
});
debuffs.push({
value: computed(() => (s.weak ? '衰' : '')),
font: font1,
color: '#feccd0'
});
debuffs.push({
value: computed(() => (s.curse ? '咒' : '')),
font: font1,
color: '#c2f4e7'
});
}
//#region 布局控制
/** 用于显示状态的高度,高度=状态栏高度-工具栏高度-楼层名高度-填充高度 */
const statusHeight =
STATUS_BAR_HEIGHT - 113 - TITLE_HEIGHT - STATUS_PAD * 2;
/** 每一行的高度 */
const rowHeight = computed(() => {
const length = statusInfo.length + keyLike.length;
return statusHeight / length;
});
/** 钥匙、破炸飞、毒衰咒开始显示的纵坐标 */
const keyStart = computed(() => {
const statusHeight = statusInfo.length * rowHeight.value;
return STATUS_Y + statusHeight;
});
/**
*
* @param y
*/
const central = (y: number): ElementLocator => { const central = (y: number): ElementLocator => {
const width = p.loc[2] ?? 200; const width = p.loc[2] ?? 200;
return [width / 2, y, void 0, void 0, 0.5, 0.5]; return [width / 2, y, void 0, void 0, 0.5, 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];
};
const viewMap = () => { const viewMap = () => {
openViewMap(mainUIController, [0, 0, 840, 480]); openViewMap(mainUIController, [0, 0, MAIN_WIDTH, MAIN_HEIGHT]);
}; };
return () => ( return () => (
<container loc={p.loc} hidden={p.hidden}> <container loc={p.loc} hidden={p.hidden}>
<text <text
text={floorName.value} text={floorName.value}
loc={central(24)} loc={central(18)}
font={font1} font={font1}
cursor="pointer" cursor="pointer"
onClick={viewMap} onClick={viewMap}
></text> ></text>
<text text={s.lv} loc={central(54)} font={font1}></text> <g-line
<image image={hpIcon} loc={iconLoc(0)}></image> lineWidth={1}
<text text={f(s.hp)} loc={textLoc(0)} font={font1}></text> strokeStyle="#888"
<image image={atkIcon} loc={iconLoc(1)}></image> line={[0, TITLE_HEIGHT, STATUS_BAR_WIDTH, TITLE_HEIGHT]}
<text text={f(s.atk)} loc={textLoc(1)} font={font1}></text> />
<image image={defIcon} loc={iconLoc(2)}></image> {statusInfo
<text text={f(s.def)} loc={textLoc(2)} font={font1}></text> .map((v, i) => {
<image image={mdefIcon} loc={iconLoc(3)}></image> const h = rowHeight.value;
<text text={f(s.mdef)} loc={textLoc(3)} font={font1}></text> const y = STATUS_Y + i * h;
<image image={moneyIcon} loc={iconLoc(4)}></image> const cy = y + h / 2;
<text text={f(s.money)} loc={textLoc(4)} font={font1} /> const iconSize = Math.min(32, h * 0.8);
<image image={expIcon} loc={iconLoc(5)}></image> const pad = (h - iconSize) / 2;
<text text={f(s.exp)} loc={textLoc(5)} font={font1}></text> return [
<image
loc={[STATUS_PAD, y + pad, iconSize, iconSize]}
image={v.icon}
noanti // 取消抗锯齿
/>,
<text <text
text={key(s.yellowKey)} loc={[TEXT_X, cy]}
loc={keyLoc(0)} anc={[0, 0.5]}
font={font2} text={v.value.value}
fillStyle="#fca" fillStyle={v.color}
></text> font={v.font}
/>
];
})
.flat()}
{keyLike
.map(({ items }, i) => {
const h = rowHeight.value;
const y = keyStart.value + i * h;
const cy = y + h / 2;
const rw = STATUS_BAR_WIDTH / (items.length + 1);
return items.map((v, i) => {
const x = rw * (i + 1);
return (
<text <text
text={key(s.blueKey)} loc={[x, cy]}
loc={keyLoc(1)} anc={[0.5, 0.5]}
font={font2} text={v.value.value}
fillStyle="#aad" font={v.font}
></text> fillStyle={v.color}
<text />
text={key(s.redKey)} );
loc={keyLoc(2)} });
font={font2} })
fillStyle="#f88" .flat()}
></text>
<g-line <g-line
lineWidth={1} lineWidth={1}
strokeStyle="#888" strokeStyle="#888"
@ -144,22 +448,10 @@ export const LeftStatusBar = defineComponent<StatusBarProps<ILeftHeroStatus>>(
MAIN_HEIGHT - 113 MAIN_HEIGHT - 113
]} ]}
/> />
{inNumpad.value ? ( <MixedToolbar
<NumpadToolbar
loc={[0, MAIN_HEIGHT - 113, STATUS_BAR_WIDTH, 113]}
onNumpad={onNumpad}
/>
) : s.replay.replaying ? (
<ReplayingToolbar
loc={[0, MAIN_HEIGHT - 113, STATUS_BAR_WIDTH, 113]} loc={[0, MAIN_HEIGHT - 113, STATUS_BAR_WIDTH, 113]}
status={s.replay} status={s.replay}
/> />
) : (
<PlayingToolbar
loc={[0, MAIN_HEIGHT - 113, STATUS_BAR_WIDTH, 113]}
onNumpad={onNumpad}
/>
)}
</container> </container>
); );
}, },

View File

@ -155,8 +155,6 @@ export interface ReplayingStatus {
played: number; played: number;
/** 总长度 */ /** 总长度 */
total: number; total: number;
/** 是否是录像模式 */
replaying: boolean;
} }
export interface ReplayingProps extends ToolbarProps { export interface ReplayingProps extends ToolbarProps {

View File

@ -528,9 +528,7 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
"_type": "checkboxSet", "_type": "checkboxSet",
"_checkboxSet": { "_checkboxSet": {
"prefix": [ "prefix": [
"楼层", "等级",
"名字",
"<br>等级",
"血限", "血限",
"<br>生命", "<br>生命",
"魔力", "魔力",
@ -540,16 +538,12 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
"金币", "金币",
"<br>经验", "<br>经验",
"升级", "升级",
"<br>升级扣除模式",
"<br>钥匙", "<br>钥匙",
"绿钥", "绿钥",
"<br>破炸", "<br>破炸",
"负面", "负面"
"<br>技能"
], ],
"key": [ "key": [
"enableFloor",
"enableName",
"enableLv", "enableLv",
"enableHPMax", "enableHPMax",
"enableHP", "enableHP",
@ -560,12 +554,10 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
"enableMoney", "enableMoney",
"enableExp", "enableExp",
"enableLevelUp", "enableLevelUp",
"levelUpLeftMode",
"enableKeys", "enableKeys",
"enableGreenKey", "enableGreenKey",
"enablePZF", "enablePZF",
"enableDebuff", "enableDebuff"
"enableSkill"
] ]
}, },
"_data": "状态栏显示项" "_data": "状态栏显示项"

View File

@ -803,16 +803,12 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
}, },
"flags": { "flags": {
"statusBarItems": [ "statusBarItems": [
"enableFloor",
"enableLv",
"enableHP", "enableHP",
"enableAtk", "enableAtk",
"enableDef", "enableDef",
"enableMDef", "enableMDef",
"enableMoney", "enableMoney",
"enableExp", "enableExp",
"enableLevelUp",
"levelUpLeftMode",
"enableKeys" "enableKeys"
], ],
"flyNearStair": false, "flyNearStair": false,
@ -836,7 +832,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"enableRouteFolding": true, "enableRouteFolding": true,
"disableShopOnDamage": false, "disableShopOnDamage": false,
"blurFg": true, "blurFg": true,
"extendToolbar": true, "extendToolbar": false,
"enableEnemyPoint": null, "enableEnemyPoint": null,
"autoScale": true "autoScale": true
} }