diff --git a/src/components/beat-generator.js b/src/components/beat-generator.js index dc15f72ea..9e9ed5a5d 100644 --- a/src/components/beat-generator.js +++ b/src/components/beat-generator.js @@ -47,15 +47,6 @@ AFRAME.registerComponent('beat-generator', { 'downright' ], - horizontalPositions: [-0.75, -0.25, 0.25, 0.75], - - horizontalPositionsHumanized: { - 0: 'left', - 1: 'middleleft', - 2: 'middleright', - 3: 'right' - }, - positionHumanized: { topLeft: { layer: 2, index: 0 }, topCenterLeft: { layer: 2, index: 1 }, @@ -73,12 +64,6 @@ AFRAME.registerComponent('beat-generator', { bottomRight: { layer: 0, index: 3 } }, - verticalPositionsHumanized: { - 0: 'bottom', - 1: 'middle', - 2: 'top' - }, - init: function () { this.audioAnalyserEl = document.getElementById('audioanalyser'); this.beatContainer = document.getElementById('beatContainer'); @@ -88,6 +73,7 @@ AFRAME.registerComponent('beat-generator', { this.preloadTime = 0; this.songTime = undefined; this.bpm = undefined; + this.mappingExtensions = false; this.curve = null; this.curveEl = document.getElementById('curve'); this.curveFollowRigEl = document.getElementById('curveFollowRig'); @@ -164,6 +150,7 @@ AFRAME.registerComponent('beat-generator', { this.beatData._obstacles.sort(lessThan); this.beatData._notes.sort(lessThan); this.bpm = this.beatData._beatsPerMinute; + this.mappingExtensions = this.beatData.mappingExtensions; // Performance: Remove all obstacles if there are more than 256 (often used with Noodle Extensions) if (this.beatData._obstacles.length > 256) { @@ -286,8 +273,32 @@ AFRAME.registerComponent('beat-generator', { // Apply sword offset. Blocks arrive on beat in front of the user. const cutDirection = this.orientationsHumanized[noteInfo._cutDirection]; - const horizontalPosition = this.horizontalPositionsHumanized[noteInfo._lineIndex] || 'left'; - const verticalPosition = this.verticalPositionsHumanized[noteInfo._lineLayer] || 'middle'; + let horizontalPosition; + let verticalPosition; + if (this.mappingExtensions) { + // Normally, notes go from 0 to 3 for lineIndex, and 0 to 2 for lineLayer. + // With custom grids, this could be -99 to 99 for both, in theory. + // But, because we want to support decimal values, we need to switch to the + // alternate format, where the values omit everything from -999 to 999. + // + // 0 -> 1000 + // 1 -> 2000 + // 2 -> 3000 + // -1 -> -2000 + horizontalPosition = + noteInfo._lineIndex < 0 + ? noteInfo._lineIndex / 1000 + 1 + : noteInfo._lineIndex / 1000 - 1; + verticalPosition = + noteInfo._lineLayer < 0 + ? noteInfo._lineLayer / 1000 + 1 + : noteInfo._lineLayer / 1000 - 1; + } else { + horizontalPosition = noteInfo._lineIndex; + verticalPosition = noteInfo._lineLayer; + } + if (horizontalPosition === undefined) horizontalPosition = 0 /* left */; + if (verticalPosition === undefined) verticalPosition = 1 /* middle */; // Factor in sword offset and beat anticipation time (percentage). const weaponOffset = this.data.gameMode === 'classic' ? SWORD_OFFSET : PUNCH_OFFSET; @@ -336,7 +347,7 @@ AFRAME.registerComponent('beat-generator', { if (data.has3DOFVR && data.gameMode !== 'viewer') { return; } const durationSeconds = 60 * (wallInfo._duration / this.bpm); - const horizontalPosition = this.horizontalPositionsHumanized[wallInfo._lineIndex] || 'none'; + const horizontalPosition = wallInfo._lineIndex; const isCeiling = wallInfo._type === 1; const length = durationSeconds * data.speed; const width = wallInfo._width / 2; // We want half the reported width. diff --git a/src/components/beat.js b/src/components/beat.js index 557b3cdb7..9ec6971bd 100644 --- a/src/components/beat.js +++ b/src/components/beat.js @@ -203,9 +203,19 @@ AFRAME.registerComponent('beat-system', { } }, - horizontalPositions: {}, + horizontalPositions: { + value: function (noteSpace) { + return this.offset + this.scale * noteSpace; + }, + get middle() { return this.value(1.5); } + }, - verticalPositions: {}, + verticalPositions: { + value: function (noteSpace) { + return this.offset + this.scale * noteSpace; + }, + get middle() { return this.value(1); } + }, /** * Update positioning between blocks, vertically and horizontally depending on @@ -236,26 +246,17 @@ AFRAME.registerComponent('beat-system', { // of extra margin. // For punch mode, we want a wider horizontal spread in punch range, but not vertical. const hMargin = gameMode === CLASSIC ? size : size * 1.2; - horizontalPositions.left = -1.5 * hMargin; - horizontalPositions.middleleft = -0.5 * hMargin; - horizontalPositions.middle = hMargin; - horizontalPositions.middleright = 0.5 * hMargin; - horizontalPositions.right = 1.5 * hMargin; + horizontalPositions.scale = hMargin; + horizontalPositions.offset = -1.5 * hMargin; // Vertical margin based on size of blocks so they don't overlap. // And then overall shifted up and down based on user height (camera Y). // But not too low to go underneath the ground. const bottomHeight = BOTTOM_HEIGHTS[gameMode]; const vMargin = size; - verticalPositions.bottom = Math.max( - BOTTOM_HEIGHT_MIN, - bottomHeight + heightOffset); - verticalPositions.middle = Math.max( - BOTTOM_HEIGHT_MIN + vMargin, - bottomHeight + vMargin + heightOffset); - verticalPositions.top = Math.max( - BOTTOM_HEIGHT_MIN + vMargin * 2, - bottomHeight + (vMargin * 2) + heightOffset); + const vOffset = Math.max(BOTTOM_HEIGHT_MIN, bottomHeight + heightOffset); + verticalPositions.scale = vMargin; + verticalPositions.offset = vOffset; }; })(), @@ -394,7 +395,7 @@ AFRAME.registerComponent('beat', { const supercurve = this.curveEl.components.supercurve; supercurve.getPointAt(songPosition, el.object3D.position); supercurve.alignToCurve(songPosition, el.object3D); - el.object3D.position.x += this.beatSystem.horizontalPositions[horizontalPosition]; + el.object3D.position.x += this.beatSystem.horizontalPositions.value(horizontalPosition); if (data.type !== DOT) { el.object3D.rotation.z = THREE.Math.degToRad(ROTATIONS[cutDirection]); @@ -409,7 +410,7 @@ AFRAME.registerComponent('beat', { const offset = 0.5; el.object3D.position.y -= offset; this.positionStart = el.object3D.position.y; - this.positionChange = this.verticalPositions[verticalPosition] + offset + heightOffset; + this.positionChange = this.verticalPositions.value(verticalPosition) + offset + heightOffset; }, /** @@ -635,7 +636,7 @@ AFRAME.registerComponent('beat', { * Load OBJ from already parsed and loaded OBJ template. */ const geometries = {}; -function setObjModelFromTemplate (el, templateId) { +function setObjModelFromTemplate(el, templateId) { // Load into cache. if (!geometries[templateId]) { const templateEl = document.getElementById(templateId); @@ -659,12 +660,12 @@ function setObjModelFromTemplate (el, templateId) { } } -function getElasticEasing (a, p) { +function getElasticEasing(a, p) { return t => 1 - elastic(a, p)(1 - t); } -function elastic (amplitude, period) { - function minMax (val, min, max) { +function elastic(amplitude, period) { + function minMax(val, min, max) { return Math.min(Math.max(val, min), max); } @@ -679,10 +680,10 @@ function elastic (amplitude, period) { }; } -function remap (value, low1, high1, low2, high2) { +function remap(value, low1, high1, low2, high2) { return low2 + (high2 - low2) * (value - low1) / (high1 - low1); } -function clamp (val, min, max) { +function clamp(val, min, max) { return Math.min(Math.max(val, min), max); } diff --git a/src/components/blade.js b/src/components/blade.js index 9890f0294..3a5f6856d 100644 --- a/src/components/blade.js +++ b/src/components/blade.js @@ -85,8 +85,6 @@ AFRAME.registerComponent('blade', { const bladeLocalPositions = [new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3()]; const bladeLocalTriangle = new THREE.Triangle(); - const LEFT = 'left'; - const RIGHT = 'right'; return function (beat) { if (this.strokeSpeed < 1) { return false; } @@ -107,7 +105,7 @@ AFRAME.registerComponent('blade', { // Increase hitbox for high beats. bbox.copy(beat.bbox); bbox.expandByScalar(0.02); - if (beat.horizontalPosition === LEFT || beat.horizontalPosition === RIGHT) { + if (beat.horizontalPosition < 0.5 || beat.horizontalPosition > 2.5) { bbox.expandByScalar(0.07); } diff --git a/src/components/debug-beat-positioning.js b/src/components/debug-beat-positioning.js index c1e7fb687..121bbf1a7 100644 --- a/src/components/debug-beat-positioning.js +++ b/src/components/debug-beat-positioning.js @@ -1,5 +1,5 @@ -const horizontalPositions = ['left', 'middleleft', 'middleright', 'right']; -const verticalPositions = ['bottom', 'middle', 'top']; +const horizontalPositions = [0, 1, 2, 3]; +const verticalPositions = [0, 1, 2]; /** * Display all beat positions at once. diff --git a/src/components/plume.js b/src/components/plume.js index 3c157a83c..8bbb3af75 100644 --- a/src/components/plume.js +++ b/src/components/plume.js @@ -1,18 +1,14 @@ AFRAME.registerComponent('plume', { schema: { - color: {default: ''}, - cutDirection: {default: ''}, - horizontalPosition: {default: 'middleleft', oneOf: ['left', 'middleleft', 'middleright', 'right']}, - songPosition: {default: 0}, - type: {default: 'arrow', oneOf: ['arrow', 'dot', 'mine']}, - verticalPosition: {default: 'middle', oneOf: ['bottom', 'middle', 'top']} + color: { default: '' }, + cutDirection: { default: '' }, + songPosition: { default: 0 }, + type: { default: 'arrow', oneOf: ['arrow', 'dot', 'mine'] }, }, - horizontalPositions: { - left: -0.95, - middleleft: -0.6, - middleright: 0.6, - right: 0.95 + getHorizontalPosition: noteSpace => { + const centered = noteSpace - 1.5; + return centered < 0 ? 0.35 * centered - 0.425 : 0.35 * centered + 0.425; }, init: function () { @@ -34,14 +30,13 @@ AFRAME.registerComponent('plume', { }, onGenerate: function (songPosition, horizontalPosition, verticalPosition, heightOffset) { - const data = this.data; const el = this.el; // Set position. const supercurve = this.curveEl.components.supercurve; supercurve.getPointAt(songPosition, el.object3D.position); supercurve.alignToCurve(songPosition, el.object3D); - el.object3D.position.x += this.horizontalPositions[horizontalPosition]; - el.object3D.position.y += this.verticalPositions[verticalPosition] + heightOffset; + el.object3D.position.x += this.getHorizontalPosition(horizontalPosition); + el.object3D.position.y += this.verticalPositions.value(verticalPosition) + heightOffset; el.object3D.rotation.z = Math.random() * Math.PI * 2; this.songPosition = songPosition; diff --git a/src/components/wall.js b/src/components/wall.js index 648dcf198..650ffd5a5 100644 --- a/src/components/wall.js +++ b/src/components/wall.js @@ -74,8 +74,8 @@ AFRAME.registerComponent('wall', { // Offset vectors to get the left / right vertex points to pass into curve helper. // Note that curve is upside down so the positions are reversed...normally, this would // read as `+ (width / 2) - 0.25`. - const centerPosition = (-1 * beatSystem.horizontalPositions[horizontalPosition]) - - (width / 2) + 0.25; + const origPosition = beatSystem.horizontalPositions.value(horizontalPosition); + const centerPosition = (-1 * origPosition) - (width / 2) + 0.25; left.x = centerPosition - (width / 2); right.x = centerPosition + (width / 2); diff --git a/src/workers/zip.js b/src/workers/zip.js index 272686989..68d1c4d38 100644 --- a/src/workers/zip.js +++ b/src/workers/zip.js @@ -87,6 +87,10 @@ addEventListener('message', function (evt) { const id = beatmapCharacteristicName + '-' + difficulty; if (data.beats[id] === undefined) { data.beats[id] = beatFiles[beatmapFilename]; + + data.beats[id].mappingExtensions = + Array.isArray(difficultyBeatmap._customData._requirements) && + difficultyBeatmap._customData._requirements.includes('Mapping Extensions'); } } }