Compare commits

...

3 Commits

Author SHA1 Message Date
39baab94ae feat: 文本框自动分词断行 2024-12-09 23:34:23 +08:00
b679cadd1b fix: 商店录像报错 2024-12-09 23:33:51 +08:00
40c4120445 refactor: 开头剧情 2024-12-09 23:33:25 +08:00
10 changed files with 245 additions and 50 deletions

View File

@ -2663,6 +2663,11 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
nanoid@3.3.8:
resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
nanopop@2.4.2:
resolution: {integrity: sha512-NzOgmMQ+elxxHeIha+OG/Pv3Oc3p4RU2aBhwWwAqDpXrdTbtRylbRLQztLy8dMMwfl6pclznBdfUhccEn9ZIzw==}
@ -2784,8 +2789,8 @@ packages:
picocolors@1.0.1:
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
picocolors@1.1.0:
resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
@ -6275,6 +6280,8 @@ snapshots:
nanoid@3.3.7: {}
nanoid@3.3.8: {}
nanopop@2.4.2: {}
needle@3.3.1:
@ -6396,7 +6403,7 @@ snapshots:
picocolors@1.0.1: {}
picocolors@1.1.0: {}
picocolors@1.1.1: {}
picomatch@2.3.1: {}
@ -6627,8 +6634,8 @@ snapshots:
postcss@8.4.47:
dependencies:
nanoid: 3.3.7
picocolors: 1.1.0
nanoid: 3.3.8
picocolors: 1.1.1
source-map-js: 1.2.1
process-nextick-args@2.0.1: {}

View File

