@novely/core 0.35.0 → 0.36.0-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/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,31 @@ 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
+ };
991
+ const assetsToPreloadAdd = (asset) => {
992
+ if (!PRELOADED_ASSETS.has(asset)) {
993
+ ASSETS_TO_PRELOAD.add(asset);
994
+ }
995
+ };
899
996
  const scriptBase = async (part) => {
900
997
  Object.assign(story, flattenStory(part));
901
998
  let loadingIsShown = false;
@@ -907,31 +1004,14 @@ var novely = ({
907
1004
  if (!loadingIsShown) {
908
1005
  renderer.ui.showLoading();
909
1006
  }
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);
1007
+ await handleAssetsPreloading();
927
1008
  }
928
- ASSETS_TO_PRELOAD.clear();
929
1009
  await dataLoaded.promise;
930
1010
  renderer.ui.hideLoading();
931
1011
  if (!initialScreenWasShown) {
932
1012
  initialScreenWasShown = true;
933
1013
  if (initialScreen === "game") {
934
- restore();
1014
+ restore(void 0);
935
1015
  } else {
936
1016
  renderer.ui.showScreen(initialScreen);
937
1017
  }
@@ -940,6 +1020,33 @@ var novely = ({
940
1020
  const script = (part) => {
941
1021
  return limitScript(() => scriptBase(part));
942
1022
  };
1023
+ const checkAndAddToPreloadingList = (action2, props) => {
1024
+ if (action2 === "showBackground") {
1025
+ if (isImageAsset(props[0])) {
1026
+ assetsToPreloadAdd(props[0]);
1027
+ }
1028
+ if (props[0] && typeof props[0] === "object") {
1029
+ for (const value of Object.values(props[0])) {
1030
+ if (!isImageAsset(value))
1031
+ continue;
1032
+ assetsToPreloadAdd(value);
1033
+ }
1034
+ }
1035
+ }
1036
+ if (isAudioAction(action2) && isString(props[0])) {
1037
+ assetsToPreloadAdd(props[0]);
1038
+ }
1039
+ if (action2 === "showCharacter" && isString(props[0]) && isString(props[1])) {
1040
+ const images = characters[props[0]].emotions[props[1]];
1041
+ if (Array.isArray(images)) {
1042
+ for (const asset of images) {
1043
+ assetsToPreloadAdd(asset);
1044
+ }
1045
+ } else {
1046
+ assetsToPreloadAdd(images);
1047
+ }
1048
+ }
1049
+ };
943
1050
  const action = new Proxy({}, {
944
1051
  get(_, action2) {
945
1052
  if (action2 in renderer.actions) {
@@ -947,31 +1054,7 @@ var novely = ({
947
1054
  }
948
1055
  return (...props) => {
949
1056
  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
- }
1057
+ checkAndAddToPreloadingList(action2, props);
975
1058
  }
976
1059
  return [action2, ...props];
977
1060
  };
@@ -1098,7 +1181,12 @@ var novely = ({
1098
1181
  return prev.saves.push(save2), prev;
1099
1182
  });
1100
1183
  }
1101
- restore(save2);
1184
+ const context = renderer.getContext(MAIN_CONTEXT_KEY);
1185
+ const stack = useStack(context);
1186
+ stack.value = save2;
1187
+ context.meta.restoring = context.meta.goingBack = false;
1188
+ renderer.ui.showScreen("game");
1189
+ render(context);
1102
1190
  };
1103
1191
  const set = (save2, ctx) => {
1104
1192
  const stack = useStack(ctx || MAIN_CONTEXT_KEY);
@@ -1153,8 +1241,8 @@ var novely = ({
1153
1241
  data: latest[1]
1154
1242
  });
1155
1243
  }
1156
- const lastQueueItem = queue.at(-1) || [];
1157
- const lastQueueItemRequiresUserAction = isSkippedDuringRestore(lastQueueItem[0]) || isUserRequiredAction(lastQueueItem);
1244
+ const lastQueueItem = queue.at(-1);
1245
+ const lastQueueItemRequiresUserAction = lastQueueItem && isBlockingAction(lastQueueItem);
1158
1246
  await run((item) => {
1159
1247
  if (!latest)
1160
1248
  return;
@@ -1321,6 +1409,40 @@ var novely = ({
1321
1409
  matchActionOptions.push(ctx);
1322
1410
  if (!ctx.meta.preview)
1323
1411
  interactivity(true);
1412
+ },
1413
+ onBeforeActionCall({ action: action2, props, ctx }) {
1414
+ if (preloadAssets !== "automatic")
1415
+ return;
1416
+ if (ctx.meta.preview || ctx.meta.restoring)
1417
+ return;
1418
+ if (!isBlockingAction([action2, ...props]))
1419
+ return;
1420
+ try {
1421
+ const collection = collectActionsBeforeBlockingAction({
1422
+ path: nextPath(klona(useStack(ctx).value[0])),
1423
+ refer
1424
+ });
1425
+ for (const [action3, ...props2] of collection) {
1426
+ checkAndAddToPreloadingList(action3, props2);
1427
+ }
1428
+ const { preloadAudioBlocking, preloadImage } = renderer.misc;
1429
+ ASSETS_TO_PRELOAD.forEach(async (asset) => {
1430
+ ASSETS_TO_PRELOAD.delete(asset);
1431
+ const type = await getResourseType(request, asset);
1432
+ switch (type) {
1433
+ case "audio": {
1434
+ preloadAudioBlocking(asset);
1435
+ break;
1436
+ }
1437
+ case "image": {
1438
+ preloadImage(asset);
1439
+ break;
1440
+ }
1441
+ }
1442
+ });
1443
+ } catch (cause) {
1444
+ console.error(cause);
1445
+ }
1324
1446
  }
1325
1447
  };
1326
1448
  const match = matchAction(matchActionOptions, {
@@ -1602,6 +1724,9 @@ var novely = ({
1602
1724
  render(ctx);
1603
1725
  },
1604
1726
  preload({ ctx, push }, [source]) {
1727
+ if (DEV2 && preloadAssets !== "lazy") {
1728
+ console.error(`You do not need a preload action becase "preloadAssets" strategy was set to "${preloadAssets}"`);
1729
+ }
1605
1730
  if (!ctx.meta.goingBack && !ctx.meta.restoring && !PRELOADED_ASSETS.has(source)) {
1606
1731
  PRELOADED_ASSETS.add(renderer.misc.preloadImage(source));
1607
1732
  }