littlejsengine 1.20.0 → 1.22.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.
@@ -0,0 +1,352 @@
1
+ /**
2
+ * LittleJS 3D Level Plugin
3
+ * - A 3D level is a list of objects, each a type name, a position, and a rotation, scale and properties where
4
+ * they are not the default, kept as plain JSON that can be written by hand
5
+ * - level3DAddType names the types a game makes them from, as objectLayersAddType does in 2D
6
+ * - level3DLoad makes every object of a level
7
+ * - Box, Sphere, Cylinder and Light are built in, to block out and light a level with no code
8
+ * - The 3D level editor, in debug builds, edits the level a game loaded
9
+ * @namespace Level3D
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ ///////////////////////////////////////////////////////////////////////////////
15
+
16
+ const LEVEL3D_VERSION = 1; // the format a level's littlejs3D names
17
+
18
+ // the types a level's objects are made from, by name
19
+ const level3DTypes = new Map;
20
+
21
+ // the scale each object had when it was made, the level's scale multiplies it
22
+ const level3DBaseScale = new WeakMap;
23
+
24
+ /** Add a type of object, so level3DLoad makes one wherever a level has an object of that type
25
+ * - The name is a string because minified builds rename classes
26
+ * - A class, or any function with a prototype, is made with new make(pos3D, properties); an arrow function is
27
+ * called as make(pos3D, properties), for what is not an object, like a player start
28
+ * - properties is the defaults with the object's own values over them, and each of the type's own is also set on
29
+ * what was made; a property the type has no default for is in properties and is not set
30
+ * - Give a class a constructor of its own that takes the position: one that hands every argument on to
31
+ * EngineObject3D would hand it the properties as its mesh
32
+ * - An EngineObject3D then gets the object's rotation, and its scale times the scale it was made with
33
+ * - Adding a name again replaces it, Box, Sphere, Cylinder and Light too
34
+ * @param {string} name - The type the objects have in the level
35
+ * @param {Function} make - A class made at each object's position, or a function called with it
36
+ * @param {Object} [defaults] - Properties of each one made, the level editor shows inputs for them
37
+ * @param {TileInfo} [tileInfo] - An icon for the level editor
38
+ * @memberof Level3D
39
+ * @example
40
+ * level3DAddType('Crate', Crate, {health: 3});
41
+ * level3DAddType('PlayerStart', (pos)=> playerStart = pos); */
42
+ function level3DAddType(name, make, defaults={}, tileInfo)
43
+ {
44
+ ASSERT(isStringLike(name), 'object type name must be a string');
45
+ ASSERT(typeof make === 'function', 'make must be a class or function');
46
+ ASSERT(!!defaults && typeof defaults === 'object', 'defaults must be an object');
47
+ level3DTypes.set(String(name), {make, defaults, tileInfo});
48
+ }
49
+
50
+ /** Add a type that is a mesh and nothing more, a static prop with no class to write, for a built mesh or a model
51
+ * - Each object has a color and a solid property, solid collides as the box around the mesh
52
+ * @param {string} name - The type the objects have in the level
53
+ * @param {Mesh} mesh - Shared by every object of the type
54
+ * @param {TileInfo} [tileInfo] - Its texture
55
+ * @param {Color} [color] - Its color, an object's own color property goes over it
56
+ * @memberof Level3D
57
+ * @example
58
+ * level3DAddMesh('Tree', treeMesh, tile(4)); */
59
+ function level3DAddMesh(name, mesh, tileInfo, color=WHITE)
60
+ {
61
+ ASSERT(mesh instanceof Mesh, 'mesh must be a Mesh');
62
+ ASSERT(isColor(color), 'color must be a color');
63
+ level3DAddType(name, function(pos, properties) { return level3DMakeShape(pos, properties, mesh, tileInfo); },
64
+ {color, solid: false}, tileInfo);
65
+ }
66
+
67
+ /** Make the objects of a level, each from the type added for its name
68
+ * - The level is an object: {littlejs3D: 1, objects: [{id, type, pos: [x, y, z]}, ...]}, and {} is a new one
69
+ * - rotation is pitch, yaw and roll in degrees, scale is a number for each axis, both left out when they are
70
+ * the default, and properties holds what differs from the type's defaults
71
+ * - A Color property is a #rrggbb or #rrggbbaa string, a Vector2 or Vector3 an array, as the default says
72
+ * - An object whose type was not added is skipped, with a warning in debug builds
73
+ * - What a file written by hand gets wrong uses the default: a value that is not of its default's type
74
+ * - An object its type can not make is skipped with an error in debug builds, where asserts throw, and the rest
75
+ * of the level is made
76
+ * - A level may hold a map of blocks, in a voxels block: pos, its corner, size, its cells along x, y and z, and
77
+ * blocks, runs of a count and a type along x, then y, then z; it is made a VoxelMap, the first of what is
78
+ * returned, see level3DVoxelSetup for its sheet
79
+ * - A level may hold a terrain, in a terrain block: pos, its center, size, its size in the world along x and z,
80
+ * height, how tall a full height is, color, and heights, rows of 0 to 1 from -z to +z, each from -x to +x; it
81
+ * is made a HeightMap, returned with what else was made
82
+ * - A level may set the scene too, in a scene block beside its objects: sky, three colors for straight up, the
83
+ * horizon and straight down, ambient, how much of them lights the scene, .5 when not given, sunDirection and
84
+ * sunColor, fog, its start and end, fogColor, the horizon color when not given, and shadows; what the block
85
+ * leaves out stays as the game set it, and a level with no block changes nothing
86
+ * @param {Object} level - The level, the level editor edits this same object
87
+ * @return {Array<any>} - What each object's type made, a function that made nothing is left out
88
+ * @memberof Level3D */
89
+ function level3DLoad(level)
90
+ {
91
+ ASSERT(!!level && typeof level === 'object', 'a level is an object, {} for a new one');
92
+ ASSERT(!(level.littlejs3D > LEVEL3D_VERSION), 'the level was made by a newer LittleJS');
93
+ editor3DLevelLoaded(level); // debug builds: the 3D editor takes the level, its autosaved edits go in first
94
+ level3DSceneApply(level.scene);
95
+ const made = [], map = level3DVoxelMap = level3DVoxelsMake(level.voxels);
96
+ map && made.push(map);
97
+ const terrain = level3DTerrainMap = level3DTerrainMake(level.terrain);
98
+ terrain && made.push(terrain);
99
+ for (const object of isArray(level.objects) ? level.objects : [])
100
+ {
101
+ if (!object || typeof object !== 'object') continue;
102
+ const result = level3DMake(object);
103
+ editor3DObjectMade(object, result); // debug builds link it for the 3D editor
104
+ result && made.push(result);
105
+ }
106
+ return made;
107
+ }
108
+
109
+ // the sheet a level's block map is made with, and what sets each one up, see level3DVoxelSetup; and the map the
110
+ // level loaded last made, the one the 3D editor paints
111
+ let level3DVoxelTiles, level3DVoxelSetupMap, level3DVoxelMap;
112
+
113
+ /** How a level's block map is made: the sheet its blocks show tiles of, and a function to set it up
114
+ * - A level's voxels block makes a VoxelMap when the level loads, with texture 0 and the default tile size unless
115
+ * a sheet is given here; a block's type shows that tile of the sheet on every face
116
+ * - setup is called with each map a level makes, to give block types their own faces or make them see-through
117
+ * - Call it before level3DLoad; with no arguments the defaults are back
118
+ * @param {TileInfo} [tileInfo] - The sheet's first tile, as for a VoxelMap
119
+ * @param {function(VoxelMap): void} [setup]
120
+ * @example
121
+ * level3DVoxelSetup(tile(0, 16, 1), (map)=> map.setBlockType(1, {top: 0, side: 1, bottom: 2})); // grass
122
+ * @memberof Level3D */
123
+ function level3DVoxelSetup(tileInfo, setup)
124
+ {
125
+ level3DVoxelTiles = tileInfo;
126
+ level3DVoxelSetupMap = setup;
127
+ }
128
+
129
+ // the most cells a level's map may have, 256 each way
130
+ const LEVEL3D_VOXEL_CELLS = 2 ** 24;
131
+
132
+ // a map's blocks as the file keeps them, runs of a count and a type, cell by cell in the map's own order
133
+ function level3DVoxelsEncode(data)
134
+ {
135
+ const blocks = [];
136
+ for (let i = 0; i < data.length;)
137
+ {
138
+ let end = i + 1;
139
+ while (end < data.length && data[end] === data[i]) ++end;
140
+ blocks.push(end - i, data[i]);
141
+ i = end;
142
+ }
143
+ return blocks;
144
+ }
145
+
146
+ // runs into a map's cells: what they do not reach is empty, a type that is not 0 to 255 is empty, and a run that
147
+ // is not two numbers ends them
148
+ function level3DVoxelsDecode(blocks, data)
149
+ {
150
+ data.fill(0);
151
+ if (!isArray(blocks)) return;
152
+ for (let i = 0, cell = 0; i + 1 < blocks.length && cell < data.length; i += 2)
153
+ {
154
+ const count = blocks[i], type = blocks[i + 1];
155
+ if (!isNumber(count) || !isNumber(type) || count < 0) return;
156
+ const end = min(cell + floor(count), data.length);
157
+ data.fill(type >= 0 && type <= 255 && type % 1 === 0 ? type : 0, cell, end);
158
+ cell = end;
159
+ }
160
+ }
161
+
162
+ // the corner and the size in cells of a level's voxels block, undefined when it is not one a map can be made of
163
+ function level3DVoxelsShape(voxels)
164
+ {
165
+ if (!voxels || typeof voxels !== 'object') return;
166
+ const size = level3DVector(voxels.size);
167
+ if (!size || [size.x, size.y, size.z].some((n)=> n < 1 || n % 1) ||
168
+ size.x * size.y * size.z > LEVEL3D_VOXEL_CELLS) return;
169
+ return {pos: level3DVector(voxels.pos, vec3()), size};
170
+ }
171
+
172
+ // the VoxelMap of a level's voxels block, undefined when it has none or it is wrong
173
+ function level3DVoxelsMake(voxels)
174
+ {
175
+ const shape = level3DVoxelsShape(voxels);
176
+ if (!shape || typeof VoxelMap == 'undefined') return;
177
+ const map = new VoxelMap(shape.pos, shape.size, level3DVoxelTiles || tile());
178
+ level3DVoxelsDecode(voxels.blocks, map.data);
179
+ level3DVoxelSetupMap?.(map);
180
+ map.rebuild();
181
+ return map;
182
+ }
183
+
184
+ // the terrain the level loaded last made, the one the 3D editor sculpts, and the most samples it may have a side
185
+ let level3DTerrainMap;
186
+ const LEVEL3D_TERRAIN_SAMPLES = 129;
187
+
188
+ // what a level's terrain block is made with: its center, its size in the world along x and z, how tall a full
189
+ // height is, and its heights as rows of 0 to 1, a copy, each row as long as the first; undefined when it is not
190
+ // one a HeightMap can be made of
191
+ function level3DTerrainShape(terrain)
192
+ {
193
+ if (!terrain || typeof terrain !== 'object') return;
194
+ const size = terrain.size, rows = terrain.heights, n = LEVEL3D_TERRAIN_SAMPLES;
195
+ if (!isArray(size) || size.length !== 2 || !size.every((v)=> isNumber(v) && v > 0)) return;
196
+ if (!isArray(rows) || rows.length < 2 || rows.length > n || !isArray(rows[0])) return;
197
+ const columns = rows[0].length;
198
+ if (columns < 2 || columns > n || !rows.every((row)=> isArray(row) && row.length === columns)) return;
199
+ const heights = rows.map((row)=> row.map((v)=> isNumber(v) ? clamp(v) : 0));
200
+ return {pos: level3DVector(terrain.pos, vec3()), size: vec2(size[0], size[1]),
201
+ height: isNumber(terrain.height) && terrain.height > 0 ? terrain.height : 1, heights};
202
+ }
203
+
204
+ // the HeightMap of a level's terrain block, undefined when it has none or it is wrong
205
+ function level3DTerrainMake(terrain)
206
+ {
207
+ const shape = level3DTerrainShape(terrain);
208
+ if (!shape || typeof HeightMap == 'undefined') return;
209
+ const map = new HeightMap(shape.heights, shape.size, shape.height, undefined, shape.pos);
210
+ if (/^#([0-9a-f]{6}|[0-9a-f]{8})$/i.test(terrain.color))
211
+ map.color = new Color().setHex(terrain.color);
212
+ return map;
213
+ }
214
+
215
+ // the sky domes levels made, a level's sky takes the place of the one before and disposes it; a dome the game made
216
+ // itself is left whole, for the game to put back
217
+ const level3DSkies = new WeakSet;
218
+
219
+ // set the scene a level's scene block gives: sky as its top, horizon and bottom colors, with ambient how much of
220
+ // them lights the scene, sunDirection and sunColor, fog as its start and end with fogColor, the horizon without one,
221
+ // and shadows; a setting the block does not have, or has wrong, stays as it is
222
+ function level3DSceneApply(scene)
223
+ {
224
+ const r = render3D;
225
+ if (!r || !scene || typeof scene !== 'object') return;
226
+ const color = (value)=> /^#([0-9a-f]{6}|[0-9a-f]{8})$/i.test(value) ? new Color().setHex(value) : undefined;
227
+ const sky = isArray(scene.sky) && scene.sky.length === 3 ? scene.sky.map(color) : [];
228
+ if (sky.length && sky.every((c)=> c))
229
+ {
230
+ // as setSky does, without disposing a dome that is the game's own
231
+ const [top, horizon, bottom] = sky, ambient = isNumber(scene.ambient) ? scene.ambient : .5;
232
+ level3DSkies.has(r.sky) && r.sky.dispose();
233
+ level3DSkies.add(r.sky = buildSky(top, horizon, bottom));
234
+ r.fogColor = horizon.copy();
235
+ r.ambientColor = top.scale(ambient, 1);
236
+ r.ambientGroundColor = bottom.scale(ambient, 1);
237
+ }
238
+ const sun = level3DVector(scene.sunDirection);
239
+ if (sun && sun.lengthSquared())
240
+ r.sunDirection = sun;
241
+ r.sunColor = color(scene.sunColor) || r.sunColor;
242
+ const fog = scene.fog;
243
+ if (isArray(fog) && fog.length === 2 && fog.every((v)=> isNumber(v)))
244
+ r.fogStart = fog[0], r.fogEnd = fog[1];
245
+ r.fogColor = color(scene.fogColor) || r.fogColor;
246
+ if (typeof scene.shadows === 'boolean')
247
+ r.shadows = scene.shadows;
248
+ }
249
+
250
+ // a vec3 of an array of three numbers, as the file has them, or the fallback
251
+ function level3DVector(value, fallback)
252
+ {
253
+ return isArray(value) && value.length === 3 && value.every((v)=> isNumber(v)) ?
254
+ vec3(value[0], value[1], value[2]) : fallback;
255
+ }
256
+
257
+ // the defaults of an object's type, a Color or vector copied for each object, then its own properties over them,
258
+ // each read as its default is; one the file got wrong, a value not of its default's type, keeps the default, and
259
+ // one the type has no default for is kept as it is
260
+ function level3DProperties(type, object)
261
+ {
262
+ const properties = {}, own = object.properties;
263
+ for (const [key, value] of Object.entries(type.defaults))
264
+ properties[key] = value?.copy ? value.copy() : value;
265
+ for (const [key, value] of Object.entries(own && typeof own === 'object' ? own : {}))
266
+ {
267
+ const d = type.defaults[key];
268
+ if (isColor(d))
269
+ /^#([0-9a-f]{6}|[0-9a-f]{8})$/i.test(value) && (properties[key] = new Color().setHex(value));
270
+ else if (isVector3(d))
271
+ properties[key] = level3DVector(value, properties[key]);
272
+ else if (isVector2(d))
273
+ isArray(value) && value.length === 2 && value.every((v)=> isNumber(v)) &&
274
+ (properties[key] = vec2(value[0], value[1]));
275
+ else if (d === undefined || typeof value === typeof d)
276
+ properties[key] = value;
277
+ }
278
+ return properties;
279
+ }
280
+
281
+ // make one object of a level from the type added for its name, undefined when there is no such type or it made
282
+ // nothing
283
+ function level3DMake(object)
284
+ {
285
+ const type = level3DTypes.get(object.type);
286
+ if (!type)
287
+ {
288
+ debug && console.warn(`level3DLoad: no type added for ${object.type}, skipped`);
289
+ return;
290
+ }
291
+ const pos = level3DVector(object.pos, vec3()), properties = level3DProperties(type, object);
292
+ const {make} = type, count = engineObjects.length;
293
+ let result;
294
+ try { result = make.prototype ? new make(pos, properties) : make(pos, properties); }
295
+ catch (error)
296
+ {
297
+ // a value the type can not be made with: in debug builds, where asserts throw, the object is skipped with
298
+ // what it made so far, so the rest of the level loads and the level editor can put the value right
299
+ if (!debug) throw error;
300
+ console.error(`level3DLoad: ${object.type} ${object.id} could not be made, skipped:`, error);
301
+ for (const o of engineObjects.slice(count))
302
+ o.destroy();
303
+ return;
304
+ }
305
+ if (!result || typeof result !== 'object') return;
306
+ if (result instanceof EngineObject3D)
307
+ {
308
+ level3DBaseScale.set(result, result.scale3D.copy());
309
+ result.rotation3D = level3DVector(object.rotation, vec3()).scale(PI / 180);
310
+ result.scale3D = result.scale3D.multiply(level3DVector(object.scale, vec3(1)));
311
+ }
312
+
313
+ // the type's own properties are set on it, one the type has no default for could be a field of the engine's
314
+ for (const key in type.defaults)
315
+ result[key] = properties[key];
316
+ return result;
317
+ }
318
+
319
+ ///////////////////////////////////////////////////////////////////////////////
320
+ // the built-in types
321
+
322
+ // a static object of a mesh, with the color, tile and solid properties the built-in types have
323
+ function level3DMakeShape(pos, properties, mesh, tileInfo, asSphere=false)
324
+ {
325
+ const o = new EngineObject3D(pos, mesh, properties.tile >= 0 ? tile(properties.tile) : tileInfo, properties.color);
326
+ o.collideAsSphere3D = asSphere;
327
+ properties.solid && o.setCollision();
328
+ return o;
329
+ }
330
+
331
+ // the cylinder all Cylinder objects share, built the first time one is made
332
+ let level3DCylinderMesh;
333
+
334
+ level3DAddType('Box', function(pos, properties) { return level3DMakeShape(pos, properties, render3D.boxMesh); },
335
+ {color: WHITE, tile: -1, solid: true});
336
+ level3DAddType('Sphere', function(pos, properties)
337
+ { return level3DMakeShape(pos, properties, render3D.sphereMesh, undefined, true); },
338
+ {color: WHITE, tile: -1, solid: true});
339
+ level3DAddType('Cylinder', function(pos, properties)
340
+ { return level3DMakeShape(pos, properties, level3DCylinderMesh ||= buildCylinder()); },
341
+ {color: WHITE, tile: -1, solid: true});
342
+ // a light, a spotlight with a cone: cone is the angle in degrees from its forward out to the edge of its beam, the
343
+ // object's rotation aims it, softness is how much of the cone fades, and shadows makes it the one that casts them
344
+ level3DAddType('Light', function(pos, properties)
345
+ {
346
+ const light = new Light3D(pos, properties.radius, properties.color, properties.intensity);
347
+ light.coneAngle = max(properties.cone, 0) * PI / 180;
348
+ light.coneSoftness = properties.softness;
349
+ if (properties.shadows && render3D)
350
+ render3D.shadowLight = light;
351
+ return light;
352
+ }, {color: WHITE, radius: 5, intensity: 1, cone: 0, softness: .2, shadows: false});
@@ -465,6 +465,18 @@ class VoxelMap extends EngineObject3D
465
465
  * @ignore */
466
466
  levelRaycast3D(ray) { return this.raycast(ray)?.distance; }
467
467
 
468
+ /** Where a short move goes into a block, for a particle's move in one frame: the part of the move made before it,
469
+ * 0 to 1, and the normal of the face it comes in through; undefined when it hits none, or starts inside one
470
+ * @param {Vector3} from
471
+ * @param {Vector3} to
472
+ * @return {{distance: number, normal: Vector3}|undefined}
473
+ * @ignore */
474
+ levelSegment3D(from, to)
475
+ {
476
+ const hit = this.raycast(new Ray3D(from, to.subtract(from)), 1);
477
+ return hit && hit.distance > 0 ? {distance: hit.distance, normal: hit.normal} : undefined;
478
+ }
479
+
468
480
  /** Keeps an eye on its placement, called automatically each frame */
469
481
  update()
470
482
  {
@@ -0,0 +1,59 @@
1
+ /**
2
+ * LittleJS Scene System Plugin
3
+ * - A game's states, like a title, the game and game over, as scene objects
4
+ * - setScene leaves the current scene, destroys the objects and enters the next
5
+ * - Objects with the persistent flag set outlive every switch
6
+ * - Pause is the engine's paused flag, a scene's update stops with gameUpdate
7
+ * @namespace SceneSystem
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ ///////////////////////////////////////////////////////////////////////////////
13
+
14
+ /** A scene, any object with the hooks it needs, each optional and called as a method so this is the scene
15
+ * @typedef {Object} Scene
16
+ * @property {function():void} [enter] - Called when the scene starts, after the old one left and the objects were destroyed
17
+ * @property {function():void} [leave] - Called before the next scene starts, while the scene is current and its objects are still there
18
+ * @property {function():void} [update] - Called each update after gameUpdate, not while paused or at time scale 0
19
+ * @property {function():void} [render] - Called before gameRender, to draw under the objects
20
+ * @property {function():void} [renderPost] - Called after gameRenderPost, to draw over the game, while paused too; plugins made after the first setScene draw over it
21
+ * @memberof SceneSystem */
22
+
23
+ let sceneCurrent, sceneLeaving = false, scenePluginAdded = false;
24
+
25
+ /** Leave the current scene, destroy every object that is not persistent, and enter the next scene
26
+ * - Setting the current scene again restarts it, and no scene leaves the game with none
27
+ * - The switch happens at once, it can be called from anywhere but a scene's leave
28
+ * @param {Scene} [scene] - The scene to enter
29
+ * @memberof SceneSystem */
30
+ function setScene(scene)
31
+ {
32
+ ASSERT(!sceneLeaving, 'setScene can not be called from a scene leave');
33
+ if (!scenePluginAdded)
34
+ {
35
+ scenePluginAdded = true;
36
+ engineAddPlugin(sceneUpdate, sceneRenderPost, undefined, undefined, sceneRender);
37
+ }
38
+
39
+ if (sceneCurrent)
40
+ {
41
+ // a leave that throws still lets the next switch through
42
+ sceneLeaving = true;
43
+ try { sceneCurrent.leave?.(); }
44
+ finally { sceneLeaving = false; }
45
+ }
46
+ engineObjectsDestroy();
47
+ sceneCurrent = scene;
48
+ scene?.enter?.();
49
+ }
50
+
51
+ /** Get the current scene
52
+ * @return {Scene|undefined}
53
+ * @memberof SceneSystem */
54
+ function getScene() { return sceneCurrent; }
55
+
56
+ // the plugin hooks, update skips the frozen ticks that skip gameUpdate
57
+ function sceneUpdate() { paused || !timeScale || sceneCurrent?.update?.(); }
58
+ function sceneRender() { sceneCurrent?.render?.(); }
59
+ function sceneRenderPost() { sceneCurrent?.renderPost?.(); }
@@ -596,7 +596,9 @@ function tweenUpdate(gameDelta, realDelta)
596
596
  for (let i = list.length; i--;)
597
597
  {
598
598
  const t = list[i];
599
- if (!t.active || t.activePass === pass) continue; // stopped, or started again by a callback this update
599
+ // stopped, or started again by a callback this update, or during an update a callback ran inside it, which
600
+ // counts on from this one
601
+ if (!t.active || t.activePass >= pass) continue;
600
602
  let dt;
601
603
  if (enginePath)
602
604
  {
@@ -620,7 +622,7 @@ function tweenUpdate(gameDelta, realDelta)
620
622
  // Completion: fire end value, remove from active, start the next iteration
621
623
  // of a loop or pingPong, or when there is none it has completed, fire onComplete
622
624
  t.callback(t.interp(0));
623
- if (!t.active || t.activePass === pass)
625
+ if (!t.active || t.activePass >= pass)
624
626
  continue; // stopped or restarted by its own callback, the run it was on ends without completing
625
627
  tweenDeactivate(t);
626
628
  const next = t.thenCallback;