@novely/core 0.35.0 → 0.36.0-beta.0

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/dist/index.js CHANGED
@@ -82,9 +82,16 @@ function klona(val) {
82
82
  }
83
83
 
84
84
  // src/utils.ts
85
- var matchAction = ({ getContext, push, forward }, values) => {
85
+ var matchAction = ({ getContext, onBeforeActionCall, push, forward }, values) => {
86
86
  return (action, props, { ctx, data }) => {
87
87
  const context = typeof ctx === "string" ? getContext(ctx) : ctx;
88
+ if (action !== "say") {
89
+ onBeforeActionCall({
90
+ action,
91
+ props,
92
+ ctx: context
93
+ });
94
+ }
88
95
  return values[action]({
89
96
  ctx: context,
90
97
  data,
@@ -249,7 +256,7 @@ var getOppositeAction = (action) => {
249
256
  var getActionsFromPath = (story, path, filter) => {
250
257
  let current = story;
251
258
  let precurrent;
252
- let ignoreNested = false;
259
+ let ignoreNestedBefore = null;
253
260
  let index = 0;
254
261
  let skipPreserve = void 0;
255
262
  const skip = /* @__PURE__ */ new Set();
@@ -270,11 +277,11 @@ var getActionsFromPath = (story, path, filter) => {
270
277
  if (isNumber(val)) {
271
278
  index++;
272
279
  let startIndex = 0;
273
- if (ignoreNested) {
274
- const prev = findLastPathItemBeforeItemOfType(path.slice(0, index), "block");
280
+ if (ignoreNestedBefore) {
281
+ const prev = findLastPathItemBeforeItemOfType(path.slice(0, index), ignoreNestedBefore);
275
282
  if (prev) {
276
283
  startIndex = prev[1];
277
- ignoreNested = false;
284
+ ignoreNestedBefore = null;
278
285
  }
279
286
  }
280
287
  for (let i = startIndex; i <= val; i++) {
@@ -309,7 +316,7 @@ var getActionsFromPath = (story, path, filter) => {
309
316
  current = story[val];
310
317
  } else if (type === "block:exit" || type === "choice:exit" || type === "condition:exit") {
311
318
  current = blocks.pop();
312
- ignoreNested = true;
319
+ ignoreNestedBefore = type.slice(0, -5);
313
320
  }
314
321
  }
315
322
  return {
@@ -493,6 +500,9 @@ var fetchContentType = async (request, url) => {
493
500
  }
494
501
  };
495
502
  var getResourseType = async (request, url) => {
503
+ if (!isCSSImage(url)) {
504
+ return "other";
505
+ }
496
506
  const extension = getUrlFileExtension(url);
497
507
  if (HOWLER_SUPPORTED_FILE_FORMATS.has(extension)) {
498
508
  return "audio";
@@ -606,6 +616,68 @@ var nextPath = (path) => {
606
616
  }
607
617
  return path;
608
618
  };
619
+ var isBlockingAction = (action) => {
620
+ return isUserRequiredAction(action) || isSkippedDuringRestore(action[0]) && action[0] !== "vibrate";
621
+ };
622
+ var collectActionsBeforeBlockingAction = ({ path, refer }) => {
623
+ const collection = [];
624
+ let action = refer(path);
625
+ while (true) {
626
+ if (action == void 0) {
627
+ const { exitImpossible } = exitPath({
628
+ path,
629
+ refer
630
+ });
631
+ if (exitImpossible) {
632
+ break;
633
+ }
634
+ }
635
+ if (!action) {
636
+ break;
637
+ }
638
+ if (isBlockingAction(action)) {
639
+ const [name, ...props] = action;
640
+ if (name === "choice") {
641
+ const choiceProps = props;
642
+ for (let i = 0; i < choiceProps.length; i++) {
643
+ const branchContent = choiceProps[i];
644
+ if (!Array.isArray(branchContent))
645
+ continue;
646
+ const virtualPath = klona(path);
647
+ virtualPath.push(["choice", i], [null, 0]);
648
+ const innerActions = collectActionsBeforeBlockingAction({
649
+ path: virtualPath,
650
+ refer
651
+ });
652
+ collection.push(...innerActions);
653
+ }
654
+ } else if (name === "condition") {
655
+ const conditionProps = props;
656
+ const conditions = Object.keys(conditionProps[1]);
657
+ for (const condition of conditions) {
658
+ const virtualPath = klona(path);
659
+ virtualPath.push(["condition", condition], [null, 0]);
660
+ const innerActions = collectActionsBeforeBlockingAction({
661
+ path: virtualPath,
662
+ refer
663
+ });
664
+ collection.push(...innerActions);
665
+ }
666
+ }
667
+ break;
668
+ }
669
+ collection.push(action);
670
+ if (action[0] === "jump") {
671
+ path = [["jump", action[1]], [null, 0]];
672
+ } else if (action[0] == "block") {
673
+ path.push(["block", action[1]], [null, 0]);
674
+ } else {
675
+ nextPath(path);
676
+ }
677
+ action = refer(path);
678
+ }
679
+ return collection;
680
+ };
609
681
 
610
682
  // src/novely.ts
611
683
  import { dequal } from "dequal/lite";
@@ -896,6 +968,26 @@ var novely = ({
896
968
  const intime = (value) => {
897
969
  return times.add(value), value;
898
970
  };
971
+ const handleAssetsPreloading = async () => {
972
+ const { preloadAudioBlocking, preloadImageBlocking } = renderer.misc;
973
+ const list = mapSet(ASSETS_TO_PRELOAD, (asset) => {
974
+ return limitAssetsDownload(async () => {
975
+ const type = await getResourseType(request, asset);
976
+ switch (type) {
977
+ case "audio": {
978
+ await preloadAudioBlocking(asset);
979
+ break;
980
+ }
981
+ case "image": {
982
+ await preloadImageBlocking(asset);
983
+ break;
984
+ }
985
+ }
986
+ });
987
+ });
988
+ await Promise.allSettled(list);
989
+ ASSETS_TO_PRELOAD.clear();
990
+ };
899
991
  const scriptBase = async (part) => {
900
992
  Object.assign(story, flattenStory(part));
901
993
  let loadingIsShown = false;
@@ -907,31 +999,14 @@ var novely = ({
907
999
  if (!loadingIsShown) {
908
1000
  renderer.ui.showLoading();
909
1001
  }
910
- const { preloadAudioBlocking, preloadImageBlocking } = renderer.misc;
911
- const list = mapSet(ASSETS_TO_PRELOAD, (asset) => {
912
- return limitAssetsDownload(async () => {
913
- const type = await getResourseType(request, asset);
914
- switch (type) {
915
- case "audio": {
916
- await preloadAudioBlocking(asset);
917
- break;
918
- }
919
- case "image": {
920
- await preloadImageBlocking(asset);
921
- break;
922
- }
923
- }
924
- });
925
- });
926
- await Promise.allSettled(list);
1002
+ await handleAssetsPreloading();
927
1003
  }
928
- ASSETS_TO_PRELOAD.clear();
929
1004
  await dataLoaded.promise;
930
1005
  renderer.ui.hideLoading();
931
1006
  if (!initialScreenWasShown) {
932
1007
  initialScreenWasShown = true;
933
1008
  if (initialScreen === "game") {
934
- restore();
1009
+ restore(void 0);
935
1010
  } else {
936
1011
  renderer.ui.showScreen(initialScreen);
937
1012
  }
@@ -940,6 +1015,33 @@ var novely = ({
940
1015
  const script = (part) => {
941
1016
  return limitScript(() => scriptBase(part));
942
1017
  };
1018
+ const checkAndAddToPreloadingList = (action2, props) => {
1019
+ if (action2 === "showBackground") {
1020
+ if (isImageAsset(props[0])) {
1021
+ ASSETS_TO_PRELOAD.add(props[0]);
1022
+ }
1023
+ if (props[0] && typeof props[0] === "object") {
1024
+ for (const value of Object.values(props[0])) {
1025
+ if (!isImageAsset(value))
1026
+ continue;
1027
+ ASSETS_TO_PRELOAD.add(value);
1028
+ }
1029
+ }
1030
+ }
1031
+ if (isAudioAction(action2) && isString(props[0])) {
1032
+ ASSETS_TO_PRELOAD.add(props[0]);
1033
+ }
1034
+ if (action2 === "showCharacter" && isString(props[0]) && isString(props[1])) {
1035
+ const images = characters[props[0]].emotions[props[1]];
1036
+ if (Array.isArray(images)) {
1037
+ for (const asset of images) {
1038
+ ASSETS_TO_PRELOAD.add(asset);
1039
+ }
1040
+ } else {
1041
+ ASSETS_TO_PRELOAD.add(images);
1042
+ }
1043
+ }
1044
+ };
943
1045
  const action = new Proxy({}, {
944
1046
  get(_, action2) {
945
1047
  if (action2 in renderer.actions) {
@@ -947,31 +1049,7 @@ var novely = ({
947
1049
  }
948
1050
  return (...props) => {
949
1051
  if (preloadAssets === "blocking") {
950
- if (action2 === "showBackground") {
951
- if (isImageAsset(props[0])) {
952
- ASSETS_TO_PRELOAD.add(props[0]);
953
- }
954
- if (props[0] && typeof props[0] === "object") {
955
- for (const value of Object.values(props[0])) {
956
- if (!isImageAsset(value))
957
- continue;
958
- ASSETS_TO_PRELOAD.add(value);
959
- }
960
- }
961
- }
962
- if (isAudioAction(action2) && isString(props[0])) {
963
- ASSETS_TO_PRELOAD.add(props[0]);
964
- }
965
- if (action2 === "showCharacter" && isString(props[0]) && isString(props[1])) {
966
- const images = characters[props[0]].emotions[props[1]];
967
- if (Array.isArray(images)) {
968
- for (const asset of images) {
969
- ASSETS_TO_PRELOAD.add(asset);
970
- }
971
- } else {
972
- ASSETS_TO_PRELOAD.add(images);
973
- }
974
- }
1052
+ checkAndAddToPreloadingList(action2, props);
975
1053
  }
976
1054
  return [action2, ...props];
977
1055
  };
@@ -1098,7 +1176,12 @@ var novely = ({
1098
1176
  return prev.saves.push(save2), prev;
1099
1177
  });
1100
1178
  }
1101
- restore(save2);
1179
+ const context = renderer.getContext(MAIN_CONTEXT_KEY);
1180
+ const stack = useStack(context);
1181
+ stack.value = save2;
1182
+ context.meta.restoring = context.meta.goingBack = false;
1183
+ renderer.ui.showScreen("game");
1184
+ render(context);
1102
1185
  };
1103
1186
  const set = (save2, ctx) => {
1104
1187
  const stack = useStack(ctx || MAIN_CONTEXT_KEY);
@@ -1153,8 +1236,8 @@ var novely = ({
1153
1236
  data: latest[1]
1154
1237
  });
1155
1238
  }
1156
- const lastQueueItem = queue.at(-1) || [];
1157
- const lastQueueItemRequiresUserAction = isSkippedDuringRestore(lastQueueItem[0]) || isUserRequiredAction(lastQueueItem);
1239
+ const lastQueueItem = queue.at(-1);
1240
+ const lastQueueItemRequiresUserAction = lastQueueItem && isBlockingAction(lastQueueItem);
1158
1241
  await run((item) => {
1159
1242
  if (!latest)
1160
1243
  return;
@@ -1321,6 +1404,40 @@ var novely = ({
1321
1404
  matchActionOptions.push(ctx);
1322
1405
  if (!ctx.meta.preview)
1323
1406
  interactivity(true);
1407
+ },
1408
+ onBeforeActionCall({ action: action2, props, ctx }) {
1409
+ if (preloadAssets !== "automatic")
1410
+ return;
1411
+ if (ctx.meta.preview || ctx.meta.restoring)
1412
+ return;
1413
+ if (!isBlockingAction([action2, ...props]))
1414
+ return;
1415
+ try {
1416
+ const collection = collectActionsBeforeBlockingAction({
1417
+ path: nextPath(klona(useStack(ctx).value[0])),
1418
+ refer
1419
+ });
1420
+ for (const [action3, ...props2] of collection) {
1421
+ checkAndAddToPreloadingList(action3, props2);
1422
+ }
1423
+ const { preloadAudioBlocking, preloadImage } = renderer.misc;
1424
+ ASSETS_TO_PRELOAD.forEach(async (asset) => {
1425
+ ASSETS_TO_PRELOAD.delete(asset);
1426
+ const type = await getResourseType(request, asset);
1427
+ switch (type) {
1428
+ case "audio": {
1429
+ preloadAudioBlocking(asset);
1430
+ break;
1431
+ }
1432
+ case "image": {
1433
+ preloadImage(asset);
1434
+ break;
1435
+ }
1436
+ }
1437
+ });
1438
+ } catch (cause) {
1439
+ console.error(cause);
1440
+ }
1324
1441
  }
1325
1442
  };
1326
1443
  const match = matchAction(matchActionOptions, {
@@ -1602,6 +1719,9 @@ var novely = ({
1602
1719
  render(ctx);
1603
1720
  },
1604
1721
  preload({ ctx, push }, [source]) {
1722
+ if (DEV2 && preloadAssets !== "lazy") {
1723
+ console.error(`You do not need a preload action becase "preloadAssets" strategy was set to "${preloadAssets}"`);
1724
+ }
1605
1725
  if (!ctx.meta.goingBack && !ctx.meta.restoring && !PRELOADED_ASSETS.has(source)) {
1606
1726
  PRELOADED_ASSETS.add(renderer.misc.preloadImage(source));
1607
1727
  }