squishjs 1.4.1 → 1.4.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "squishjs",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "description": "squish & unsquish stuff",
5
5
  "scripts": {
6
6
  "test": "node testRunner.js"
package/src/Squisher.js CHANGED
@@ -67,30 +67,34 @@ class Squisher {
67
67
  let toSquish = [];
68
68
 
69
69
  const playerMap = {};
70
+ // Shared (visible-to-everyone) nodes in traversal order, kept so a
71
+ // player frame created mid-traversal can be seeded with everything
72
+ // shared that was squished before that player's first scoped node.
73
+ const sharedNodes = [];
70
74
 
71
75
  if (this.customBottomLayer) {
72
76
  const squishedLayer = [];
73
- this.squishHelper(this.customBottomLayer.root, squishedLayer, this.customBottomLayer.scale, playerMap);
77
+ this.squishHelper(this.customBottomLayer.root, squishedLayer, this.customBottomLayer.scale, playerMap, new Set(), sharedNodes);
74
78
  toSquish.push(squishedLayer);
75
79
  }
76
-
80
+
77
81
  for (const layerIndex in layers) {
78
82
  const squishedLayer = [];
79
83
  const layerInfo = layers[layerIndex];
80
-
84
+
81
85
  const layerScale = layerInfo.scale ? {
82
86
  x: this.scale.x * layerInfo.scale.x,
83
87
  y: this.scale.y * layerInfo.scale.y
84
88
  } : this.scale;
85
89
 
86
- this.squishHelper(layerInfo.root, squishedLayer, scale || layerScale, playerMap);
90
+ this.squishHelper(layerInfo.root, squishedLayer, scale || layerScale, playerMap, new Set(), sharedNodes);
87
91
  toSquish.push(squishedLayer);
88
92
  }
89
93
 
90
94
 
91
- if (this.customTopLayer) {
95
+ if (this.customTopLayer) {
92
96
  const squishedLayer = [];
93
- this.squishHelper(this.customTopLayer.root, squishedLayer, this.customTopLayer.scale, playerMap);
97
+ this.squishHelper(this.customTopLayer.root, squishedLayer, this.customTopLayer.scale, playerMap, new Set(), sharedNodes);
94
98
  toSquish.push(squishedLayer);
95
99
  }
96
100
 
@@ -123,7 +127,7 @@ class Squisher {
123
127
  return !!(sound && sound.enabled === false);
124
128
  }
125
129
 
126
- squishHelper(node, squishedNodes, scale = {x: 1, y: 1}, playerMap = {}, playerIdFilter = new Set()) {
130
+ squishHelper(node, squishedNodes, scale = {x: 1, y: 1}, playerMap = {}, playerIdFilter = new Set(), sharedNodes = []) {
127
131
  if (!node.node.listeners.has(this)) {
128
132
  node.addListener(this);
129
133
  }
@@ -139,7 +143,13 @@ class Squisher {
139
143
  let playerIdsToRemove = new Set();
140
144
  for (let playerId of playerIdFilter) {
141
145
  if (!playerMap[playerId]) {
142
- playerMap[Number(playerId)] = [];
146
+ // First scoped node seen for this player. Seed their frame
147
+ // with every shared node squished so far — otherwise the
148
+ // frame would be missing everything shared (root
149
+ // background included) that preceded it in traversal.
150
+ playerMap[Number(playerId)] = sharedNodes
151
+ .filter(shared => !this._isAudioMutedFor(shared.node, playerId))
152
+ .map(shared => shared.squished);
143
153
  }
144
154
 
145
155
  if (node.node.playerIds.length === 0 || node.node.playerIds.findIndex(i => Number(i) === Number(playerId)) >= 0) {
@@ -154,10 +164,8 @@ class Squisher {
154
164
  playerIdFilter.delete(id);
155
165
  }
156
166
  } else {
167
+ sharedNodes.push({ node, squished });
157
168
  Object.keys(playerMap).forEach(playerId => {
158
- if (!playerMap[playerId]) {
159
- playerMap[Number(playerId)] = [];
160
- }
161
169
  if (!this._isAudioMutedFor(node, playerId)) {
162
170
  playerMap[playerId].push(squished);
163
171
  }
@@ -166,10 +174,10 @@ class Squisher {
166
174
 
167
175
  for (let i = 0; i < node.node.children.length; i++) {
168
176
  // if (node.node.children[i].node.playerIds
169
- // make a new set so child calls within a single generation arent
177
+ // make a new set so child calls within a single generation arent
170
178
  // modifying the same filter set
171
179
  const pathFilter = new Set(playerIdFilter);
172
- this.squishHelper(node.node.children[i], squishedNodes, scale, playerMap, pathFilter);
180
+ this.squishHelper(node.node.children[i], squishedNodes, scale, playerMap, pathFilter, sharedNodes);
173
181
  }
174
182
 
175
183
  }
@@ -82,6 +82,36 @@ test("squisher does not crash on an asset node with an unregistered key", () =>
82
82
  assert(frame && frame.length >= 1);
83
83
  });
84
84
 
85
+ test("player frame includes shared nodes squished before the player's first scoped node", () => {
86
+ // The root background and an early shared sibling precede the scoped
87
+ // node in traversal order. The player's frame must still contain them
88
+ // (seeded on frame creation), in draw order.
89
+ const root = rectNode({ x: 0, y: 0, width: 100, height: 100, fill: COLORS.WHITE });
90
+ const earlyShared = rectNode({ x: 1, y: 1, width: 5, height: 5, fill: COLORS.GREEN });
91
+ const scoped = new GameNode.Shape({
92
+ shapeType: Shapes.POLYGON,
93
+ coordinates2d: ShapeUtils.rectangle(2, 2, 5, 5),
94
+ fill: COLORS.BLUE,
95
+ playerIds: [7]
96
+ });
97
+ const lateShared = rectNode({ x: 3, y: 3, width: 5, height: 5, fill: COLORS.RED });
98
+
99
+ root.addChild(earlyShared);
100
+ root.addChild(scoped);
101
+ root.addChild(lateShared);
102
+
103
+ const game = new FakeGame([{ root, scale: { x: 1, y: 1 } }]);
104
+ const squisher = new Squisher({ game });
105
+
106
+ const frame = squisher.getPlayerFrame(7);
107
+ assert(frame.length === 4);
108
+ const fills = frame.map(squished => unsquish(squished).node.fill);
109
+ verifyArrayEquality(fills[0], COLORS.WHITE);
110
+ verifyArrayEquality(fills[1], COLORS.GREEN);
111
+ verifyArrayEquality(fills[2], COLORS.BLUE);
112
+ verifyArrayEquality(fills[3], COLORS.RED);
113
+ });
114
+
85
115
  test("squisher withholds muted audio from a player-scoped subtree", () => {
86
116
  const root = new GameNode.Shape({
87
117
  shapeType: Shapes.POLYGON,