webgal-parser 4.6.2 → 4.6.3-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/es/index.js CHANGED
@@ -35,6 +35,7 @@ var commandType;
35
35
  commandType[commandType["applyStyle"] = 32] = "applyStyle";
36
36
  commandType[commandType["wait"] = 33] = "wait";
37
37
  commandType[commandType["callSteam"] = 34] = "callSteam";
38
+ commandType[commandType["return"] = 35] = "return";
38
39
  })(commandType || (commandType = {}));
39
40
 
40
41
  const SCRIPT_CONFIG = [
@@ -76,6 +77,7 @@ const SCRIPT_CONFIG = [
76
77
  { scriptString: 'applyStyle', scriptType: commandType.applyStyle },
77
78
  { scriptString: 'wait', scriptType: commandType.wait },
78
79
  { scriptString: 'callSteam', scriptType: commandType.callSteam },
80
+ { scriptString: 'return', scriptType: commandType.return },
79
81
  ];
80
82
  const ADD_NEXT_ARG_LIST = [
81
83
  commandType.bgm,
@@ -134,7 +136,7 @@ function argsParser(argsRaw, assetSetter) {
134
136
  argValue = undefined;
135
137
  }
136
138
  // 判断是不是语音参数
137
- if (argName.toLowerCase().match(/.ogg|.mp3|.wav/)) {
139
+ if (argName.toLowerCase().match(/.ogg|.mp3|.wav|.opus/)) {
138
140
  returnArrayList.push({
139
141
  key: 'vocal',
140
142
  value: assetSetter(e, fileType.vocal),
@@ -459,7 +461,10 @@ const scriptParser = (sentenceRaw, assetSetter, ADD_NEXT_ARG_LIST, SCRIPT_CONFIG
459
461
  args: [{ key: 'next', value: true }],
460
462
  sentenceAssets: [],
461
463
  subScene: [],
462
- inlineComment: '', // 行内注释
464
+ inlineComment: '',
465
+ startLine: lineNumber,
466
+ endLine: lineNumber,
467
+ isLineBreakHolder: false,
463
468
  };
464
469
  }
465
470
  // 截取命令
@@ -510,92 +515,14 @@ const scriptParser = (sentenceRaw, assetSetter, ADD_NEXT_ARG_LIST, SCRIPT_CONFIG
510
515
  args: args,
511
516
  sentenceAssets: sentenceAssets,
512
517
  subScene: subScene,
513
- inlineComment: sentenceComment.trim(), // 行内注释
518
+ inlineComment: sentenceComment.trim(),
519
+ // 单行语句的行范围就是自己,多行语句的 endLine 由 sceneParser 回填
520
+ startLine: lineNumber,
521
+ endLine: lineNumber,
522
+ isLineBreakHolder: false,
514
523
  };
515
524
  };
516
525
 
517
- /**
518
- * 场景解析器
519
- * @param rawScene 原始场景
520
- * @param sceneName 场景名称
521
- * @param sceneUrl 场景url
522
- * @param assetsPrefetcher
523
- * @param assetSetter
524
- * @param ADD_NEXT_ARG_LIST
525
- * @param SCRIPT_CONFIG_MAP
526
- * @return {IScene} 解析后的场景
527
- */
528
- const sceneParser = (rawScene, sceneName, sceneUrl, assetsPrefetcher, assetSetter, ADD_NEXT_ARG_LIST, SCRIPT_CONFIG_MAP) => {
529
- const rawSentenceList = rawScene.replaceAll('\r', '').split('\n'); // 原始句子列表
530
- // 去分号留到后面去做了,现在注释要单独处理
531
- const rawSentenceListWithoutEmpty = rawSentenceList;
532
- // .map((sentence) => sentence.split(";")[0])
533
- // .filter((sentence) => sentence.trim() !== "");
534
- let assetsList = []; // 场景资源列表
535
- let subSceneList = []; // 子场景列表
536
- const sentenceList = rawSentenceListWithoutEmpty.map((sentence, index) => {
537
- const returnSentence = scriptParser(sentence, assetSetter, ADD_NEXT_ARG_LIST, SCRIPT_CONFIG_MAP, index);
538
- // 在这里解析出语句可能携带的资源和场景,合并到 assetsList 和 subSceneList
539
- assetsList = [...assetsList, ...returnSentence.sentenceAssets];
540
- subSceneList = [...subSceneList, ...returnSentence.subScene];
541
- return returnSentence;
542
- });
543
- // 开始资源的预加载
544
- assetsList = deduplicateAssets(assetsList);
545
- assetsPrefetcher(assetsList);
546
- return {
547
- sceneName: sceneName,
548
- sceneUrl: sceneUrl,
549
- sentenceList: sentenceList,
550
- assetsList: assetsList,
551
- subSceneList: subSceneList, // 子场景列表
552
- };
553
- };
554
- const deduplicateAssets = (assetsList) => {
555
- const seenAssets = new Set();
556
- return assetsList.filter((asset) => {
557
- if (!asset || typeof asset.url !== 'string' || asset.url === '') {
558
- return false;
559
- }
560
- const assetKey = `${asset.type}:${asset.url}`;
561
- if (seenAssets.has(assetKey)) {
562
- return false;
563
- }
564
- seenAssets.add(assetKey);
565
- return true;
566
- });
567
- };
568
-
569
- function scss2cssinjsParser(scssString) {
570
- const [classNameStyles, others] = parseCSS(scssString);
571
- return {
572
- classNameStyles,
573
- others,
574
- };
575
- }
576
- /**
577
- * GPT 4 写的,临时用,以后要重构!!!
578
- * TODO:用人类智能重构,要是用着一直没问题,也不是不可以 trust AI
579
- * @param css
580
- */
581
- function parseCSS(css) {
582
- const result = {};
583
- let specialRules = '';
584
- let matches;
585
- // 使用非贪婪匹配,尝试正确处理任意层次的嵌套
586
- const classRegex = /\.([^{\s]+)\s*{((?:[^{}]*|{[^}]*})*)}/g;
587
- const specialRegex = /(@[^{]+{\s*(?:[^{}]*{[^}]*}[^{}]*)+\s*})/g;
588
- while ((matches = classRegex.exec(css)) !== null) {
589
- const key = matches[1];
590
- const value = matches[2].trim().replace(/\s*;\s*/g, ';\n');
591
- result[key] = value;
592
- }
593
- while ((matches = specialRegex.exec(css)) !== null) {
594
- specialRules += matches[1].trim() + '\n';
595
- }
596
- return [result, specialRules.trim()];
597
- }
598
-
599
526
  /**
600
527
  * Preprocessor for scene text.
601
528
  *
@@ -625,13 +552,11 @@ function sceneTextPreProcessPassOne(lines) {
625
552
  let lastLineIsMultiline = false;
626
553
  let thisLineIsMultiline = false;
627
554
  for (const line of lines) {
628
- thisLineIsMultiline = false;
629
- if (canBeMultiline(line)) {
630
- thisLineIsMultiline = true;
631
- }
632
- if (shouldNotBeMultiline(line, lastLineIsMultiline)) {
633
- thisLineIsMultiline = false;
634
- }
555
+ // 续行需要接到上一行,所以场景的第一行永远不会是续行。
556
+ thisLineIsMultiline =
557
+ processedLines.length > 0 &&
558
+ canBeMultiline(line) &&
559
+ !shouldNotBeMultiline(line, lastLineIsMultiline);
635
560
  if (thisLineIsMultiline) {
636
561
  processedLines[processedLines.length - 1] += '\\';
637
562
  }
@@ -689,7 +614,6 @@ function sceneTextPreProcessPassTwo(lines) {
689
614
  placeHolderLines.push(placeholderLine(line));
690
615
  }
691
616
  for (const line of lines) {
692
- console.log(line);
693
617
  if (line.endsWith('\\')) {
694
618
  const trueLine = line.slice(0, -1);
695
619
  if (currentMultilineContent === "") {
@@ -713,8 +637,24 @@ function sceneTextPreProcessPassTwo(lines) {
713
637
  }
714
638
  processedLines.push(line);
715
639
  }
640
+ // 场景末行仍以 \ 结尾时循环里没有机会收尾,在这里把累积的内容补出去。
641
+ if (currentMultilineContent !== "") {
642
+ processedLines.push(currentMultilineContent, ...placeHolderLines);
643
+ }
716
644
  return processedLines;
717
645
  }
646
+ /**
647
+ * 占位行的前缀。占位行本身是一条注释,引擎会空跑掉;
648
+ * 它的作用是让预处理后的行数与原始场景一致,从而保持
649
+ * 「解析后语句 index == 文件行号」这一不变量。
650
+ */
651
+ const WEBGAL_LINE_BREAK_MARK = ';_WEBGAL_LINE_BREAK_';
652
+ /**
653
+ * 判断预处理后的某一行是否是占位行,即它是上一条多行语句折叠掉的续行。
654
+ */
655
+ function isLineBreakPlaceholder(processedLine) {
656
+ return processedLine.startsWith(WEBGAL_LINE_BREAK_MARK);
657
+ }
718
658
  /**
719
659
  * Placeholder Line. Adding this line preserves the original number of lines
720
660
  * in the scene text, so that it can be compatible with the graphical editor.
@@ -723,70 +663,116 @@ function sceneTextPreProcessPassTwo(lines) {
723
663
  * @returns The placeholder line
724
664
  */
725
665
  function placeholderLine(content = "") {
726
- return ";_WEBGAL_LINE_BREAK_" + content;
666
+ return WEBGAL_LINE_BREAK_MARK + content;
667
+ }
668
+
669
+ /**
670
+ * 场景解析器
671
+ * @param rawScene 原始场景
672
+ * @param sceneName 场景名称
673
+ * @param sceneUrl 场景url
674
+ * @param assetsPrefetcher
675
+ * @param assetSetter
676
+ * @param ADD_NEXT_ARG_LIST
677
+ * @param SCRIPT_CONFIG_MAP
678
+ * @return {IScene} 解析后的场景
679
+ */
680
+ const sceneParser = (rawScene, sceneName, sceneUrl, assetsPrefetcher, assetSetter, ADD_NEXT_ARG_LIST, SCRIPT_CONFIG_MAP) => {
681
+ // 预处理把多行语句折叠进它的首行,并用占位行补齐被折叠掉的行,
682
+ // 因此这里的行数与原始场景严格一致,「语句 index == 文件行号」始终成立。
683
+ const rawSentenceList = sceneTextPreProcess(rawScene).split('\n'); // 原始句子列表
684
+ // 去分号留到后面去做了,现在注释要单独处理
685
+ const rawSentenceListWithoutEmpty = rawSentenceList;
686
+ // .map((sentence) => sentence.split(";")[0])
687
+ // .filter((sentence) => sentence.trim() !== "");
688
+ let assetsList = []; // 场景资源列表
689
+ let subSceneList = []; // 子场景列表
690
+ const sentenceList = rawSentenceListWithoutEmpty.map((sentence, index) => {
691
+ const returnSentence = scriptParser(sentence, assetSetter, ADD_NEXT_ARG_LIST, SCRIPT_CONFIG_MAP, index);
692
+ // 在这里解析出语句可能携带的资源和场景,合并到 assetsList 和 subSceneList
693
+ assetsList = [...assetsList, ...returnSentence.sentenceAssets];
694
+ subSceneList = [...subSceneList, ...returnSentence.subScene];
695
+ return returnSentence;
696
+ });
697
+ markMultilineRanges(sentenceList, rawSentenceList);
698
+ // 开始资源的预加载
699
+ assetsList = deduplicateAssets(assetsList);
700
+ assetsPrefetcher(assetsList);
701
+ return {
702
+ sceneName: sceneName,
703
+ sceneUrl: sceneUrl,
704
+ sentenceList: sentenceList,
705
+ assetsList: assetsList,
706
+ subSceneList: subSceneList, // 子场景列表
707
+ };
708
+ };
709
+ /**
710
+ * 回填多行语句的行范围。
711
+ *
712
+ * 预处理后,一条多行语句的完整内容都落在它的首行,紧跟其后的若干占位行
713
+ * 就是被折叠掉的续行。这里把占位行标记出来,并把首行语句的 endLine
714
+ * 推到最后一条续行,图形编辑器据此按整个行范围替换语句。
715
+ *
716
+ * @param sentenceList 解析后的语句列表
717
+ * @param processedLines 预处理后的行,与 sentenceList 一一对应
718
+ */
719
+ const markMultilineRanges = (sentenceList, processedLines) => {
720
+ let headIndex = -1; // 最近一条非占位语句的下标,即当前多行语句的首行
721
+ for (let index = 0; index < sentenceList.length; index++) {
722
+ if (!isLineBreakPlaceholder(processedLines[index])) {
723
+ headIndex = index;
724
+ continue;
725
+ }
726
+ sentenceList[index].isLineBreakHolder = true;
727
+ if (headIndex >= 0) {
728
+ sentenceList[headIndex].endLine = index;
729
+ }
730
+ }
731
+ };
732
+ const deduplicateAssets = (assetsList) => {
733
+ const seenAssets = new Set();
734
+ return assetsList.filter((asset) => {
735
+ if (!asset || typeof asset.url !== 'string' || asset.url === '') {
736
+ return false;
737
+ }
738
+ const assetKey = `${asset.type}:${asset.url}`;
739
+ if (seenAssets.has(assetKey)) {
740
+ return false;
741
+ }
742
+ seenAssets.add(assetKey);
743
+ return true;
744
+ });
745
+ };
746
+
747
+ function scss2cssinjsParser(scssString) {
748
+ const [classNameStyles, others] = parseCSS(scssString);
749
+ return {
750
+ classNameStyles,
751
+ others,
752
+ };
727
753
  }
728
- // export function sceneTextPreProcess(sceneText: string): string {
729
- // const lines = sceneText.replaceAll('\r', '').split('\n');
730
- // const processedLines: string[] = [];
731
- // let lastNonMultilineIndex = -1;
732
- // let isInMultilineSequence = false;
733
- // function isMultiline(line: string): boolean {
734
- // if (!line.startsWith(' ')) return false;
735
- // const trimmedLine = line.trimStart();
736
- // return trimmedLine.startsWith('|') || trimmedLine.startsWith('-');
737
- // }
738
- // for (let i = 0; i < lines.length; i++) {
739
- // const line = lines[i];
740
- // if (line.trim() === '') {
741
- // // Empty line handling
742
- // if (isInMultilineSequence) {
743
- // // Check if the next line is a multiline line
744
- // let isStillInMulti = false;
745
- // for (let j = i + 1; j < lines.length; j++) {
746
- // const lookForwardLine = lines[j] || '';
747
- // // 遇到正常语句了,直接中断
748
- // if (lookForwardLine.trim() !== '' && !isMultiline(lookForwardLine)) {
749
- // isStillInMulti = false;
750
- // break;
751
- // }
752
- // // 必须找到后面接的是参数,并且中间没有遇到任何正常语句才行
753
- // if (lookForwardLine.trim() !== '' && isMultiline(lookForwardLine)) {
754
- // isStillInMulti = true;
755
- // break;
756
- // }
757
- // }
758
- // if (isStillInMulti) {
759
- // // Still within a multiline sequence
760
- // processedLines.push(';_WEBGAL_LINE_BREAK_');
761
- // } else {
762
- // // End of multiline sequence
763
- // isInMultilineSequence = false;
764
- // processedLines.push(line);
765
- // }
766
- // } else {
767
- // // Preserve empty lines outside of multiline sequences
768
- // processedLines.push(line);
769
- // }
770
- // } else if (isMultiline(line)) {
771
- // // Multiline statement handling
772
- // if (lastNonMultilineIndex >= 0) {
773
- // // Concatenate to the previous non-multiline statement
774
- // const trimedLine = line.trimStart();
775
- // const addBlank = trimedLine.startsWith('-') ? ' ' : '';
776
- // processedLines[lastNonMultilineIndex] += addBlank + trimedLine;
777
- // }
778
- // // Add the special comment line
779
- // processedLines.push(';_WEBGAL_LINE_BREAK_' + line);
780
- // isInMultilineSequence = true;
781
- // } else {
782
- // // Non-multiline statement handling
783
- // processedLines.push(line);
784
- // lastNonMultilineIndex = processedLines.length - 1;
785
- // isInMultilineSequence = false;
786
- // }
787
- // }
788
- // return processedLines.join('\n');
789
- // }
754
+ /**
755
+ * GPT 4 写的,临时用,以后要重构!!!
756
+ * TODO:用人类智能重构,要是用着一直没问题,也不是不可以 trust AI
757
+ * @param css
758
+ */
759
+ function parseCSS(css) {
760
+ const result = {};
761
+ let specialRules = '';
762
+ let matches;
763
+ // 使用非贪婪匹配,尝试正确处理任意层次的嵌套
764
+ const classRegex = /\.([^{\s]+)\s*{((?:[^{}]*|{[^}]*})*)}/g;
765
+ const specialRegex = /(@[^{]+{\s*(?:[^{}]*{[^}]*}[^{}]*)+\s*})/g;
766
+ while ((matches = classRegex.exec(css)) !== null) {
767
+ const key = matches[1];
768
+ const value = matches[2].trim().replace(/\s*;\s*/g, ';\n');
769
+ result[key] = value;
770
+ }
771
+ while ((matches = specialRegex.exec(css)) !== null) {
772
+ specialRules += matches[1].trim() + '\n';
773
+ }
774
+ return [result, specialRules.trim()];
775
+ }
790
776
 
791
777
  class SceneParser {
792
778
  assetsPrefetcher;
@@ -831,4 +817,4 @@ class SceneParser {
831
817
  }
832
818
  }
833
819
 
834
- export { ADD_NEXT_ARG_LIST, SCRIPT_CONFIG, SceneParser as default, sceneTextPreProcess };
820
+ export { ADD_NEXT_ARG_LIST, SCRIPT_CONFIG, WEBGAL_LINE_BREAK_MARK, SceneParser as default, isLineBreakPlaceholder, sceneTextPreProcess };
@@ -6,6 +6,8 @@ export interface sceneEntry {
6
6
  sceneName: string;
7
7
  sceneUrl: string;
8
8
  continueLine: number;
9
+ locals?: Record<string, any>;
10
+ writeReturnTo?: string;
9
11
  }
10
12
  /**
11
13
  * 场景栈条目接口 (兼容性别名)
@@ -38,7 +38,8 @@ export declare enum commandType {
38
38
  getUserInput = 31,
39
39
  applyStyle = 32,
40
40
  wait = 33,
41
- callSteam = 34
41
+ callSteam = 34,
42
+ return = 35
42
43
  }
43
44
  /**
44
45
  * 单个参数接口
@@ -70,6 +71,15 @@ export interface ISentence {
70
71
  sentenceAssets: Array<IAsset>;
71
72
  subScene: Array<string>;
72
73
  inlineComment: string;
74
+ /**
75
+ * 本语句在原始场景文件中占据的行范围(0-based,含首尾)。
76
+ * 单行语句 startLine === endLine;多行语句 endLine 指向最后一条续行。
77
+ * 图形编辑器据此做「只替换被编辑语句所占的行」。
78
+ */
79
+ startLine: number;
80
+ endLine: number;
81
+ /** 本条是多行语句被折叠掉的续行占位,仅用于补齐行数,不应展示给用户 */
82
+ isLineBreakHolder: boolean;
73
83
  }
74
84
  /**
75
85
  * 场景接口
@@ -9,3 +9,13 @@
9
9
  * @returns The processed scene text
10
10
  */
11
11
  export declare function sceneTextPreProcess(sceneText: string): string;
12
+ /**
13
+ * 占位行的前缀。占位行本身是一条注释,引擎会空跑掉;
14
+ * 它的作用是让预处理后的行数与原始场景一致,从而保持
15
+ * 「解析后语句 index == 文件行号」这一不变量。
16
+ */
17
+ export declare const WEBGAL_LINE_BREAK_MARK = ";_WEBGAL_LINE_BREAK_";
18
+ /**
19
+ * 判断预处理后的某一行是否是占位行,即它是上一条多行语句折叠掉的续行。
20
+ */
21
+ export declare function isLineBreakPlaceholder(processedLine: string): boolean;
@@ -3,7 +3,7 @@ import { WebgalConfig } from './configParser/configParser';
3
3
  import { fileType } from './interface/assets';
4
4
  import { IAsset } from './interface/sceneInterface';
5
5
  import { IWebGALStyleObj } from "./styleParser";
6
- import { sceneTextPreProcess } from "./sceneTextPreProcessor";
6
+ import { isLineBreakPlaceholder, sceneTextPreProcess, WEBGAL_LINE_BREAK_MARK } from "./sceneTextPreProcessor";
7
7
  export default class SceneParser {
8
8
  private readonly assetsPrefetcher;
9
9
  private readonly assetSetter;
@@ -23,4 +23,4 @@ export default class SceneParser {
23
23
  parseScssToWebgalStyleObj(scssString: string): IWebGALStyleObj;
24
24
  }
25
25
  export { ADD_NEXT_ARG_LIST, SCRIPT_CONFIG };
26
- export { sceneTextPreProcess };
26
+ export { sceneTextPreProcess, isLineBreakPlaceholder, WEBGAL_LINE_BREAK_MARK };
@@ -6,6 +6,8 @@ export interface sceneEntry {
6
6
  sceneName: string;
7
7
  sceneUrl: string;
8
8
  continueLine: number;
9
+ locals?: Record<string, any>;
10
+ writeReturnTo?: string;
9
11
  }
10
12
  /**
11
13
  * 场景栈条目接口 (兼容性别名)
@@ -38,7 +38,8 @@ export declare enum commandType {
38
38
  getUserInput = 31,
39
39
  applyStyle = 32,
40
40
  wait = 33,
41
- callSteam = 34
41
+ callSteam = 34,
42
+ return = 35
42
43
  }
43
44
  /**
44
45
  * 单个参数接口
@@ -70,6 +71,15 @@ export interface ISentence {
70
71
  sentenceAssets: Array<IAsset>;
71
72
  subScene: Array<string>;
72
73
  inlineComment: string;
74
+ /**
75
+ * 本语句在原始场景文件中占据的行范围(0-based,含首尾)。
76
+ * 单行语句 startLine === endLine;多行语句 endLine 指向最后一条续行。
77
+ * 图形编辑器据此做「只替换被编辑语句所占的行」。
78
+ */
79
+ startLine: number;
80
+ endLine: number;
81
+ /** 本条是多行语句被折叠掉的续行占位,仅用于补齐行数,不应展示给用户 */
82
+ isLineBreakHolder: boolean;
73
83
  }
74
84
  /**
75
85
  * 场景接口
@@ -9,3 +9,13 @@
9
9
  * @returns The processed scene text
10
10
  */
11
11
  export declare function sceneTextPreProcess(sceneText: string): string;
12
+ /**
13
+ * 占位行的前缀。占位行本身是一条注释,引擎会空跑掉;
14
+ * 它的作用是让预处理后的行数与原始场景一致,从而保持
15
+ * 「解析后语句 index == 文件行号」这一不变量。
16
+ */
17
+ export declare const WEBGAL_LINE_BREAK_MARK = ";_WEBGAL_LINE_BREAK_";
18
+ /**
19
+ * 判断预处理后的某一行是否是占位行,即它是上一条多行语句折叠掉的续行。
20
+ */
21
+ export declare function isLineBreakPlaceholder(processedLine: string): boolean;