@bct-app/game-engine 0.1.13 → 0.1.15

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.d.mts CHANGED
@@ -1,9 +1,5 @@
1
1
  import { TPlayer, TOperation, TAbility, TPopulatedCharacter, TTimeline, TTriggerPhase, TEffect, TTargetRef, TPlayerReminder, TReminder } from '@bct-app/game-model';
2
2
 
3
- type TLifetimeStatusResolver = (input: {
4
- player: TPlayer;
5
- status: 'POISONED' | 'DRUNK' | 'HEALTHY';
6
- }) => boolean;
7
3
  type TLifetimeCustomResolver = (input: {
8
4
  key: string;
9
5
  payload?: unknown;
@@ -19,10 +15,9 @@ type TApplyOperationToPlayersArgs = {
19
15
  characters: TPopulatedCharacter[];
20
16
  timelines: TTimeline[];
21
17
  timelineIndexAtNow?: number;
22
- statusResolver?: TLifetimeStatusResolver;
23
18
  customResolver?: TLifetimeCustomResolver;
24
19
  };
25
- declare const applyOperationToPlayers: ({ players, operations, allAbilities, characters, timelines, timelineIndexAtNow, statusResolver, customResolver, }: TApplyOperationToPlayersArgs) => TPlayer[];
20
+ declare const applyOperationToPlayers: ({ players, operations, allAbilities, characters, timelines, timelineIndexAtNow, customResolver, }: TApplyOperationToPlayersArgs) => TPlayer[];
26
21
 
27
22
  type TCanInvokeAbilityCustomResolver = (atom: {
28
23
  resolverId: string;
package/dist/index.d.ts CHANGED
@@ -1,9 +1,5 @@
1
1
  import { TPlayer, TOperation, TAbility, TPopulatedCharacter, TTimeline, TTriggerPhase, TEffect, TTargetRef, TPlayerReminder, TReminder } from '@bct-app/game-model';
2
2
 
3
- type TLifetimeStatusResolver = (input: {
4
- player: TPlayer;
5
- status: 'POISONED' | 'DRUNK' | 'HEALTHY';
6
- }) => boolean;
7
3
  type TLifetimeCustomResolver = (input: {
8
4
  key: string;
9
5
  payload?: unknown;
@@ -19,10 +15,9 @@ type TApplyOperationToPlayersArgs = {
19
15
  characters: TPopulatedCharacter[];
20
16
  timelines: TTimeline[];
21
17
  timelineIndexAtNow?: number;
22
- statusResolver?: TLifetimeStatusResolver;
23
18
  customResolver?: TLifetimeCustomResolver;
24
19
  };
25
- declare const applyOperationToPlayers: ({ players, operations, allAbilities, characters, timelines, timelineIndexAtNow, statusResolver, customResolver, }: TApplyOperationToPlayersArgs) => TPlayer[];
20
+ declare const applyOperationToPlayers: ({ players, operations, allAbilities, characters, timelines, timelineIndexAtNow, customResolver, }: TApplyOperationToPlayersArgs) => TPlayer[];
26
21
 
27
22
  type TCanInvokeAbilityCustomResolver = (atom: {
28
23
  resolverId: string;
package/dist/index.js CHANGED
@@ -843,10 +843,6 @@ var evalPlayerStateOnPlayer = (player, atom, ctx) => {
843
843
  if (field === "IS_DEAD") {
844
844
  return compareBool(Boolean(player.isDead), op, value);
845
845
  }
846
- if (field === "IS_POISONED" || field === "IS_DRUNK" || field === "IS_HEALTHY") {
847
- const resolved = ctx.statusResolver ? ctx.statusResolver({ player, status: field === "IS_POISONED" ? "POISONED" : field === "IS_DRUNK" ? "DRUNK" : "HEALTHY" }) : false;
848
- return compareBool(resolved, op, value);
849
- }
850
846
  if (field === "CHARACTER_ID") {
851
847
  return compareString(player.characterId, op, value);
852
848
  }
@@ -1025,7 +1021,6 @@ var evaluateLifetime = ({
1025
1021
  snapshotSeatMap,
1026
1022
  characterMap,
1027
1023
  payloads,
1028
- statusResolver,
1029
1024
  customResolver
1030
1025
  }) => {
1031
1026
  const lifetime = getLifetime(effect, payloads);
@@ -1040,7 +1035,6 @@ var evaluateLifetime = ({
1040
1035
  snapshotSeatMap,
1041
1036
  characterMap,
1042
1037
  payloads,
1043
- statusResolver,
1044
1038
  customResolver
1045
1039
  };
1046
1040
  const expr = lifetime.expr;
@@ -1050,6 +1044,34 @@ var evaluateLifetime = ({
1050
1044
  }
1051
1045
  return targets.filter((target) => evalExpr(expr, { ...baseCtx, target }));
1052
1046
  };
1047
+ var evaluatePrecondition = ({
1048
+ expr,
1049
+ operationTimelineIdx,
1050
+ nowTimelineIndex,
1051
+ timelines,
1052
+ effector,
1053
+ targets,
1054
+ snapshotSeatMap,
1055
+ characterMap,
1056
+ payloads,
1057
+ customResolver
1058
+ }) => {
1059
+ const baseCtx = {
1060
+ operationTimelineIdx,
1061
+ nowTimelineIndex,
1062
+ timelines,
1063
+ effector,
1064
+ snapshotSeatMap,
1065
+ characterMap,
1066
+ payloads,
1067
+ customResolver
1068
+ };
1069
+ const dependsOnTarget = expressionReferencesTarget(expr);
1070
+ if (!dependsOnTarget) {
1071
+ return evalExpr(expr, { ...baseCtx, target: void 0 }) ? targets : [];
1072
+ }
1073
+ return targets.filter((target) => evalExpr(expr, { ...baseCtx, target }));
1074
+ };
1053
1075
 
1054
1076
  // src/apply-operation.ts
1055
1077
  var isDeferredOperation = (operation, abilityMap) => {
@@ -1070,7 +1092,6 @@ var applyOperationToPlayers = ({
1070
1092
  characters,
1071
1093
  timelines,
1072
1094
  timelineIndexAtNow,
1073
- statusResolver,
1074
1095
  customResolver
1075
1096
  }) => {
1076
1097
  if (operations.length === 0) {
@@ -1089,6 +1110,7 @@ var applyOperationToPlayers = ({
1089
1110
  }
1090
1111
  operationsByTimeline.get(timelineIndex)?.push(operation);
1091
1112
  });
1113
+ const preconditionCache = /* @__PURE__ */ new Map();
1092
1114
  const runReplay = (lifetimeSnapshotSeatMap) => {
1093
1115
  const playersWithStatus = copyPlayers(players);
1094
1116
  const applyOpsSequence = (ops) => {
@@ -1110,7 +1132,7 @@ var applyOperationToPlayers = ({
1110
1132
  }
1111
1133
  return snapshotSeatMap;
1112
1134
  };
1113
- ability.effects.forEach((effect) => {
1135
+ ability.effects.forEach((effect, effectIdx) => {
1114
1136
  const resolvedTargets = resolveEffectTargets({
1115
1137
  effect,
1116
1138
  operation,
@@ -1122,17 +1144,45 @@ var applyOperationToPlayers = ({
1122
1144
  if (!resolvedTargets) {
1123
1145
  return;
1124
1146
  }
1147
+ const precondition = "precondition" in effect ? effect.precondition : void 0;
1148
+ let gatedTargets;
1149
+ if (precondition) {
1150
+ const cachedSeats = preconditionCache.get(operation)?.get(effectIdx);
1151
+ if (cachedSeats) {
1152
+ gatedTargets = resolvedTargets.filter((t) => cachedSeats.has(t.seat));
1153
+ } else {
1154
+ gatedTargets = evaluatePrecondition({
1155
+ expr: precondition,
1156
+ operationTimelineIdx: operationInTimelineIdx,
1157
+ nowTimelineIndex,
1158
+ timelines,
1159
+ effector,
1160
+ targets: resolvedTargets,
1161
+ snapshotSeatMap: playerSeatMap,
1162
+ characterMap,
1163
+ payloads,
1164
+ customResolver
1165
+ });
1166
+ const opCache = preconditionCache.get(operation) ?? /* @__PURE__ */ new Map();
1167
+ opCache.set(effectIdx, new Set(gatedTargets.map((t) => t.seat)));
1168
+ preconditionCache.set(operation, opCache);
1169
+ }
1170
+ } else {
1171
+ gatedTargets = resolvedTargets;
1172
+ }
1173
+ if (gatedTargets.length === 0 && resolvedTargets.length > 0) {
1174
+ return;
1175
+ }
1125
1176
  const makePlayersEffect = evaluateLifetime({
1126
1177
  effect,
1127
1178
  operationTimelineIdx: operationInTimelineIdx,
1128
1179
  nowTimelineIndex,
1129
1180
  timelines,
1130
1181
  effector,
1131
- targets: resolvedTargets,
1182
+ targets: gatedTargets,
1132
1183
  snapshotSeatMap: lifetimeSnapshotSeatMap,
1133
1184
  characterMap,
1134
1185
  payloads,
1135
- statusResolver,
1136
1186
  customResolver
1137
1187
  });
1138
1188
  const handler = effectHandlers[effect.type];
package/dist/index.mjs CHANGED
@@ -803,10 +803,6 @@ var evalPlayerStateOnPlayer = (player, atom, ctx) => {
803
803
  if (field === "IS_DEAD") {
804
804
  return compareBool(Boolean(player.isDead), op, value);
805
805
  }
806
- if (field === "IS_POISONED" || field === "IS_DRUNK" || field === "IS_HEALTHY") {
807
- const resolved = ctx.statusResolver ? ctx.statusResolver({ player, status: field === "IS_POISONED" ? "POISONED" : field === "IS_DRUNK" ? "DRUNK" : "HEALTHY" }) : false;
808
- return compareBool(resolved, op, value);
809
- }
810
806
  if (field === "CHARACTER_ID") {
811
807
  return compareString(player.characterId, op, value);
812
808
  }
@@ -985,7 +981,6 @@ var evaluateLifetime = ({
985
981
  snapshotSeatMap,
986
982
  characterMap,
987
983
  payloads,
988
- statusResolver,
989
984
  customResolver
990
985
  }) => {
991
986
  const lifetime = getLifetime(effect, payloads);
@@ -1000,7 +995,6 @@ var evaluateLifetime = ({
1000
995
  snapshotSeatMap,
1001
996
  characterMap,
1002
997
  payloads,
1003
- statusResolver,
1004
998
  customResolver
1005
999
  };
1006
1000
  const expr = lifetime.expr;
@@ -1010,6 +1004,34 @@ var evaluateLifetime = ({
1010
1004
  }
1011
1005
  return targets.filter((target) => evalExpr(expr, { ...baseCtx, target }));
1012
1006
  };
1007
+ var evaluatePrecondition = ({
1008
+ expr,
1009
+ operationTimelineIdx,
1010
+ nowTimelineIndex,
1011
+ timelines,
1012
+ effector,
1013
+ targets,
1014
+ snapshotSeatMap,
1015
+ characterMap,
1016
+ payloads,
1017
+ customResolver
1018
+ }) => {
1019
+ const baseCtx = {
1020
+ operationTimelineIdx,
1021
+ nowTimelineIndex,
1022
+ timelines,
1023
+ effector,
1024
+ snapshotSeatMap,
1025
+ characterMap,
1026
+ payloads,
1027
+ customResolver
1028
+ };
1029
+ const dependsOnTarget = expressionReferencesTarget(expr);
1030
+ if (!dependsOnTarget) {
1031
+ return evalExpr(expr, { ...baseCtx, target: void 0 }) ? targets : [];
1032
+ }
1033
+ return targets.filter((target) => evalExpr(expr, { ...baseCtx, target }));
1034
+ };
1013
1035
 
1014
1036
  // src/apply-operation.ts
1015
1037
  var isDeferredOperation = (operation, abilityMap) => {
@@ -1030,7 +1052,6 @@ var applyOperationToPlayers = ({
1030
1052
  characters,
1031
1053
  timelines,
1032
1054
  timelineIndexAtNow,
1033
- statusResolver,
1034
1055
  customResolver
1035
1056
  }) => {
1036
1057
  if (operations.length === 0) {
@@ -1049,6 +1070,7 @@ var applyOperationToPlayers = ({
1049
1070
  }
1050
1071
  operationsByTimeline.get(timelineIndex)?.push(operation);
1051
1072
  });
1073
+ const preconditionCache = /* @__PURE__ */ new Map();
1052
1074
  const runReplay = (lifetimeSnapshotSeatMap) => {
1053
1075
  const playersWithStatus = copyPlayers(players);
1054
1076
  const applyOpsSequence = (ops) => {
@@ -1070,7 +1092,7 @@ var applyOperationToPlayers = ({
1070
1092
  }
1071
1093
  return snapshotSeatMap;
1072
1094
  };
1073
- ability.effects.forEach((effect) => {
1095
+ ability.effects.forEach((effect, effectIdx) => {
1074
1096
  const resolvedTargets = resolveEffectTargets({
1075
1097
  effect,
1076
1098
  operation,
@@ -1082,17 +1104,45 @@ var applyOperationToPlayers = ({
1082
1104
  if (!resolvedTargets) {
1083
1105
  return;
1084
1106
  }
1107
+ const precondition = "precondition" in effect ? effect.precondition : void 0;
1108
+ let gatedTargets;
1109
+ if (precondition) {
1110
+ const cachedSeats = preconditionCache.get(operation)?.get(effectIdx);
1111
+ if (cachedSeats) {
1112
+ gatedTargets = resolvedTargets.filter((t) => cachedSeats.has(t.seat));
1113
+ } else {
1114
+ gatedTargets = evaluatePrecondition({
1115
+ expr: precondition,
1116
+ operationTimelineIdx: operationInTimelineIdx,
1117
+ nowTimelineIndex,
1118
+ timelines,
1119
+ effector,
1120
+ targets: resolvedTargets,
1121
+ snapshotSeatMap: playerSeatMap,
1122
+ characterMap,
1123
+ payloads,
1124
+ customResolver
1125
+ });
1126
+ const opCache = preconditionCache.get(operation) ?? /* @__PURE__ */ new Map();
1127
+ opCache.set(effectIdx, new Set(gatedTargets.map((t) => t.seat)));
1128
+ preconditionCache.set(operation, opCache);
1129
+ }
1130
+ } else {
1131
+ gatedTargets = resolvedTargets;
1132
+ }
1133
+ if (gatedTargets.length === 0 && resolvedTargets.length > 0) {
1134
+ return;
1135
+ }
1085
1136
  const makePlayersEffect = evaluateLifetime({
1086
1137
  effect,
1087
1138
  operationTimelineIdx: operationInTimelineIdx,
1088
1139
  nowTimelineIndex,
1089
1140
  timelines,
1090
1141
  effector,
1091
- targets: resolvedTargets,
1142
+ targets: gatedTargets,
1092
1143
  snapshotSeatMap: lifetimeSnapshotSeatMap,
1093
1144
  characterMap,
1094
1145
  payloads,
1095
- statusResolver,
1096
1146
  customResolver
1097
1147
  });
1098
1148
  const handler = effectHandlers[effect.type];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bct-app/game-engine",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "Game engine utilities for BCT",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -30,7 +30,7 @@
30
30
  "access": "public"
31
31
  },
32
32
  "dependencies": {
33
- "@bct-app/game-model": "0.1.8"
33
+ "@bct-app/game-model": "0.1.9"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@vitest/ui": "^4.1.3",