mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-18 20:09:27 +08:00
Compare commits
3 Commits
dbae5a7c7d
...
39baab94ae
Author | SHA1 | Date | |
---|---|---|---|
39baab94ae | |||
b679cadd1b | |||
40c4120445 |
@ -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: {}
|
||||
|
@ -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,
|
||||
|
@ -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*蓝钥匙"
|
||||
|
@ -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 |
@ -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) {
|
||||
|
1
src/core/render/components/index.ts
Normal file
1
src/core/render/components/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './textbox';
|
191
src/core/render/components/textbox.tsx
Normal file
191
src/core/render/components/textbox.tsx
Normal 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;
|
||||
}
|
@ -87,3 +87,4 @@ export * from './shader';
|
||||
export * from './sprite';
|
||||
export * from './transform';
|
||||
export * from './utils';
|
||||
export * from './components';
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user