@gamepark/mythologies 0.11.0 → 0.12.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.
Files changed (40) hide show
  1. package/dist/MythologiesRules.d.ts +33 -31
  2. package/dist/MythologiesRules.js +5 -1
  3. package/dist/material/Entity.d.ts +2 -2
  4. package/dist/material/Entity.js +2 -2
  5. package/dist/material/Pantheon.d.ts +1 -1
  6. package/dist/material/Pantheon.js +19 -10
  7. package/dist/material/entity/AutoEffectRule.d.ts +2 -2
  8. package/dist/material/entity/Effect.d.ts +25 -1
  9. package/dist/material/entity/Effect.js +6 -0
  10. package/dist/material/entity/MoveEffectRule.d.ts +1 -1
  11. package/dist/material/entity/MoveEffectRule.js +2 -2
  12. package/dist/material/entity/PlaceCardEffectRule.d.ts +2 -0
  13. package/dist/material/entity/PlaceCardEffectRule.js +15 -0
  14. package/dist/material/entity/PlayerEffectRule.d.ts +2 -2
  15. package/dist/material/entity/SimultaneousEffectRule.d.ts +2 -2
  16. package/dist/material/entity/SimultaneousMoveEffectRule.d.ts +1 -1
  17. package/dist/material/entity/SimultaneousMoveEffectRule.js +5 -6
  18. package/dist/material/entity/hindu/Vishnu.js +2 -13
  19. package/dist/rules/EndGameRule.js +1 -1
  20. package/dist/rules/PlaceCardsRule.d.ts +2 -2
  21. package/dist/rules/PlaceCardsRule.js +1 -1
  22. package/dist/rules/RuleId.d.ts +31 -30
  23. package/dist/rules/RuleId.js +31 -30
  24. package/dist/rules/effects/AnubisEffectRule.d.ts +1 -1
  25. package/dist/rules/effects/AnubisEffectRule.js +1 -11
  26. package/dist/rules/effects/AthenaEffectRule.js +1 -1
  27. package/dist/rules/effects/CentaurEffectRule.d.ts +2 -2
  28. package/dist/rules/effects/CentaurEffectRule.js +2 -5
  29. package/dist/rules/effects/InkosazanaEffectRule.d.ts +1 -1
  30. package/dist/rules/effects/InkosazanaEffectRule.js +1 -11
  31. package/dist/rules/effects/MultipleSimultaneousMoveEffectRule.d.ts +2 -1
  32. package/dist/rules/effects/MultipleSimultaneousMoveEffectRule.js +30 -5
  33. package/dist/rules/effects/ResolveEffectsRule.d.ts +10 -7
  34. package/dist/rules/effects/ResolveEffectsRule.js +93 -26
  35. package/dist/rules/effects/SphinxEffectRule.d.ts +1 -1
  36. package/dist/rules/effects/TriggerEffectsRule.d.ts +6 -5
  37. package/dist/rules/effects/TriggerEffectsRule.js +15 -13
  38. package/dist/rules/effects/VishnuEffectRule.d.ts +10 -0
  39. package/dist/rules/effects/VishnuEffectRule.js +44 -0
  40. package/package.json +3 -3
@@ -47,6 +47,7 @@ import { TianlongEffectRule, TianlongOpponentsEffectRule } from './rules/effects
47
47
  import { UkukuEffectRule, UkukuPlaceEffectRule } from './rules/effects/UkukuEffectRule';
48
48
  import { VetalaEffectRule } from './rules/effects/VetalaEffectRule';
49
49
  import { ViracochaEffectRule } from './rules/effects/ViracochaEffectRule';
50
+ import { VishnuEffectRule } from './rules/effects/VishnuEffectRule';
50
51
  import { WerehyenaEffectRule } from './rules/effects/WerehyenaEffectRule';
51
52
  import { YanluoWangEffectRule } from './rules/effects/YanluoWangEffectRule';
52
53
  import { EndGameRule } from './rules/EndGameRule';
@@ -85,36 +86,37 @@ export declare class MythologiesRules extends SecretMaterialRules<PlayerColor, M
85
86
  30: typeof CobraInvokeEffect;
86
87
  31: typeof AmmitEffectRule;
87
88
  32: typeof BennuEffectRule;
88
- 33: typeof ShivaEffectRule;
89
- 34: typeof RakshasaEffectRule;
90
- 35: typeof GarudaEffectRule;
91
- 62: typeof GarudaSacrificeRule;
92
- 36: typeof VetalaEffectRule;
93
- 37: typeof KinnaraEffectRule;
94
- 38: typeof KinnaraPlaceEffectRule;
95
- 39: typeof ViracochaEffectRule;
96
- 40: typeof IllapaEffectRule;
97
- 41: typeof IllapaSacrificeRule;
98
- 42: typeof CaimanEffectRule;
99
- 43: typeof CondorEffectRule;
100
- 44: typeof SupayEffectRule;
101
- 45: typeof UkukuEffectRule;
102
- 46: typeof UkukuPlaceEffectRule;
103
- 47: typeof YanluoWangEffectRule;
104
- 48: typeof NuwaEffectRule;
105
- 49: typeof TianlongEffectRule;
106
- 50: typeof TianlongOpponentsEffectRule;
107
- 51: typeof QilinEffectRule;
108
- 52: typeof FenghuangEffectRule;
109
- 53: typeof JiangshiEffectRule;
110
- 54: typeof InkosazanaEffectRule;
111
- 55: typeof ImpunduluEffectRule;
112
- 56: typeof WerehyenaEffectRule;
113
- 57: typeof IdloziEffectRule;
114
- 58: typeof FairyEffectRule;
115
- 59: typeof LeprechaunEffectRule;
116
- 60: typeof SelkieEffectRule;
117
- 61: typeof DullahanEffectRule;
89
+ 33: typeof VishnuEffectRule;
90
+ 34: typeof ShivaEffectRule;
91
+ 35: typeof RakshasaEffectRule;
92
+ 36: typeof GarudaEffectRule;
93
+ 63: typeof GarudaSacrificeRule;
94
+ 37: typeof VetalaEffectRule;
95
+ 38: typeof KinnaraEffectRule;
96
+ 39: typeof KinnaraPlaceEffectRule;
97
+ 40: typeof ViracochaEffectRule;
98
+ 41: typeof IllapaEffectRule;
99
+ 42: typeof IllapaSacrificeRule;
100
+ 43: typeof CaimanEffectRule;
101
+ 44: typeof CondorEffectRule;
102
+ 45: typeof SupayEffectRule;
103
+ 46: typeof UkukuEffectRule;
104
+ 47: typeof UkukuPlaceEffectRule;
105
+ 48: typeof YanluoWangEffectRule;
106
+ 49: typeof NuwaEffectRule;
107
+ 50: typeof TianlongEffectRule;
108
+ 51: typeof TianlongOpponentsEffectRule;
109
+ 52: typeof QilinEffectRule;
110
+ 53: typeof FenghuangEffectRule;
111
+ 54: typeof JiangshiEffectRule;
112
+ 55: typeof InkosazanaEffectRule;
113
+ 56: typeof ImpunduluEffectRule;
114
+ 57: typeof WerehyenaEffectRule;
115
+ 58: typeof IdloziEffectRule;
116
+ 59: typeof FairyEffectRule;
117
+ 60: typeof LeprechaunEffectRule;
118
+ 61: typeof SelkieEffectRule;
119
+ 62: typeof DullahanEffectRule;
118
120
  };
