feat: 默认状态栏

This commit is contained in:
unanmed 2025-09-28 23:37:28 +08:00
parent 8f0852822b
commit d86a9616d7
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 ENABLE_RIGHT_STATUS_BAR = true;
export const ENABLE_RIGHT_STATUS_BAR = false;
/** 状态栏数量,启用右侧状态栏为两个,不启用为一个 */
export const STATUS_BAR_COUNT = ENABLE_RIGHT_STATUS_BAR ? 2 : 1;
/** 状态栏宽度的一半 */

View File

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

View File

@ -1,29 +1,68 @@
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 { DefaultProps, ElementLocator, Font } from '@motajs/render';
import {
NumpadToolbar,
PlayingToolbar,
ReplayingStatus,
ReplayingToolbar
} from './toolbar';
DefaultProps,
ElementLocator,
Font,
SizedCanvasImageSource
} from '@motajs/render';
import { MixedToolbar, ReplayingStatus } from './toolbar';
import { openViewMap } from './viewmap';
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 {
hp: number;
atk: number;
def: number;
mdef: number;
money: number;
exp: number;
yellowKey: number;
blueKey: number;
redKey: number;
/** 楼层 id */
floor: FloorIds;
/** 等级名称 */
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;
}
@ -32,6 +71,31 @@ export interface IRightHeroStatus {
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 {
loc: ElementLocator;
status: T;
@ -44,96 +108,336 @@ const statusBarProps = {
export const LeftStatusBar = defineComponent<StatusBarProps<ILeftHeroStatus>>(
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'];
//#region 参数定义
/** 属性文字的横坐标 */
const TEXT_X = 54;
/** 图标的横坐标 */
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 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 key = (num: number) => {
return num.toString().padStart(2, '0');
};
const onNumpad = () => {
inNumpad.value = !inNumpad.value;
};
//#region 属性显示
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 => {
return [60, 92 + 44 * n, void 0, void 0, 0, 0.5];
};
const list = core.flags.statusBarItems;
// 等级
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 width = p.loc[2] ?? 200;
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 = () => {
openViewMap(mainUIController, [0, 0, 840, 480]);
openViewMap(mainUIController, [0, 0, MAIN_WIDTH, MAIN_HEIGHT]);
};
return () => (
<container loc={p.loc} hidden={p.hidden}>
<text
text={floorName.value}
loc={central(24)}
loc={central(18)}
font={font1}
cursor="pointer"
onClick={viewMap}
></text>
<text text={s.lv} loc={central(54)} font={font1}></text>
<image image={hpIcon} loc={iconLoc(0)}></image>
<text text={f(s.hp)} loc={textLoc(0)} font={font1}></text>
<image image={atkIcon} loc={iconLoc(1)}></image>
<text text={f(s.atk)} loc={textLoc(1)} font={font1}></text>
<image image={defIcon} loc={iconLoc(2)}></image>
<text text={f(s.def)} loc={textLoc(2)} font={font1}></text>
<image image={mdefIcon} loc={iconLoc(3)}></image>
<text text={f(s.mdef)} loc={textLoc(3)} font={font1}></text>
<image image={moneyIcon} loc={iconLoc(4)}></image>
<text text={f(s.money)} loc={textLoc(4)} font={font1} />
<image image={expIcon} loc={iconLoc(5)}></image>
<text text={f(s.exp)} loc={textLoc(5)} font={font1}></text>
<text
text={key(s.yellowKey)}
loc={keyLoc(0)}
font={font2}
fillStyle="#fca"
></text>
<text
text={key(s.blueKey)}
loc={keyLoc(1)}
font={font2}
fillStyle="#aad"
></text>
<text
text={key(s.redKey)}
loc={keyLoc(2)}
font={font2}
fillStyle="#f88"
></text>
<g-line
lineWidth={1}
strokeStyle="#888"
line={[0, TITLE_HEIGHT, STATUS_BAR_WIDTH, TITLE_HEIGHT]}
/>
{statusInfo
.map((v, i) => {
const h = rowHeight.value;
const y = STATUS_Y + i * h;
const cy = y + h / 2;
const iconSize = Math.min(32, h * 0.8);
const pad = (h - iconSize) / 2;
return [
<image
loc={[STATUS_PAD, y + pad, iconSize, iconSize]}
image={v.icon}
noanti // 取消抗锯齿
/>,
<text
loc={[TEXT_X, cy]}
anc={[0, 0.5]}
text={v.value.value}
fillStyle={v.color}
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
loc={[x, cy]}
anc={[0.5, 0.5]}
text={v.value.value}
font={v.font}
fillStyle={v.color}
/>
);
});
})
.flat()}
<g-line
lineWidth={1}
strokeStyle="#888"
@ -144,22 +448,10 @@ export const LeftStatusBar = defineComponent<StatusBarProps<ILeftHeroStatus>>(
MAIN_HEIGHT - 113
]}
/>
{inNumpad.value ? (
<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]}
status={s.replay}
/>
) : (
<PlayingToolbar
loc={[0, MAIN_HEIGHT - 113, STATUS_BAR_WIDTH, 113]}
onNumpad={onNumpad}
/>
)}
<MixedToolbar
loc={[0, MAIN_HEIGHT - 113, STATUS_BAR_WIDTH, 113]}
status={s.replay}
/>
</container>
);
},

View File

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

View File

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

View File

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