@ -550,6 +550,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
},
{
"type": "setText",
"position": "down",
"text": [
0,
0,
@ -570,78 +571,63 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"type": "playSound",
"name": "paper.mp3"
},
"人们说要铭记历史,但他们却忘记了历史。\n ——我是这样评价这个故事的。",
{
"type": "playSound",
"name": "paper.mp3"
},
"人类简史——起源篇",
{
"type": "playSound",
"name": "paper.mp3"
},
"在万物的发展中,任何物体都有它自己的发光点。",
"在历史的长河中,山火、暴雨、地震不过是自然界的常态,是时间流逝中微不足道的涟漪。",
{
"type": "playSound",
"name": "paper.mp3"
},
"人类,这个起初完全不起眼的种族,",
"这些自然现象如同大地的呼吸,时而平静,时而狂暴。",
{
"type": "playSound",
"name": "paper.mp3"
},
"却在那一天发生了惊天的变动。",
"对于动物和植物而言,这些变化是生存的考验,是自然选择的无情法则。",
{
"type": "playSound",
"name": "paper.mp3"
},
"那曾是一个不起眼的日子。",
"每一次山火,都意味着森林的重生与毁灭;每一场暴雨,都带来了生命的滋润与洪水的威胁;每一次地震,都改变了地貌,塑造了新的环境。",
{
"type": "playSound",
"name": "paper.mp3"
},
"直到一记闪电劈在了山上。",
"在这片土地上,生命在自然的力量中挣扎、适应、繁衍。",
{
"type": "playSound",
"name": "paper.mp3"
},
"山火蔓延,霎时间,茂密的树林已然变为了焦炭。",
"那些无法适应的,最终被淘汰;而那些幸存者,则继续在这片土地上书写着生命的传奇。",
{
"type": "playSound",
"name": "paper.mp3"
},
"山火的发生让野兽饥不择食,",
"然而,对于那些在这片土地上生存的原始人而言,这些自然现象不仅仅是生存的考验,更是他们日常生活中不可或缺的一部分。",
{
"type": "playSound",
"name": "paper.mp3"
},
"无数的人类被野兽硬生生拖走。",
"在公元前8000年这里曾有一个不起眼的山洞隐匿于群山之间仿佛与世隔绝。山洞中原始人正忙碌着准备迎接即将到来的季节变化。",
{
"type": "playSound",
"name": "paper.mp3"
},
"那又是一个不起眼的日子,",
{
"type": "playSound",
"name": "paper.mp3"
},
"却让人类又一次发生了翻天地覆的变化。",
{
"type": "playSound",
"name": "paper.mp3"
},
"一位野蛮人,也是我们的主角,",
{
"type": "playSound",
"name": "paper.mp3"
},
"踏上了属于他的旅途。",
{
"type": "sleep",
"time": 1000
},
{
"type": "playSound",
"name": "paper.mp3"
},
"公元前8000年",
{
"type": "setText",
"position": "down",
"text": [
255,
255,

View File

@ -39,10 +39,14 @@ main.floors.MT0=
"time": 500,
"keep": true
},
"\t[原始人]\b[up,hero]家里有没有柴火了,看来需要上山砍柴了啊。",
"\t[原始人]\b[up,hero]刚刚经历过山火,山上的柴火也不多了。",
"\t[原始人]\b[up,hero]为什么这么倒霉的事会摊在我头上。",
"\t[原始人]\b[up,hero]哎,不管了,先出去看看再说。",
"\t[原始人]又到了秋天,天气开始变凉了",
"秋风从石头的缝隙中穿过,原始人站在山洞中,感受着秋风的凉意,心中涌起一丝熟悉的紧迫感。",
"他知道,随着秋天的到来,山上的树木将变得干燥,而山火的风险也会随之增加。",
"他也知道,每一次暴雨过后,山洞外的河流可能会泛滥,淹没他们的狩猎场。",
"\t[原始人]柴火的消耗逐渐变多了,看来需要再上山砍柴了啊。",
"他从石头的缝隙中看去,看到满山的枫叶在秋风中摇曳,仿佛在提醒他时间的流逝。",
"这些自然的变迁,虽然无情,却也教会了他如何适应和生存。",
"\t[原始人]今天的天气似乎不错,那就上山看看吧。",
"\r[red]注意!!!\r[]该塔新增了很多新的功能同时对样板的ui进行了大幅度的改动操作也有改变由于内容过多这里不再一一描述具体请在道具栏查看百科全书百科全书是在你面前的几个道具中的其中一个",
{
"type": "function",
@ -58,8 +62,7 @@ main.floors.MT0=
"本塔有很多新的功能,所有的说明都详细地写在了前方的百科全书里面,里面包含所有的功能说明,不阅读可能会影响正常的游戏体验,请仔细阅读。",
"例如你现在首先感受到的应该是状态栏的变动,你可以打开百科全书阅读状态栏相关内容。里面包含状态栏的功能说明与布局说明等。",
"注意百科全书中的内容非常基础详细,如果对魔塔有一定的了解,可以选择性地阅读。",
"打开百科全书的快捷键是H",
"特别提醒:本游戏没有考虑录像的二次播放性,因此如果你播放录像之后继续游玩,最后可能会导致提交成绩后红录像。"
"打开百科全书的快捷键是H"
],
"8,12": [
"第一章计分方式:生命+5000*黄钥匙+15000*蓝钥匙"

View File

@ -14,10 +14,10 @@ main.floors.MT1=
"defaultGround": "T331",
"bgm": "cave.mp3",
"firstArrive": [
"\t[原始人]\b[up,hero]呼,今天也有这些黏糊糊的东西啊。",
"\t[原始人]\b[up,hero]真是奇怪,自从那次山火之后这里就出现了这些东西。",
"\t[原始人]\b[up,hero]真是搞不清他们的来历。",
"\t[原始人]\b[up,hero]不过好在他们反抗能力很弱,随便打打就能打过去了。",
"\t[原始人]不知为何,最近这些蝙蝠的攻击性变得很强,而且还不知道从哪冒出来了这些黏糊糊的东西。",
"\t[原始人]之前捡到了一个来历不明的方块状东西(怪物手册),好像能打开,不知道里面有没有写什么",
"\t[原始人]或许会有一些东西吧,但愿是我能看懂的文字,别像之前杰克给我的东西一样,完全看不懂是什么文字。",
"\t[系统提示]游戏中每个怪物都有自己的说明,这些说明不会影响正常的剧情流程,但查看它们可以更好地了解本游戏的世界观,剧情玩家建议阅读。",
{
"type": "if",
"condition": "(flag:hard===1)",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -147,7 +147,6 @@ function showSpecialSetting(id: string, vBind?: any) {
mainUi.showAll();
});
mainUi.open(id, vBind);
console.log(core.status.lockControl);
}
function HotkeySetting(props: SettingComponentProps) {

View File

@ -0,0 +1 @@
export * from './textbox';

View File

@ -0,0 +1,191 @@
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
import { defineComponent } from 'vue';
export const enum WordBreak {
/** 不换行 */
None,
/** 仅空格和连字符等可换行CJK 字符可任意换行,默认值 */
Space,
/** 所有字符都可以换行 */
All
}
export interface TextContentProps {
text: string;
x?: number;
y?: number;
width?: number;
height?: number;
font?: string;
/** 打字机时间间隔,即两个字出现之间相隔多长时间 */
interval?: number;
/** 行高 */
lineHeight?: number;
/** 分词规则 */
wordBreak?: WordBreak;
/** 行首忽略字符,即不会出现在行首的字符 */
ignoreLineStart?: Iterable<string>;
/** 行尾忽略字符,即不会出现在行尾的字符 */
ignoreLineEnd?: Iterable<string>;
}
interface TextContentData {
text: string;
width: number;
font: string;
/** 分词规则 */
wordBreak: WordBreak;
/** 行首忽略字符,即不会出现在行首的字符 */
ignoreLineStart: Set<string>;
/** 行尾忽略字符,即不会出现在行尾的字符 */
ignoreLineEnd: Set<string>;
/** 会被分词规则识别的文字 */
breakChars: Set<string>;
}
export const TextContent = defineComponent((props, ctx) => {
return () => {};
});
export const Textbox = defineComponent((props, ctx) => {
return () => {};
});
let testCanvas: MotaOffscreenCanvas2D;
Mota.require('var', 'loading').once('coreInit', () => {
testCanvas = new MotaOffscreenCanvas2D(false);
testCanvas.withGameScale(false);
testCanvas.setHD(false);
testCanvas.size(32, 32);
testCanvas.freeze();
});
/**
*
* @param data
*/
function splitLines(data: TextContentData) {
const words = breakWords(data);
if (words.length === 1) return [words[0]];
// 对文字二分,然后计算长度
const text = data.text;
let start = 0;
let end = words.length;
let resolved = 0;
let mid = 0;
const res: number[] = [];
const ctx = testCanvas.ctx;
ctx.font = data.font;
console.time();
while (1) {
const diff = end - start;
if (diff === 1) {
const data1 = ctx.measureText(
text.slice(words[resolved], words[end])
);
if (data1.width <= data.width) {
res.push(words[end - 1]);
} else {
res.push(words[start]);
}
if (end === words.length) break;
resolved = start;
end = words.length;
} else {
mid = Math.floor((start + end) / 2);
const chars = text.slice(words[resolved], words[mid]);
const { width } = ctx.measureText(chars);
if (width <= data.width) {
start = mid;
if (start === end) end++;
} else {
end = mid;
if (start === end) end++;
}
}
}
console.timeEnd();
return res;
}
const defaultsBreak = ' -,.)]}?!;:,。)】?!;:';
const breakSet = new Set(defaultsBreak);
/**
* CJK
* @param char
*/
function isCJK(char: number) {
// 参考自 https://blog.csdn.net/brooksychen/article/details/2755395
return (
(char >= 0x4e00 && char <= 0x9fff) ||
(char >= 0x3000 && char <= 0x30ff) ||
(char >= 0xac00 && char <= 0xd7af) ||
(char >= 0xf900 && char <= 0xfaff) ||
(char >= 0x3400 && char <= 0x4dbf) ||
(char >= 0x20000 && char <= 0x2ebef) ||
(char >= 0x30000 && char <= 0x323af) ||
(char >= 0x2e80 && char <= 0x2eff) ||
(char >= 0x31c0 && char <= 0x31ef)
);
}
/**
*
* @param data
*/
function breakWords(data: TextContentData) {
let allBreak = false;
const breakChars = breakSet.union(data.breakChars);
switch (data.wordBreak) {
case WordBreak.None: {
return [data.text.length];
}
case WordBreak.Space: {
allBreak = false;
break;
}
case WordBreak.All: {
allBreak = true;
break;
}
}
console.time();
const res: number[] = [0];
const text = data.text;
const { ignoreLineStart, ignoreLineEnd } = data;
for (let pointer = 0; pointer < text.length; pointer++) {
const char = text[pointer];
const next = text[pointer + 1];
if (!ignoreLineEnd.has(char) && ignoreLineEnd.has(next)) {
res.push(pointer);
continue;
}
if (ignoreLineStart.has(char) && !ignoreLineStart.has(next)) {
res.push(pointer);
continue;
}
if (
breakChars.has(char) ||
allBreak ||
char === '\n' ||
isCJK(char.charCodeAt(0))
) {
res.push(pointer);
continue;
}
}
res.push(text.length);
console.timeEnd();
return res;
}

View File

@ -87,3 +87,4 @@ export * from './shader';
export * from './sprite';
export * from './transform';
export * from './utils';
export * from './components';

View File

@ -1,5 +1,6 @@
import { HeroSkill } from '@/game/mechanism/misc';
import { getSkillFromIndex, upgradeSkill } from './skillTree';
import { canOpenShop } from './shop';
const replayableSettings = ['autoSkill'];
@ -73,9 +74,17 @@ export function init() {
// 商店
let shopOpened = false;
let openedShopId = '';
Mota.require('var', 'hook').on('reset', () => {
shopOpened = false;
openedShopId = '';
});
core.registerReplayAction('openShop', name => {
if (!name.startsWith('openShop:')) return false;
if (shopOpened) return false;
const id = name.slice(9);
if (!canOpenShop(id)) return false;
if (shopOpened && openedShopId === id) return true;
openedShopId = name.slice(9);
shopOpened = true;
core.status.route.push(name);
@ -87,9 +96,8 @@ export function init() {
if (!name.startsWith('buy:') && !name.startsWith('sell:')) return false;
if (!shopOpened) return false;
if (!openedShopId) return false;
const [type, id, num] = name
.split(':')
.map(v => (/^\d+$/.test(v) ? parseInt(v) : v));
const [type, id, n] = name.split(':');
const num = parseInt(n);
const shop = core.status.shops[openedShopId] as ItemShopEvent;
const item = shop.choices.find(v => v.id === id);
if (!item) return false;
@ -122,7 +130,6 @@ export function init() {
core.registerReplayAction('closeShop', name => {
if (name !== 'closeShop') return false;
if (!shopOpened) return false;
shopOpened = false;
openedShopId = '';
core.status.route.push(name);