119
121
  locationsStrategies: {
120
122
  2: {
@@ -151,7 +153,7 @@ export declare class MythologiesRules extends SecretMaterialRules<PlayerColor, M
151
153
  keepMoveSecret(move: MaterialMove): boolean;
152
154
  isPlayerDestiny(move: MoveItem): boolean;
153
155
  beforeItemMove(move: ItemMove): MaterialMove<PlayerColor, MaterialType, LocationType, number>[];
154
- getAutomaticMoves(): import("@gamepark/rules-api").StartPlayerTurn<number, RuleId.ResolveEffects>[] | import("@gamepark/rules-api").StartRule<RuleId.ResolveEffects>[];
156
+ getAutomaticMoves(): import("@gamepark/rules-api").StartRule<RuleId.ResolveEffects>[] | import("@gamepark/rules-api").StartPlayerTurn<PlayerColor, RuleId.ResolveEffects>[];
155
157
  giveTime(): number;
156
158
  getScore(player: PlayerColor): number;
157
159
  getTieBreaker(tieBreaker: number, player: PlayerColor): number | undefined;
@@ -52,6 +52,7 @@ import { TriggerEffectsRule } from './rules/effects/TriggerEffectsRule';
52
52
  import { UkukuEffectRule, UkukuPlaceEffectRule } from './rules/effects/UkukuEffectRule';
53
53
  import { VetalaEffectRule } from './rules/effects/VetalaEffectRule';
54
54
  import { ViracochaEffectRule } from './rules/effects/ViracochaEffectRule';
55
+ import { VishnuEffectRule } from './rules/effects/VishnuEffectRule';
55
56
  import { WerehyenaEffectRule } from './rules/effects/WerehyenaEffectRule';
56
57
  import { YanluoWangEffectRule } from './rules/effects/YanluoWangEffectRule';
57
58
  import { EndGameRule } from './rules/EndGameRule';
@@ -90,6 +91,7 @@ export class MythologiesRules extends SecretMaterialRules {
90
91
  [RuleId.CobraInvokeEffect]: CobraInvokeEffect,
91
92
  [RuleId.AmmitEffect]: AmmitEffectRule,
92
93
  [RuleId.BennuEffect]: BennuEffectRule,
94
+ [RuleId.VishnuEffect]: VishnuEffectRule,
93
95
  [RuleId.ShivaEffect]: ShivaEffectRule,
94
96
  [RuleId.RakshasaEffect]: RakshasaEffectRule,
95
97
  [RuleId.GarudaEffect]: GarudaEffectRule,
@@ -190,7 +192,9 @@ export class MythologiesRules extends SecretMaterialRules {
190
192
  return super.beforeItemMove(move);
191
193
  }
192
194
  getAutomaticMoves() {
193
- if (!this.remind(Memory.OngoingEffect) && this.game.rule?.id !== RuleId.ResolveEffects && this.remind(Memory.PendingEffects).length) {
195
+ if (!this.remind(Memory.OngoingEffect) &&
196
+ this.game.rule?.id !== RuleId.ResolveEffects &&
197
+ this.remind(Memory.PendingEffects).length) {
194
198
  return new ResolveEffectsRule(this.game).startEffectsResolution();
195
199
  }
196
200
  return [];
@@ -11,8 +11,8 @@ export declare enum Entity {
11
11
  Centaur = 6,
12
12
  Gorgon = 7,
13
13
  Cyclops = 8,
14
- Chimera = 9,
15
- Griffin = 10,
14
+ Griffin = 9,
15
+ Chimera = 10,
16
16
  Odin = 11,
17
17
  Thor = 12,
18
18
  Loki = 13,
@@ -8,8 +8,8 @@ export var Entity;
8
8
  Entity[Entity["Centaur"] = 6] = "Centaur";
9
9
  Entity[Entity["Gorgon"] = 7] = "Gorgon";
10
10
  Entity[Entity["Cyclops"] = 8] = "Cyclops";
11
- Entity[Entity["Chimera"] = 9] = "Chimera";
12
- Entity[Entity["Griffin"] = 10] = "Griffin";
11
+ Entity[Entity["Griffin"] = 9] = "Griffin";
12
+ Entity[Entity["Chimera"] = 10] = "Chimera";
13
13
  Entity[Entity["Odin"] = 11] = "Odin";
14
14
  Entity[Entity["Thor"] = 12] = "Thor";
15
15
  Entity[Entity["Loki"] = 13] = "Loki";
@@ -29,7 +29,7 @@ export declare class Pantheon extends MaterialRulesPart {
29
29
  updateGrid(gridAfter?: (number | null)[][]): TriggerEvent[];
30
30
  private getEntitiesMoved;
31
31
  private getEntitiesCrushed;
32
- gainBonus(grid?: (number | null)[][]): MoveItem[];
32
+ triggerBonusGains(grid?: (number | null)[][]): void;
33
33
  gainLineBonus(y: number): MoveItem<number, number, number>[];
34
34
  gainColumnBonus(x: number): MoveItem<number, number, number>[];
35
35
  getBonusGem(y: number): Material<number, number, number>;
@@ -6,7 +6,7 @@ import { PermanentEffectsRule } from '../rules/effects/PermanentEffectsRule';
6
6
  import { TriggerEffectsRule } from '../rules/effects/TriggerEffectsRule';
7
7
  import { Destiny } from './Destiny';
8
8
  import { entityMythology, isGod } from './Entity';
9
- import { isTriggeredEffect, LineEventType, TriggerEventType } from './entity/Effect';
9
+ import { isTriggeredEffect, LineEventType, PendingEffectsType, TriggerEventType } from './entity/Effect';
10
10
  import { entities, getEffects } from './entity/EntityDescription';
11
11
  import { LocationType } from './LocationType';
12
12
  import { MaterialType } from './MaterialType';
@@ -99,14 +99,13 @@ export class Pantheon extends MaterialRulesPart {
99
99
  const grid = this.indexGrid;
100
100
  const crush = grid[move.location.y][move.location.x] !== null;
101
101
  grid[move.location.y][move.location.x] = move.itemIndex;
102
- moves.push(...this.gainBonus(grid));
103
102
  const triggerEffectsRule = new TriggerEffectsRule(this.game);
104
- triggerEffectsRule.triggerCardEffect(move.itemIndex, location, { type: TriggerEventType.SelfPlaced, crush });
105
- const triggerEvents = this.updateGrid(grid);
103
+ triggerEffectsRule.triggerCardEffect(move.itemIndex, location, { type: TriggerEventType.SelfPlaced, crush }, entity);
106
104
  const invoked = this.isInvoke;
107
105
  if (invoked) {
108
106
  moves.push(...this.onInvoke(move));
109
107
  }
108
+ const triggerEvents = this.updateGrid(grid);
110
109
  triggerEvents.push({ type: TriggerEventType.EntityPlaced, cardIndex: move.itemIndex, entity, location, invoked });
111
110
  if (triggerEvents.length) {
112
111
  triggerEffectsRule.triggerEffects(...triggerEvents);
@@ -117,6 +116,7 @@ export class Pantheon extends MaterialRulesPart {
117
116
  return new MythologiesRules(this.game).delegate().isInvoke;
118
117
  }
119
118
  updateGrid(gridAfter = this.indexGrid) {
119
+ this.triggerBonusGains(gridAfter);
120
120
  const gridBefore = this.remind(Memory.PlayerGrid, this.player) ?? this.indexGrid;
121
121
  this.memorize(Memory.PlayerGrid, gridAfter, this.player);
122
122
  const triggerEvents = [];
@@ -165,17 +165,23 @@ export class Pantheon extends MaterialRulesPart {
165
165
  }
166
166
  return entities;
167
167
  }
168
- gainBonus(grid = this.indexGrid) {
169
- const moves = [];
168
+ triggerBonusGains(grid = this.indexGrid) {
169
+ const effect = { type: PendingEffectsType.BonusGains, player: this.player, lines: [], columns: [] };
170
170
  const fullLines = range(0, 3).filter((y) => !grid[y].includes(null));
171
171
  for (const y of fullLines) {
172
- moves.push(...this.gainLineBonus(y));
172
+ if (this.getBonusGem(y).length) {
173
+ effect.lines.push(y);
174
+ }
173
175
  }
174
176
  const fullColumns = range(0, 3).filter((x) => grid.every((line) => line[x] !== null));
175
177
  for (const x of fullColumns) {
176
- moves.push(...this.gainColumnBonus(x));
178
+ if (this.getBonusFavor(x).length) {
179
+ effect.columns.push(x);
180
+ }
181
+ }
182
+ if (effect.lines.length || effect.columns.length) {
183
+ this.remind(Memory.PendingEffects).unshift(effect);
177
184
  }
178
- return moves;
179
185
  }
180
186
  gainLineBonus(y) {
181
187
  const bonusGem = this.getBonusGem(y);
@@ -217,7 +223,10 @@ export class Pantheon extends MaterialRulesPart {
217
223
  }
218
224
  }
219
225
  }
220
- moves.push(...this.getInvokeGains(entity, move.location.y));
226
+ const invokeGain = entities[entity].invoke[move.location.y];
227
+ if (invokeGain.favor || invokeGain.gem) {
228
+ this.remind(Memory.PendingEffects).unshift({ type: PendingEffectsType.InvokeGain, player: this.player, entity, line: move.location.y });
229
+ }
221
230
  return moves;
222
231
  }
223
232
  getInvokeGains(entity, line) {
@@ -1,11 +1,11 @@
1
1
  import { MaterialMove, MaterialRulesPart } from '@gamepark/rules-api';
2
2
  import { PlayerColor } from '../../PlayerColor';
3
3
  import { EntityId } from '../Entity';
4
- import { EffectRule, PendingEffect, TriggerEvent } from './Effect';
4
+ import { CardEffect, EffectRule, TriggerEvent } from './Effect';
5
5
  export declare abstract class AutoEffectRule extends MaterialRulesPart implements EffectRule {
6
6
  canPlayEffect: () => boolean;
7
7
  abstract playEffect(): MaterialMove[];
8
- getEffect<Event extends TriggerEvent>(): PendingEffect<Event>;
8
+ getEffect<Event extends TriggerEvent>(): CardEffect<Event>;
9
9
  get card(): import("@gamepark/rules-api").MaterialItem<number, number, EntityId>;
10
10
  get player(): PlayerColor;
11
11
  }
@@ -98,9 +98,33 @@ export type EndOfTurn = {
98
98
  };
99
99
  export declare const isEndOfMyTurn: (event: TriggerEvent, { cardLocation }: TriggerEventContext) => event is EndOfTurn;
100
100
  export type TriggerEvent = EndOfGameEvent | SelfPlaced | EntityPlaced | LineEvent | ColumnEvent | Infinite | EntityCrushed | EntitySacrificed | EntitiesMoved | EndOfTurn;
101
- export type PendingEffect<Event extends TriggerEvent = TriggerEvent> = {
101
+ export type PlayerPendingEffects<Event extends TriggerEvent = TriggerEvent> = PlayerPendingCardsEffects<Event> | InvokeGainEffect | BonusGainEffects;
102
+ export declare enum PendingEffectsType {
103
+ CardsEffects = 1,
104
+ InvokeGain = 2,
105
+ BonusGains = 3
106
+ }
107
+ export type PlayerPendingCardsEffects<Event extends TriggerEvent = TriggerEvent> = {
108
+ type: PendingEffectsType.CardsEffects;
109
+ player: PlayerColor;
110
+ effects: CardEffect<Event>[];
111
+ };
112
+ export type CardEffect<Event extends TriggerEvent = TriggerEvent> = {
102
113
  cardIndex: number;
103
114
  effectIndex: number;
104
115
  triggerEvent: Event;
105
116
  auto: boolean;
117
+ count?: number;
118
+ };
119
+ export type InvokeGainEffect = {
120
+ type: PendingEffectsType.InvokeGain;
121
+ player: PlayerColor;
122
+ entity: Entity;
123
+ line: number;
124
+ };
125
+ export type BonusGainEffects = {
126
+ type: PendingEffectsType.BonusGains;
127
+ player: PlayerColor;
128
+ lines: number[];
129
+ columns: number[];
106
130
  };
@@ -24,3 +24,9 @@ export const oncePerTurn = (event) => event.type === TriggerEventType.Infinite;
24
24
  export const isCrushed = (event, { cardIndex }) => event.type === TriggerEventType.EntityCrushed && event.cardIndex === cardIndex;
25
25
  export const isSacrificed = (event, { cardIndex }) => event.type === TriggerEventType.EntitySacrificed && event.cardIndex === cardIndex;
26
26
  export const isEndOfMyTurn = (event, { cardLocation }) => event.type === TriggerEventType.EndOfTurn && event.player === cardLocation.player;
27
+ export var PendingEffectsType;
28
+ (function (PendingEffectsType) {
29
+ PendingEffectsType[PendingEffectsType["CardsEffects"] = 1] = "CardsEffects";
30
+ PendingEffectsType[PendingEffectsType["InvokeGain"] = 2] = "InvokeGain";
31
+ PendingEffectsType[PendingEffectsType["BonusGains"] = 3] = "BonusGains";
32
+ })(PendingEffectsType || (PendingEffectsType = {}));
@@ -14,5 +14,5 @@ export declare abstract class MoveEffectRule extends PlayerEffectRule {
14
14
  onCardMoved(_move: MoveItem): MaterialMove[];
15
15
  onCardSacrificed(move: MoveItem): MaterialMove[];
16
16
  onEndEffect(): MaterialMove[];
17
- updateGridAfterMoves(): MoveItem[];
17
+ updateGridAfterMoves(): void;
18
18
  }
@@ -63,11 +63,11 @@ export class MoveEffectRule extends PlayerEffectRule {
63
63
  return [];
64
64
  }
65
65
  onEndEffect() {
66
- return [...this.updateGridAfterMoves(), ...super.onEndEffect()];
66
+ this.updateGridAfterMoves();
67
+ return super.onEndEffect();
67
68
  }
68
69
  updateGridAfterMoves() {
69
70
  const pantheon = new Pantheon(this.game, this.player);
70
71
  new TriggerEffectsRule(this.game).triggerEffects(...pantheon.updateGrid());
71
- return pantheon.gainBonus();
72
72
  }
73
73
  }
@@ -1,8 +1,10 @@
1
1
  import { ItemMove, Material, MaterialMove, MoveItem, XYCoordinates } from '@gamepark/rules-api';
2
2
  import { PlayerEffectRule } from './PlayerEffectRule';
3
3
  export declare abstract class PlaceCardEffectRule extends PlayerEffectRule {
4
+ times: number;
4
5
  canPlayEffect(cardIndex: number): boolean;
5
6
  playEffect(): MaterialMove[];
7
+ onRuleStart(): MaterialMove[];
6
8
  getPlayerMoves(cardIndex?: number): MaterialMove[];
7
9
  abstract getCardsToPlace(cardIndex: number): Material;
8
10
  getAvailableDestinations(_cardIndex: number): XYCoordinates[];
@@ -1,11 +1,14 @@
1
1
  import { isMoveItemType } from '@gamepark/rules-api';
2
2
  import { CustomMoveType } from '../../CustomMoveType';
3
+ import { Memory } from '../../Memory';
3
4
  import { isGod } from '../Entity';
4
5
  import { LocationType } from '../LocationType';
5
6
  import { MaterialType } from '../MaterialType';
6
7
  import { Pantheon } from '../Pantheon';
8
+ import { PendingEffectsType } from './Effect';
7
9
  import { PlayerEffectRule } from './PlayerEffectRule';
8
10
  export class PlaceCardEffectRule extends PlayerEffectRule {
11
+ times = 1;
9
12
  canPlayEffect(cardIndex) {
10
13
  return this.getPlayerMoves(cardIndex).length > 0;
11
14
  }
@@ -14,6 +17,18 @@ export class PlaceCardEffectRule extends PlayerEffectRule {
14
17
  return [];
15
18
  return super.playEffect();
16
19
  }
20
+ onRuleStart() {
21
+ const count = this.getEffect().count ?? 1;
22
+ if (count < this.times) {
23
+ const repeatEffect = {
24
+ type: PendingEffectsType.CardsEffects,
25
+ player: this.player,
26
+ effects: [{ ...this.getEffect(), count: count + 1 }]
27
+ };
28
+ this.memorize(Memory.PendingEffects, (effects) => [repeatEffect, ...effects]);
29
+ }
30
+ return super.onRuleStart();
31
+ }
17
32
  getPlayerMoves(cardIndex = this.getEffect().cardIndex) {
18
33
  const player = this.material(MaterialType.EntityCard).getItem(cardIndex).location.player;
19
34
  const cards = this.getCardsToPlace(cardIndex);
@@ -1,14 +1,14 @@
1
1
  import { CustomMove, MaterialMove, PlayerTurnRule } from '@gamepark/rules-api';
2
2
  import { RuleId } from '../../rules/RuleId';
3
3
  import { EntityId } from '../Entity';
4
- import { EffectRule, PendingEffect, TriggerEvent } from './Effect';
4
+ import { CardEffect, EffectRule, TriggerEvent } from './Effect';
5
5
  export declare abstract class PlayerEffectRule extends PlayerTurnRule implements EffectRule {
6
6
  canPlayEffect(_cardIndex: number): boolean;
7
7
  ruleId?: RuleId;
8
8
  playEffect(): MaterialMove[];
9
9
  onRuleStart(): MaterialMove[];
10
10
  get card(): import("@gamepark/rules-api").MaterialItem<number, number, EntityId>;
11
- getEffect<Event extends TriggerEvent>(): PendingEffect<Event>;
11
+ getEffect<Event extends TriggerEvent>(): CardEffect<Event>;
12
12
  onCustomMove(move: CustomMove): MaterialMove[];
13
13
  onEndEffect(): MaterialMove[];
14
14
  }
@@ -2,7 +2,7 @@ import { CustomMove, MaterialMove, SimultaneousRule } from '@gamepark/rules-api'
2
2
  import { PlayerColor } from '../../PlayerColor';
3
3
  import { RuleId } from '../../rules/RuleId';
4
4
  import { EntityId } from '../Entity';
5
- import { EffectRule, PendingEffect, TriggerEvent } from './Effect';
5
+ import { CardEffect, EffectRule, TriggerEvent } from './Effect';
6
6
  export declare abstract class SimultaneousEffectRule extends SimultaneousRule implements EffectRule {
7
7
  canPlayEffect(_cardIndex: number): boolean;
8
8
  ruleId?: RuleId;
@@ -11,7 +11,7 @@ export declare abstract class SimultaneousEffectRule extends SimultaneousRule im
11
11
  getAffectedPlayers(): PlayerColor[];
12
12
  isOpponent(player: PlayerColor): boolean;
13
13
  get card(): import("@gamepark/rules-api").MaterialItem<number, number, EntityId>;
14
- getEffect<Event extends TriggerEvent>(): PendingEffect<Event>;
14
+ getEffect<Event extends TriggerEvent>(): CardEffect<Event>;
15
15
  getMovesAfterPlayersDone(): MaterialMove[];
16
16
  onCustomMove(move: CustomMove): MaterialMove[];
17
17
  onEndEffect(): MaterialMove[];
@@ -13,5 +13,5 @@ export declare abstract class SimultaneousMoveEffectRule extends SimultaneousEff
13
13
  onCardMoved(move: MoveItem): MaterialMove[];
14
14
  onCardSacrificed(move: MoveItem): MaterialMove[];
15
15
  getMovesAfterPlayersDone(): MaterialMove[];
16
- updatePlayersGrids(): MoveItem[];
16
+ updatePlayersGrids(): void;
17
17
  }
@@ -49,17 +49,16 @@ export class SimultaneousMoveEffectRule extends SimultaneousEffectRule {
49
49
  return [this.endPlayerTurn(move.location.player)];
50
50
  }
51
51
  getMovesAfterPlayersDone() {
52
- return [...this.updatePlayersGrids(), ...super.getMovesAfterPlayersDone()];
52
+ this.updatePlayersGrids();
53
+ return super.getMovesAfterPlayersDone();
53
54
  }
54
55
  updatePlayersGrids() {
55
- const moves = [];
56
56
  const events = [];
57
- for (const player of this.game.players) {
57
+ const triggerEffectsRule = new TriggerEffectsRule(this.game);
58
+ for (const player of triggerEffectsRule.getPlayersByEffectPriorityOrder()) {
58
59
  const pantheon = new Pantheon(this.game, player);
59
60
  events.push(...pantheon.updateGrid());
60
- moves.push(...pantheon.gainBonus());
61
61
  }
62
- new TriggerEffectsRule(this.game).triggerEffects(...events);
63
- return moves;
62
+ triggerEffectsRule.triggerEffects(...events);
64
63
  }
65
64
  }
@@ -1,20 +1,9 @@
1
- import { Destiny } from '../../Destiny';
2
- import { LocationType } from '../../LocationType';
3
- import { Pantheon } from '../../Pantheon';
1
+ import { VishnuEffectRule } from '../../../rules/effects/VishnuEffectRule';
4
2
  import { isPlaced } from '../Effect';
5
- import { PlayerEffectRule } from '../PlayerEffectRule';
6
3
  export const Vishnu = {
7
4
  invoke: [{}, { gem: 1, favor: 1 }, { gem: 1 }],
8
5
  effect: {
9
6
  trigger: isPlaced,
10
- rule: class VishnuEffectRule extends PlayerEffectRule {
11
- playEffect() {
12
- const godsToExile = new Destiny(this.game).cardsWithoutToken.location(LocationType.SanctuaryGodSpot);
13
- if (!godsToExile.length)
14
- return [];
15
- const pantheon = new Pantheon(this.game, this.player);
16
- return [...godsToExile.deleteItems(), pantheon.gainFavor(godsToExile.length * 2)];
17
- }
18
- }
7
+ rule: VishnuEffectRule
19
8
  }
20
9
  };
@@ -10,7 +10,7 @@ export class EndGameRule extends PlayerTurnRule {
10
10
  new TriggerEffectsRule(this.game).triggerEffects({ type: TriggerEventType.EndOfGame });
11
11
  const pendingEffects = this.remind(Memory.PendingEffects);
12
12
  if (pendingEffects.length) {
13
- const cardIndex = pendingEffects[0][0].cardIndex;
13
+ const cardIndex = pendingEffects[0].effects[0].cardIndex;
14
14
  const player = this.material(MaterialType.EntityCard).getItem(cardIndex).location.player;
15
15
  return [this.startPlayerTurn(RuleId.ResolveEffects, player)];
16
16
  }
@@ -1,10 +1,10 @@
1
1
  import { CustomMove, ItemMove, MaterialMove, PlayerTurnRule } from '@gamepark/rules-api';
2
- import { PendingEffect } from '../material/entity/Effect';
2
+ import { CardEffect } from '../material/entity/Effect';
3
3
  import { RuleId } from './RuleId';
4
4
  export declare class PlaceCardsRule extends PlayerTurnRule {
5
5
  isInvoke: boolean;
6
6
  getPlayerMoves(): MaterialMove[];
7
- get infiniteEffects(): PendingEffect[];
7
+ get infiniteEffects(): CardEffect[];
8
8
  isOncePerTurnAvailable(cardIndex: number): boolean;
9
9
  beforeItemMove(move: ItemMove): MaterialMove[];
10
10
  onCustomMove(move: CustomMove): MaterialMove[];
@@ -72,7 +72,7 @@ export class PlaceCardsRule extends PlayerTurnRule {
72
72
  }
73
73
  else if (move.type === CustomMoveType.PlayEffect) {
74
74
  this.memorize(Memory.PendingRule, this.game.rule);
75
- return new ResolveEffectsRule(this.game).onPlayEffect(move.data);
75
+ return new ResolveEffectsRule(this.game).playEffect(move.data);
76
76
  }
77
77
  else if (move.type === CustomMoveType.EndEffect) {
78
78
  return new ResolveEffectsRule(this.game).onEndEffect();
@@ -28,36 +28,37 @@ export declare enum RuleId {
28
28
  CobraInvokeEffect = 30,
29
29
  AmmitEffect = 31,
30
30
  BennuEffect = 32,
31
- ShivaEffect = 33,
32
- RakshasaEffect = 34,
33
- GarudaEffect = 35,
34
- VetalaEffect = 36,
35
- KinnaraEffect = 37,
36
- KinnaraPlaceEffect = 38,
37
- ViracochaEffect = 39,
38
- IllapaEffect = 40,
39
- IllapaSacrifice = 41,
40
- CaimanEffect = 42,
41
- CondorEffect = 43,
42
- SupayEffect = 44,
43
- UkukuEffect = 45,
44
- UkukuPlaceEffect = 46,
45
- YanluoWangEffect = 47,
46
- NuwaEffect = 48,
47
- TianlongEffect = 49,
48
- TianlongOpponentsEffect = 50,
49
- QilinEffect = 51,
50
- FenghuangEffect = 52,
51
- JiangshiEffect = 53,
52
- InkosazanaEffect = 54,
53
- ImpunduluEffect = 55,
54
- WerehyenaEffect = 56,
55
- IdloziEffect = 57,
56
- FairyEffect = 58,
57
- LeprechaunEffect = 59,
58
- SelkieEffect = 60,
59
- DullahanEffect = 61,
60
- GarudaSacrifice = 62,
31
+ VishnuEffect = 33,
32
+ ShivaEffect = 34,
33
+ RakshasaEffect = 35,
34
+ GarudaEffect = 36,
35
+ VetalaEffect = 37,
36
+ KinnaraEffect = 38,
37
+ KinnaraPlaceEffect = 39,
38
+ ViracochaEffect = 40,
39
+ IllapaEffect = 41,
40
+ IllapaSacrifice = 42,
41
+ CaimanEffect = 43,
42
+ CondorEffect = 44,
43
+ SupayEffect = 45,
44
+ UkukuEffect = 46,
45
+ UkukuPlaceEffect = 47,
46
+ YanluoWangEffect = 48,
47
+ NuwaEffect = 49,
48
+ TianlongEffect = 50,
49
+ TianlongOpponentsEffect = 51,
50
+ QilinEffect = 52,
51
+ FenghuangEffect = 53,
52
+ JiangshiEffect = 54,
53
+ InkosazanaEffect = 55,
54
+ ImpunduluEffect = 56,
55
+ WerehyenaEffect = 57,
56
+ IdloziEffect = 58,
57
+ FairyEffect = 59,
58
+ LeprechaunEffect = 60,
59
+ SelkieEffect = 61,
60
+ DullahanEffect = 62,
61
+ GarudaSacrifice = 63,
61
62
  Mythology = 1000,
62
63
  Creature = 1001,
63
64
  Divinity = 1002,
@@ -29,36 +29,37 @@ export var RuleId;
29
29
  RuleId[RuleId["CobraInvokeEffect"] = 30] = "CobraInvokeEffect";
30
30
  RuleId[RuleId["AmmitEffect"] = 31] = "AmmitEffect";
31
31
  RuleId[RuleId["BennuEffect"] = 32] = "BennuEffect";
32
- RuleId[RuleId["ShivaEffect"] = 33] = "ShivaEffect";
33
- RuleId[RuleId["RakshasaEffect"] = 34] = "RakshasaEffect";
34
- RuleId[RuleId["GarudaEffect"] = 35] = "GarudaEffect";
35
- RuleId[RuleId["VetalaEffect"] = 36] = "VetalaEffect";
36
- RuleId[RuleId["KinnaraEffect"] = 37] = "KinnaraEffect";
37
- RuleId[RuleId["KinnaraPlaceEffect"] = 38] = "KinnaraPlaceEffect";
38
- RuleId[RuleId["ViracochaEffect"] = 39] = "ViracochaEffect";
39
- RuleId[RuleId["IllapaEffect"] = 40] = "IllapaEffect";
40
- RuleId[RuleId["IllapaSacrifice"] = 41] = "IllapaSacrifice";
41
- RuleId[RuleId["CaimanEffect"] = 42] = "CaimanEffect";
42
- RuleId[RuleId["CondorEffect"] = 43] = "CondorEffect";
43
- RuleId[RuleId["SupayEffect"] = 44] = "SupayEffect";
44
- RuleId[RuleId["UkukuEffect"] = 45] = "UkukuEffect";
45
- RuleId[RuleId["UkukuPlaceEffect"] = 46] = "UkukuPlaceEffect";
46
- RuleId[RuleId["YanluoWangEffect"] = 47] = "YanluoWangEffect";
47
- RuleId[RuleId["NuwaEffect"] = 48] = "NuwaEffect";
48
- RuleId[RuleId["TianlongEffect"] = 49] = "TianlongEffect";
49
- RuleId[RuleId["TianlongOpponentsEffect"] = 50] = "TianlongOpponentsEffect";
50
- RuleId[RuleId["QilinEffect"] = 51] = "QilinEffect";
51
- RuleId[RuleId["FenghuangEffect"] = 52] = "FenghuangEffect";
52
- RuleId[RuleId["JiangshiEffect"] = 53] = "JiangshiEffect";
53
- RuleId[RuleId["InkosazanaEffect"] = 54] = "InkosazanaEffect";
54
- RuleId[RuleId["ImpunduluEffect"] = 55] = "ImpunduluEffect";
55
- RuleId[RuleId["WerehyenaEffect"] = 56] = "WerehyenaEffect";
56
- RuleId[RuleId["IdloziEffect"] = 57] = "IdloziEffect";
57
- RuleId[RuleId["FairyEffect"] = 58] = "FairyEffect";
58
- RuleId[RuleId["LeprechaunEffect"] = 59] = "LeprechaunEffect";
59
- RuleId[RuleId["SelkieEffect"] = 60] = "SelkieEffect";
60
- RuleId[RuleId["DullahanEffect"] = 61] = "DullahanEffect";
61
- RuleId[RuleId["GarudaSacrifice"] = 62] = "GarudaSacrifice";
32
+ RuleId[RuleId["VishnuEffect"] = 33] = "VishnuEffect";
33
+ RuleId[RuleId["ShivaEffect"] = 34] = "ShivaEffect";
34
+ RuleId[RuleId["RakshasaEffect"] = 35] = "RakshasaEffect";
35
+ RuleId[RuleId["GarudaEffect"] = 36] = "GarudaEffect";
36
+ RuleId[RuleId["VetalaEffect"] = 37] = "VetalaEffect";
37
+ RuleId[RuleId["KinnaraEffect"] = 38] = "KinnaraEffect";
38
+ RuleId[RuleId["KinnaraPlaceEffect"] = 39] = "KinnaraPlaceEffect";
39
+ RuleId[RuleId["ViracochaEffect"] = 40] = "ViracochaEffect";
40
+ RuleId[RuleId["IllapaEffect"] = 41] = "IllapaEffect";
41
+ RuleId[RuleId["IllapaSacrifice"] = 42] = "IllapaSacrifice";
42
+ RuleId[RuleId["CaimanEffect"] = 43] = "CaimanEffect";
43
+ RuleId[RuleId["CondorEffect"] = 44] = "CondorEffect";
44
+ RuleId[RuleId["SupayEffect"] = 45] = "SupayEffect";
45
+ RuleId[RuleId["UkukuEffect"] = 46] = "UkukuEffect";
46
+ RuleId[RuleId["UkukuPlaceEffect"] = 47] = "UkukuPlaceEffect";
47
+ RuleId[RuleId["YanluoWangEffect"] = 48] = "YanluoWangEffect";
48
+ RuleId[RuleId["NuwaEffect"] = 49] = "NuwaEffect";
49
+ RuleId[RuleId["TianlongEffect"] = 50] = "TianlongEffect";
50
+ RuleId[RuleId["TianlongOpponentsEffect"] = 51] = "TianlongOpponentsEffect";
51
+ RuleId[RuleId["QilinEffect"] = 52] = "QilinEffect";
52
+ RuleId[RuleId["FenghuangEffect"] = 53] = "FenghuangEffect";
53
+ RuleId[RuleId["JiangshiEffect"] = 54] = "JiangshiEffect";
54
+ RuleId[RuleId["InkosazanaEffect"] = 55] = "InkosazanaEffect";
55
+ RuleId[RuleId["ImpunduluEffect"] = 56] = "ImpunduluEffect";
56
+ RuleId[RuleId["WerehyenaEffect"] = 57] = "WerehyenaEffect";
57
+ RuleId[RuleId["IdloziEffect"] = 58] = "IdloziEffect";
58
+ RuleId[RuleId["FairyEffect"] = 59] = "FairyEffect";
59
+ RuleId[RuleId["LeprechaunEffect"] = 60] = "LeprechaunEffect";
60
+ RuleId[RuleId["SelkieEffect"] = 61] = "SelkieEffect";
61
+ RuleId[RuleId["DullahanEffect"] = 62] = "DullahanEffect";
62
+ RuleId[RuleId["GarudaSacrifice"] = 63] = "GarudaSacrifice";
62
63
  RuleId[RuleId["Mythology"] = 1000] = "Mythology";
63
64
  RuleId[RuleId["Creature"] = 1001] = "Creature";
64
65
  RuleId[RuleId["Divinity"] = 1002] = "Divinity";
@@ -3,7 +3,7 @@ import { InvokeEffectRule } from '../../material/entity/PlaceCardEffectRule';
3
3
  import { RuleId } from '../RuleId';
4
4
  export declare class AnubisEffectRule extends InvokeEffectRule {
5
5
  ruleId: RuleId;
6
- onRuleStart(): import("@gamepark/rules-api").MaterialMove[];
6
+ times: number;
7
7
  getCardsToPlace(): Material;
8
8
  getAvailableDestinations(): import("@gamepark/rules-api").XYCoordinates[];
9
9
  }
@@ -1,20 +1,10 @@
1
1
  import { InvokeEffectRule } from '../../material/entity/PlaceCardEffectRule';
2
2
  import { LocationType } from '../../material/LocationType';
3
3
  import { MaterialType } from '../../material/MaterialType';
4
- import { Memory } from '../../Memory';
5
4
  import { RuleId } from '../RuleId';
6
5
  export class AnubisEffectRule extends InvokeEffectRule {
7
6
  ruleId = RuleId.AnubisEffect;
8
- onRuleStart() {
9
- if (!this.remind(Memory.EffectCount)) {
10
- this.remind(Memory.PendingEffects).unshift([this.getEffect()]);
11
- this.memorize(Memory.EffectCount, true);
12
- }
13
- else {
14
- this.forget(Memory.EffectCount);
15
- }
16
- return super.onRuleStart();
17
- }
7
+ times = 2;
18
8
  getCardsToPlace() {
19
9
  return this.material(MaterialType.EntityCard).location(LocationType.PlayerDiscard);
20
10
  }
@@ -39,7 +39,7 @@ export class AthenaEffectRule extends PlayerEffectRule {
39
39
  onEndEffect() {
40
40
  const pantheon = new Pantheon(this.game, this.player);
41
41
  new TriggerEffectsRule(this.game).triggerEffects(...pantheon.updateGrid());
42
- return [...pantheon.gainBonus(), ...super.onEndEffect()];
42
+ return super.onEndEffect();
43
43
  }
44
44
  onRuleEnd() {
45
45
  this.forget(Memory.TargetLocations);
@@ -1,4 +1,4 @@
1
- import { Location, Material, MaterialMove, XYCoordinates } from '@gamepark/rules-api';
1
+ import { Location, Material, XYCoordinates } from '@gamepark/rules-api';
2
2
  import { MoveEffectRule } from '../../material/entity/MoveEffectRule';
3
3
  import { SimultaneousSacrificeEffectRule } from '../../material/entity/SimultaneousSacrificeEffectRule';
4
4
  import { PlayerColor } from '../../PlayerColor';
@@ -7,7 +7,7 @@ export declare class CentaurEffectRule extends MoveEffectRule {
7
7
  ruleId: RuleId;
8
8
  getMovingCards(effectCardIndex?: number): Material;
9
9
  isLegalDestination(space: XYCoordinates, cardLocation: Location): boolean;
10
- onEndEffect(): MaterialMove[];
10
+ onEndEffect(): import("@gamepark/rules-api").MaterialMove[];
11
11
  }
12
12
  export declare class CentaurEffectSacrificeRule extends SimultaneousSacrificeEffectRule {
13
13
  ruleId: RuleId;
@@ -4,7 +4,6 @@ import { MoveEffectRule } from '../../material/entity/MoveEffectRule';
4
4
  import { SimultaneousSacrificeEffectRule } from '../../material/entity/SimultaneousSacrificeEffectRule';
5
5
  import { MaterialType } from '../../material/MaterialType';
6
6
  import { Pantheon } from '../../material/Pantheon';
7
- import { Memory } from '../../Memory';
8
7
  import { RuleId } from '../RuleId';
9
8
  import { TriggerEffectsRule } from './TriggerEffectsRule';
10
9
  export class CentaurEffectRule extends MoveEffectRule {
@@ -18,15 +17,13 @@ export class CentaurEffectRule extends MoveEffectRule {
18
17
  onEndEffect() {
19
18
  const pantheon = new Pantheon(this.game, this.player);
20
19
  new TriggerEffectsRule(this.game).triggerEffects(...pantheon.updateGrid());
21
- const moves = pantheon.gainBonus();
22
- moves.push(...new CentaurEffectSacrificeRule(this.game).playEffect());
23
- return moves;
20
+ return new CentaurEffectSacrificeRule(this.game).playEffect();
24
21
  }
25
22
  }
26
23
  export class CentaurEffectSacrificeRule extends SimultaneousSacrificeEffectRule {
27
24
  ruleId = RuleId.CentaurEffectSacrifice;
28
25
  getCardsToSacrifice(player) {
29
- const cardIndex = this.remind(Memory.OngoingEffect).cardIndex;
26
+ const cardIndex = this.getEffect().cardIndex;
30
27
  return super
31
28
  .getCardsToSacrifice(player)
32
29
  .index((index) => index !== cardIndex)
@@ -2,6 +2,6 @@ import { InvokeEffectRule } from '../../material/entity/PlaceCardEffectRule';
2
2
  import { RuleId } from '../RuleId';
3
3
  export declare class InkosazanaEffectRule extends InvokeEffectRule {
4
4
  ruleId: RuleId;
5
- onRuleStart(): import("@gamepark/rules-api").MaterialMove[];
5
+ times: number;
6
6
  getCardsToPlace(): import("@gamepark/rules-api").Material<number, number, number>;
7
7
  }
@@ -1,20 +1,10 @@
1
1
  import { InvokeEffectRule } from '../../material/entity/PlaceCardEffectRule';
2
2
  import { LocationType } from '../../material/LocationType';
3
3
  import { MaterialType } from '../../material/MaterialType';
4
- import { Memory } from '../../Memory';
5
4
  import { RuleId } from '../RuleId';
6
5
  export class InkosazanaEffectRule extends InvokeEffectRule {
7
6
  ruleId = RuleId.InkosazanaEffect;
8
- onRuleStart() {
9
- if (!this.remind(Memory.EffectCount)) {
10
- this.remind(Memory.PendingEffects).unshift([this.getEffect()]);
11
- this.memorize(Memory.EffectCount, true);
12
- }
13
- else {
14
- this.forget(Memory.EffectCount);
15
- }
16
- return super.onRuleStart();
17
- }
7
+ times = 2;
18
8
  getCardsToPlace() {
19
9
  return this.material(MaterialType.EntityCard).location(LocationType.PlayerDiscard).player(this.player);
20
10
  }
@@ -10,5 +10,6 @@ export declare abstract class MultipleSimultaneousMoveEffectRule extends Simulta
10
10
  onCardMoved(move: MoveItem): import("@gamepark/rules-api").EndPlayerTurn<number>[];
11
11
  onCardSacrificed(move: MoveItem): MaterialMove[];
12
12
  onRuleEnd(): never[];
13
- updatePlayersGrids(): MoveItem[];
13
+ getMovesAfterPlayersDone(): MaterialMove[];
14
+ collectOpponentBonuses(): MoveItem[];
14
15
  }
@@ -1,3 +1,6 @@
1
+ import { partition } from 'es-toolkit';
2
+ import { CustomMoveType } from '../../CustomMoveType';
3
+ import { PendingEffectsType } from '../../material/entity/Effect';
1
4
  import { SimultaneousMoveEffectRule } from '../../material/entity/SimultaneousMoveEffectRule';
2
5
  import { LocationType } from '../../material/LocationType';
3
6
  import { MaterialType } from '../../material/MaterialType';
@@ -60,12 +63,34 @@ export class MultipleSimultaneousMoveEffectRule extends SimultaneousMoveEffectRu
60
63
  this.forget(Memory.EffectCount);
61
64
  return [];
62
65
  }
63
- updatePlayersGrids() {
64
- const gainBonusMoves = super.updatePlayersGrids();
66
+ getMovesAfterPlayersDone() {
67
+ this.updatePlayersGrids();
68
+ return [...this.collectOpponentBonuses(), this.customMove(CustomMoveType.EndEffect)];
69
+ }
70
+ collectOpponentBonuses() {
71
+ const moves = [];
65
72
  const cardOwner = this.card.location.player;
66
- for (const gainBonusMove of gainBonusMoves) {
67
- gainBonusMove.location.player = cardOwner;
73
+ const pendingEffects = this.remind(Memory.PendingEffects);
74
+ const [bonusGains, otherEffects] = partition(pendingEffects, (effect) => effect.type === PendingEffectsType.BonusGains && effect.player !== cardOwner);
75
+ this.memorize(Memory.PendingEffects, otherEffects);
76
+ for (const bonusGain of bonusGains) {
77
+ if (bonusGain.type === PendingEffectsType.BonusGains) {
78
+ for (const y of bonusGain.lines) {
79
+ moves.push(this.material(MaterialType.GemToken)
80
+ .location(LocationType.PantheonLineBonus)
81
+ .player(bonusGain.player)
82
+ .location((l) => l.y === y)
83
+ .moveItem({ type: LocationType.PlayerGems, player: cardOwner }));
84
+ }
85
+ for (const x of bonusGain.columns) {
86
+ moves.push(this.material(MaterialType.FavorToken)
87
+ .location(LocationType.PantheonColumnBonus)
88
+ .player(bonusGain.player)
89
+ .location((l) => l.x === x)
90
+ .moveItem({ type: LocationType.PlayerFavor, player: cardOwner }));
91
+ }
92
+ }
68
93
  }
69
- return gainBonusMoves;
94
+ return moves;
70
95
  }
71
96
  }
@@ -1,12 +1,15 @@
1
- import { CustomMove, MaterialMove, PlayerTurnRule, RuleMove, RuleStep } from '@gamepark/rules-api';
2
- import { PendingEffect } from '../../material/entity/Effect';
1
+ import { CustomMove, ItemMove, MaterialMove, PlayerTurnRule, RuleMove, RuleStep } from '@gamepark/rules-api';
2
+ import { CardEffect, PlayerPendingEffects } from '../../material/entity/Effect';
3
3
  import { RuleId } from '../RuleId';
4
4
  export declare class ResolveEffectsRule extends PlayerTurnRule {
5
- startEffectsResolution(): import("@gamepark/rules-api").StartPlayerTurn<number, RuleId.ResolveEffects>[] | import("@gamepark/rules-api").StartRule<RuleId.ResolveEffects>[];
6
- onRuleStart(_move: RuleMove, previousRule: RuleStep): CustomMove[];
7
- getPlayerMoves(): CustomMove[];
5
+ startEffectsResolution(): import("@gamepark/rules-api").StartRule<RuleId.ResolveEffects>[] | import("@gamepark/rules-api").StartPlayerTurn<import("../../PlayerColor").PlayerColor, RuleId.ResolveEffects>[];
6
+ isAutoEffects(): boolean;
7
+ onRuleStart(_move: RuleMove, previousRule: RuleStep): MaterialMove[];
8
+ getPendingEffects(): PlayerPendingEffects;
9
+ getPlayerMoves(): CustomMove[] | import("@gamepark/rules-api").MoveItem<number, number, number>[];
10
+ beforeItemMove(move: ItemMove): CustomMove[];
8
11
  onCustomMove(move: CustomMove): MaterialMove[];
9
- onPlayEffect(pendingEffect: PendingEffect): MaterialMove[];
10
- playEffect(pendingEffect: PendingEffect): MaterialMove[];
12
+ onPlayEffect(effect: CardEffect): MaterialMove[];
13
+ playEffect(cardEffect: CardEffect): MaterialMove[];
11
14
  onEndEffect(): MaterialMove[];
12
15
  }
@@ -1,36 +1,97 @@
1
- import { MoveKind, PlayerTurnRule } from '@gamepark/rules-api';
1
+ import { isMoveItem, MoveKind, PlayerTurnRule } from '@gamepark/rules-api';
2
2
  import { isEqual } from 'es-toolkit';
3
3
  import { CustomMoveType } from '../../CustomMoveType';
4
- import { oncePerTurn } from '../../material/entity/Effect';
4
+ import { oncePerTurn, PendingEffectsType } from '../../material/entity/Effect';
5
5
  import { getEffects } from '../../material/entity/EntityDescription';
6
+ import { LocationType } from '../../material/LocationType';
6
7
  import { MaterialType } from '../../material/MaterialType';
8
+ import { Pantheon } from '../../material/Pantheon';
7
9
  import { Memory } from '../../Memory';
8
10
  import { EndGameRule } from '../EndGameRule';
9
11
  import { RuleId } from '../RuleId';
10
12
  export class ResolveEffectsRule extends PlayerTurnRule {
11
13
  startEffectsResolution() {
12
- const pendingEffects = this.remind(Memory.PendingEffects)[0];
13
- if (pendingEffects.every((effect) => effect.auto)) {
14
+ const pendingEffects = this.getPendingEffects();
15
+ if (this.isAutoEffects()) {
14
16
  return [this.startRule(RuleId.ResolveEffects)];
15
17
  }
16
- const cardIndex = pendingEffects[0].cardIndex;
17
- const player = this.material(MaterialType.EntityCard).getItem(cardIndex).location.player;
18
- return [this.startPlayerTurn(RuleId.ResolveEffects, player)];
18
+ return [this.startPlayerTurn(RuleId.ResolveEffects, pendingEffects.player)];
19
+ }
20
+ isAutoEffects() {
21
+ const pendingEffects = this.getPendingEffects();
22
+ switch (pendingEffects.type) {
23
+ case PendingEffectsType.CardsEffects:
24
+ return pendingEffects.effects.every((effect) => effect.auto);
25
+ case PendingEffectsType.InvokeGain:
26
+ return true;
27
+ case PendingEffectsType.BonusGains:
28
+ return pendingEffects.columns.length + pendingEffects.lines.length === 1;
29
+ }
19
30
  }
20
31
  onRuleStart(_move, previousRule) {
21
32
  if (!this.remind(Memory.PendingRule)) {
22
33
  this.memorize(Memory.PendingRule, previousRule);
23
34
  }
35
+ const pendingEffects = this.getPendingEffects();
36
+ if (pendingEffects.type === PendingEffectsType.InvokeGain) {
37
+ return [...new Pantheon(this.game, this.player).getInvokeGains(pendingEffects.entity, pendingEffects.line), this.customMove(CustomMoveType.EndEffect)];
38
+ }
24
39
  const moves = this.getPlayerMoves();
25
- return moves.length === 1 ? moves : [];
40
+ if (moves.length === 1) {
41
+ return moves;
42
+ }
43
+ else if (moves.length === 0) {
44
+ return [this.customMove(CustomMoveType.EndEffect)];
45
+ }
46
+ return [];
47
+ }
48
+ getPendingEffects() {
49
+ return this.remind(Memory.PendingEffects)[0];
26
50
  }
27
51
  getPlayerMoves() {
28
- const pendingEffects = this.remind(Memory.PendingEffects)[0] ?? [];
29
- const autoEffect = pendingEffects.find((effect) => effect.auto);
30
- if (autoEffect) {
31
- return [this.customMove(CustomMoveType.PlayEffect, autoEffect)];
52
+ const pendingEffects = this.getPendingEffects();
53
+ if (!pendingEffects)
54
+ return [];
55
+ if (pendingEffects.type === PendingEffectsType.CardsEffects) {
56
+ const autoEffect = pendingEffects.effects.find((effect) => effect.auto);
57
+ if (autoEffect) {
58
+ return [this.customMove(CustomMoveType.PlayEffect, autoEffect)];
59
+ }
60
+ return pendingEffects.effects.map((effect) => this.customMove(CustomMoveType.PlayEffect, effect));
32
61
  }
33
- return pendingEffects.map((effect) => this.customMove(CustomMoveType.PlayEffect, effect));
62
+ else if (pendingEffects.type === PendingEffectsType.BonusGains) {
63
+ return [
64
+ ...this.material(MaterialType.GemToken)
65
+ .location(LocationType.PantheonLineBonus)
66
+ .player(this.player)
67
+ .location((l) => pendingEffects.lines.includes(l.y))
68
+ .moveItems({ type: LocationType.PlayerGems, player: this.player }),
69
+ ...this.material(MaterialType.FavorToken)
70
+ .location(LocationType.PantheonColumnBonus)
71
+ .player(this.player)
72
+ .location((l) => pendingEffects.columns.includes(l.x))
73
+ .moveItems({ type: LocationType.PlayerFavor, player: this.player })
74
+ ];
75
+ }
76
+ return [];
77
+ }
78
+ beforeItemMove(move) {
79
+ const pendingEffects = this.getPendingEffects();
80
+ if (isMoveItem(move) &&
81
+ (move.itemType === MaterialType.GemToken || move.itemType === MaterialType.FavorToken) &&
82
+ pendingEffects.type === PendingEffectsType.BonusGains) {
83
+ const item = this.material(move.itemType).getItem(move.itemIndex);
84
+ if (item.location.type === LocationType.PantheonColumnBonus) {
85
+ pendingEffects.columns = pendingEffects.columns.filter((column) => column !== item.location.x);
86
+ }
87
+ else if (item.location.type === LocationType.PantheonLineBonus) {
88
+ pendingEffects.lines = pendingEffects.lines.filter((line) => line !== item.location.y);
89
+ }
90
+ if (this.getPlayerMoves().length === 0) {
91
+ return [this.customMove(CustomMoveType.EndEffect)];
92
+ }
93
+ }
94
+ return [];
34
95
  }
35
96
  onCustomMove(move) {
36
97
  if (move.type === CustomMoveType.PlayEffect) {
@@ -41,22 +102,23 @@ export class ResolveEffectsRule extends PlayerTurnRule {
41
102
  }
42
103
  return [];
43
104
  }
44
- onPlayEffect(pendingEffect) {
45
- const pendingEffects = this.remind(Memory.PendingEffects);
46
- if (pendingEffects.length) {
47
- pendingEffects[0] = pendingEffects[0].filter((effect) => !isEqual(effect, pendingEffect));
48
- if (pendingEffects[0].length === 0) {
49
- pendingEffects.splice(0, 1);
105
+ onPlayEffect(effect) {
106
+ const pendingEffects = this.getPendingEffects();
107
+ const index = pendingEffects.effects.findIndex((e) => isEqual(e, effect));
108
+ if (index !== -1) {
109
+ pendingEffects.effects.splice(index, 1);
110
+ if (!pendingEffects.effects.length) {
111
+ this.memorize(Memory.PendingEffects, (effects) => effects.slice(1));
50
112
  }
51
113
  }
52
- return this.playEffect(pendingEffect);
114
+ return this.playEffect(effect);
53
115
  }
54
- playEffect(pendingEffect) {
55
- this.memorize(Memory.OngoingEffect, pendingEffect);
56
- const card = this.material(MaterialType.EntityCard).getItem(pendingEffect.cardIndex);
57
- const effect = getEffects(card.id.front)[pendingEffect.effectIndex];
116
+ playEffect(cardEffect) {
117
+ this.memorize(Memory.OngoingEffect, cardEffect);
118
+ const card = this.material(MaterialType.EntityCard).getItem(cardEffect.cardIndex);
119
+ const effect = getEffects(card.id.front)[cardEffect.effectIndex];
58
120
  if (effect.trigger === oncePerTurn) {
59
- this.memorize(Memory.OncePerTurn, (indexes = []) => indexes.concat(pendingEffect.cardIndex));
121
+ this.memorize(Memory.OncePerTurn, (indexes = []) => indexes.concat(cardEffect.cardIndex));
60
122
  }
61
123
  const moves = new effect.rule(this.game).playEffect();
62
124
  if (!moves.some((move) => move.kind === MoveKind.RulesMove)) {
@@ -66,7 +128,12 @@ export class ResolveEffectsRule extends PlayerTurnRule {
66
128
  }
67
129
  onEndEffect() {
68
130
  const moves = [];
69
- this.forget(Memory.OngoingEffect);
131
+ if (this.remind(Memory.OngoingEffect) !== undefined) {
132
+ this.forget(Memory.OngoingEffect);
133
+ }
134
+ else {
135
+ this.memorize(Memory.PendingEffects, (effects) => effects.slice(1));
136
+ }
70
137
  if (this.remind(Memory.PendingEffects).length) {
71
138
  moves.push(...this.startEffectsResolution());
72
139
  }
@@ -6,5 +6,5 @@ export declare class SphinxEffectRule extends MoveEffectRule {
6
6
  getMovingCards(): Material;
7
7
  isLegalDestination(space: XYCoordinates, cardLocation: Location): boolean;
8
8
  onCardMoved(move: MoveItem): import("@gamepark/rules-api").MaterialMove[];
9
- updateGridAfterMoves(): MoveItem[];
9
+ updateGridAfterMoves(): void;
10
10
  }
@@ -1,11 +1,12 @@
1
1
  import { Location, MaterialRulesPart } from '@gamepark/rules-api';
2
- import { PendingEffect, TriggerEvent } from '../../material/entity/Effect';
2
+ import { Entity } from '../../material/Entity';
3
+ import { CardEffect, TriggerEvent } from '../../material/entity/Effect';
3
4
  import { PlayerColor } from '../../PlayerColor';
4
5
  export declare class TriggerEffectsRule extends MaterialRulesPart {
5
6
  triggerEffects(...triggerEvents: TriggerEvent[]): void;
6
7
  triggerPlayerEffects(player: PlayerColor, ...triggerEvents: TriggerEvent[]): void;
7
- getPlayerEffects(player: PlayerColor, ...triggerEvents: TriggerEvent[]): PendingEffect[];
8
- triggerCardEffect(cardIndex: number, cardLocation: Location, triggerEvent: TriggerEvent): void;
9
- getCardEffects(cardIndex: number, cardLocation: Location, triggerEvent: TriggerEvent): PendingEffect[];
10
- private getPlayersByEffectPriorityOrder;
8
+ getPlayerEffects(player: PlayerColor, ...triggerEvents: TriggerEvent[]): CardEffect[];
9
+ triggerCardEffect(cardIndex: number, cardLocation: Location, triggerEvent: TriggerEvent, entity?: Entity): void;
10
+ getCardEffects(cardIndex: number, cardLocation: Location, triggerEvent: TriggerEvent, entity?: Entity): CardEffect[];
11
+ getPlayersByEffectPriorityOrder(): PlayerColor[];
11
12
  }
@@ -1,5 +1,5 @@
1
1
  import { MaterialRulesPart } from '@gamepark/rules-api';
2
- import { isTriggeredEffect, TriggerEventType } from '../../material/entity/Effect';
2
+ import { isTriggeredEffect, PendingEffectsType, TriggerEventType } from '../../material/entity/Effect';
3
3
  import { getEffects } from '../../material/entity/EntityDescription';
4
4
  import { MaterialType } from '../../material/MaterialType';
5
5
  import { Pantheon } from '../../material/Pantheon';
@@ -12,9 +12,9 @@ export class TriggerEffectsRule extends MaterialRulesPart {
12
12
  }
13
13
  }
14
14
  triggerPlayerEffects(player, ...triggerEvents) {
15
- const pendingEffects = this.getPlayerEffects(player, ...triggerEvents);
16
- if (pendingEffects.length) {
17
- this.remind(Memory.PendingEffects).unshift(pendingEffects);
15
+ const effects = this.getPlayerEffects(player, ...triggerEvents);
16
+ if (effects.length) {
17
+ this.remind(Memory.PendingEffects).unshift({ type: PendingEffectsType.CardsEffects, player, effects });
18
18
  }
19
19
  }
20
20
  getPlayerEffects(player, ...triggerEvents) {
@@ -40,17 +40,17 @@ export class TriggerEffectsRule extends MaterialRulesPart {
40
40
  }
41
41
  return pendingEffects;
42
42
  }
43
- triggerCardEffect(cardIndex, cardLocation, triggerEvent) {
44
- const pendingEffects = this.getCardEffects(cardIndex, cardLocation, triggerEvent);
45
- if (pendingEffects.length) {
46
- this.remind(Memory.PendingEffects).unshift(pendingEffects);
43
+ triggerCardEffect(cardIndex, cardLocation, triggerEvent, entity) {
44
+ const effects = this.getCardEffects(cardIndex, cardLocation, triggerEvent, entity);
45
+ if (effects.length) {
46
+ this.remind(Memory.PendingEffects).unshift({ type: PendingEffectsType.CardsEffects, player: cardLocation.player, effects });
47
47
  }
48
48
  }
49
- getCardEffects(cardIndex, cardLocation, triggerEvent) {
49
+ getCardEffects(cardIndex, cardLocation, triggerEvent, entity = this.material(MaterialType.EntityCard).getItem(cardIndex).id.front) {
50
50
  const pendingEffects = [];
51
- const card = this.material(MaterialType.EntityCard).getItem(cardIndex);
52
- for (let effectIndex = 0; effectIndex < getEffects(card.id.front).length; effectIndex++) {
53
- const effect = getEffects(card.id.front)[effectIndex];
51
+ const effects = getEffects(entity);
52
+ for (let effectIndex = 0; effectIndex < effects.length; effectIndex++) {
53
+ const effect = effects[effectIndex];
54
54
  if (isTriggeredEffect(effect) && effect.trigger(triggerEvent, { cardIndex, cardLocation, game: this.game })) {
55
55
  pendingEffects.push({ cardIndex, effectIndex, triggerEvent, auto: !!effect.auto });
56
56
  }
@@ -58,7 +58,9 @@ export class TriggerEffectsRule extends MaterialRulesPart {
58
58
  return pendingEffects;
59
59
  }
60
60
  getPlayersByEffectPriorityOrder() {
61
- const firstPlayer = this.game.rule?.player ?? this.material(MaterialType.FirstPlayerToken).getItem().location.player;
61
+ const firstPlayer = this.game.rule?.player ??
62
+ this.remind(Memory.PendingRule)?.player ??
63
+ this.material(MaterialType.FirstPlayerToken).getItem().location.player;
62
64
  const index = this.game.players.indexOf(firstPlayer);
63
65
  return this.game.players.slice(index).concat(this.game.players.slice(0, index));
64
66
  }
@@ -0,0 +1,10 @@
1
+ import { CustomMove, MaterialMove } from '@gamepark/rules-api';
2
+ import { PlayerEffectRule } from '../../material/entity/PlayerEffectRule';
3
+ import { RuleId } from '../RuleId';
4
+ export declare class VishnuEffectRule extends PlayerEffectRule {
5
+ ruleId: RuleId;
6
+ playEffect(): (import("@gamepark/rules-api").MoveItem<number, number, number> | import("@gamepark/rules-api").StartRule<RuleId>)[];
7
+ onRuleStart(): MaterialMove[];
8
+ getPlayerMoves(): CustomMove[];
9
+ onCustomMove(move: CustomMove): MaterialMove[];
10
+ }
@@ -0,0 +1,44 @@
1
+ import { CustomMoveType } from '../../CustomMoveType';
2
+ import { Destiny } from '../../material/Destiny';
3
+ import { PlayerEffectRule } from '../../material/entity/PlayerEffectRule';
4
+ import { LocationType } from '../../material/LocationType';
5
+ import { MaterialType } from '../../material/MaterialType';
6
+ import { Pantheon } from '../../material/Pantheon';
7
+ import { Memory } from '../../Memory';
8
+ import { RuleId } from '../RuleId';
9
+ export class VishnuEffectRule extends PlayerEffectRule {
10
+ ruleId = RuleId.VishnuEffect;
11
+ playEffect() {
12
+ const godsToExile = new Destiny(this.game).cardsWithoutToken.location(LocationType.SanctuaryGodSpot);
13
+ if (!godsToExile.length)
14
+ return [];
15
+ this.memorize(Memory.TargetEntities, godsToExile.getIndexes());
16
+ const hiddenGods = godsToExile.rotation((rotation) => rotation !== undefined);
17
+ return [...hiddenGods.rotateItems(undefined), this.startRule(this.ruleId)];
18
+ }
19
+ onRuleStart() {
20
+ const moves = this.getPlayerMoves();
21
+ return moves.length === 1 ? moves : [];
22
+ }
23
+ getPlayerMoves() {
24
+ return this.remind(Memory.TargetEntities).map((entity) => this.customMove(CustomMoveType.ChooseEntityCard, entity));
25
+ }
26
+ onCustomMove(move) {
27
+ if (move.type === CustomMoveType.ChooseEntityCard) {
28
+ const pantheon = new Pantheon(this.game, this.player);
29
+ const gods = this.material(MaterialType.EntityCard).index(this.remind(Memory.TargetEntities));
30
+ const entity = gods.getItem(move.data).id.front;
31
+ return [
32
+ ...gods.deleteItems(),
33
+ pantheon.gainFavor(gods.length),
34
+ ...pantheon.getInvokeGains(entity, this.card.location.y),
35
+ this.customMove(CustomMoveType.EndEffect)
36
+ ];
37
+ }
38
+ else if (move.type === CustomMoveType.EndEffect) {
39
+ this.forget(Memory.TargetEntities);
40
+ return this.onEndEffect();
41
+ }
42
+ return [];
43
+ }
44
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gamepark/mythologies",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "The rules of Mythologies adapted for Game Park",
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -25,8 +25,8 @@
25
25
  "es-toolkit": "^1.39.10"
26
26
  },
27
27
  "devDependencies": {
28
- "@gamepark/rules-api": "~7.0.0",
28
+ "@gamepark/rules-api": "~7.0.3",
29
29
  "es-toolkit": "^1.39.10"
30
30
  },
31
- "gitHead": "f7ebe2c5280cab7ff6adcbf520ccb564cbf7caf5"
31
+ "gitHead": "0c7452e2a5ab531208e1bc4d994ec47bfd7bfcdd"
32
32
  }