User prompt
in game that holdbars should be zigzag, curvy, diaonal but not too vertical make it more horizontal and random dont just one note for it we need to hold click to hear that sound so rebuild the holdbar notes
User prompt
when pitchbaractive is moving in same region for example in slide0 and if it got a small move that sound is replaying dont do it just do when region got changed
User prompt
just play that slide sound when region got change for wherever it is for example if its in slide0 then im moving in slide0 region dont replay it but when region changed play that sound
User prompt
when pitchbaractive is moving in same slide region the slide sound replaying for al little bit moving dont do it just do when it is ended when i cannot hear that sound
User prompt
when im moving in same slide region the slide sound replaying dont do it just do when it is enden when i cannot hear that sound
User prompt
for example when im just moving in slide 6 the slide sound must played once when its ended replay it i mean dont replay it wihile in same slide number fix it for all slide and when control it when slide sound got finish replay it for whereever it is
User prompt
for example when im just moving in slide 6 the slide sound must played once when its ended replay it i mean dont replay it wihile in same slide number fix it for all slide and when control it when slide sound got finish replay it for whereever it is
User prompt
remake the notes zigzag or flat or curvy make it shorty or long way too
User prompt
Replay slide sound while holding in the same region, even if not moving
User prompt
example when pitchactivebar is in slide0 and if i have holding a click that sound is not replaying i wanna hear that sound if i hold
User prompt
the slide sounds dont replay when im holding the click
User prompt
when im moving the pitchbaractive and at same place the sound replaying always dont do it just replay it when the slide sounds ended
User prompt
when pitchbaractive has in the same slide number dont replay it if isnt ended but when it got ended replay it
User prompt
just divide that slide horizontal by 10 and name the bottom one is slide 0 uppest slide is 9 give the assets sound for it
User prompt
in game i saw 2 of that slide0 and slide 0 its near to own but when i change the mouse position the sound replay at start and that not well for hearing so dont make 2 slide0 or slide1 to slide 9 just divide 10 piece for slide
User prompt
watch the pitchbaractive when it move as u know the slide sound has to change control it for no delay
User prompt
when im holding in same slide number the sound doesnt replay for sound
User prompt
when one of slide sound got finished replay it if i have hold a click
User prompt
when i have click whereever is pitchbaractive and if i have holding a button replay it forever that slides' sounds
User prompt
replay the sound wherever it is when i have holding a click
User prompt
when i caught the notes dont play slide sounds for it just focus my click for that sound be attention to my relased or clicked
User prompt
if i have clicked in wrong time i mean it i didnt caught the note domt make combo i have to catch it start of the note and end of note note i have to release rhe click
User prompt
make optimize the slide sound would change to where has it when i caught or couldnt caught the notes
User prompt
sometimes some slide sound mixed dont do it just play it one at the moment
User prompt
do not use slide track sound on game we gonna use slide0 to slide 9 pitch by pitch
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Confetti for success var Confetti = Container.expand(function () { var self = Container.call(this); var asset = self.attachAsset('confetti', { anchorX: 0.5, anchorY: 0.5 }); self.vx = (Math.random() - 0.5) * 20; self.vy = -Math.random() * 20 - 10; self.life = 40 + Math.random() * 20; self.update = function () { self.x += self.vx; self.y += self.vy; self.vy += 1.2; self.life--; asset.rotation += 0.2; if (self.life <= 0) { self.destroy(); } }; return self; }); // Fail splash for miss var FailSplash = Container.expand(function () { var self = Container.call(this); var asset = self.attachAsset('failSplash', { anchorX: 0.5, anchorY: 0.5 }); asset.alpha = 0.7; self.life = 20; self.update = function () { asset.scaleX += 0.1; asset.scaleY += 0.1; asset.alpha -= 0.03; self.life--; if (self.life <= 0) { self.destroy(); } }; return self; }); // Note: All note types (tap, hold, glide) are handled by the Note class with type property. var Note = Container.expand(function () { var self = Container.call(this); // Properties: type ('tap', 'hold', 'glide'), pitch (0-1), time, duration (for hold/glide), glideTo (for glide) self.type = 'tap'; self.pitch = 0.5; // 0 (bottom) to 1 (top) self.time = 0; // When the note should be hit (in ms) self.duration = 0; // For hold/glide notes (ms) self.glideTo = null; // For glide notes: target pitch (0-1) // Visuals: Remove the long bar for all notes, only use the note head and curveBars for visual // Create a tiny invisible asset to keep .noteAsset for hit/miss feedback, but don't show a long bar var noteAsset = self.attachAsset('holdBar', { anchorX: 0.5, anchorY: 0.5 }); noteAsset.width = 1; noteAsset.height = 1; noteAsset.alpha = 0; // Hide the main bar, only use for scaling feedback self.noteAsset = noteAsset; // Add a note head (noteTap) at the start of every note for good catch feedback var noteHead = self.attachAsset('noteTap', { anchorX: 0.5, anchorY: 0.5 }); noteHead.width = 80; noteHead.height = 80; noteHead.x = -noteAsset.width / 2; // Start at the left end of the bar noteHead.y = 0; self.noteHead = noteHead; // Curvy bar for duration: approximate with a sequence of small rectangles to simulate a curve self.curveBars = []; var curveBarCount = 18; // More = smoother for (var i = 0; i < curveBarCount; i++) { var seg = self.attachAsset('holdBar', { anchorX: 0.5, anchorY: 0.5 }); seg.width = 18; seg.height = 24; seg.alpha = 0.7; if (self.type === 'tap') { seg.tint = 0x66ccff; } else if (self.type === 'hold') { seg.tint = 0x44dd88; } else if (self.type === 'glide') { seg.tint = 0xff66aa; } else { seg.tint = 0xffffff; } self.curveBars.push(seg); } // State self.hit = false; // Whether the note has been hit self.missed = false; // Whether the note was missed // For glide: cache start/end pitch self.glideStart = null; self.glideEnd = null; // Update visuals per frame self.update = function () { // All notes are now click-and-hold for a variable duration (not just tap) // The bar is always horizontal and its length reflects the duration // For tap: treat as a short hold, so bar length is based on tapHoldDuration var tapHoldDuration = 400; // ms, how long you must hold for a tap note (difficulty can adjust this) var barLen = 0; var startPitch = self.pitch; var endPitch = self.pitch; if (self.type === 'tap') { barLen = Math.max(80, tapHoldDuration * noteSpeed); self.duration = tapHoldDuration; } else if (self.type === 'hold') { barLen = Math.max(80, (self.duration || 1) * noteSpeed); } else if (self.type === 'glide') { barLen = Math.max(80, (self.duration || 1) * noteSpeed); startPitch = self.glideStart !== null ? self.glideStart : self.pitch; endPitch = self.glideEnd !== null ? self.glideEnd : self.pitch; } // The main bar is hidden, so only show the note head and curveBars as small segments self.noteAsset.x = 0; self.noteAsset.y = 0; // Place the note head at the start of the bar if (self.noteHead) { self.noteHead.x = -barLen / 2; self.noteHead.y = 0; } // Only show curveBars for hold and glide notes, hide for tap for (var i = 0; i < self.curveBars.length; i++) { var seg = self.curveBars[i]; var t = i / (self.curveBars.length - 1); var x = t * barLen; var y = 0; var show = false; if (self.type === 'tap') { // Hide curveBars for tap notes seg.visible = false; continue; } else if (self.type === 'hold') { // For hold notes, use holdShape and holdShapeParams for custom shapes var shape = self.holdShape || (self.holdShapeParams ? 'custom' : null); var params = self.holdShapeParams || {}; if (!shape) { // fallback: alternate diagonal/curvy if (!self._holdStyle) { if (self.duration > 1100) { self._holdStyle = 2; // curvy for long holds } else { self._holdStyle = Math.floor(self.pitch * 10) % 2 + 1; // alternate diagonal/curvy } } if (self._holdStyle === 1) { // Diagonal: y increases/decreases linearly with t var diagAmount = 60; y = (self.pitch < 0.5 ? 1 : -1) * t * diagAmount; } else if (self._holdStyle === 2) { // Curvy: sine wave, but more pronounced y = Math.sin(t * Math.PI * 1.5) * 32; } else { y = 0; } } else if (shape === 'zigzag') { // Zigzag: alternate up/down with frequency and amplitude var amp = typeof params.amplitude === "number" ? params.amplitude : 0.1; var freq = typeof params.frequency === "number" ? params.frequency : 3; var phase = typeof params.phase === "number" ? params.phase : 0; // Zigzag in pitch space, then convert to y var pitchOffset = Math.sin(t * Math.PI * freq + phase) * amp; y = pitchToY(self.pitch + pitchOffset) - pitchToY(self.pitch); } else if (shape === 'curvy') { // Curvy: smooth sine wave var amp = typeof params.amplitude === "number" ? params.amplitude : 0.1; var freq = typeof params.frequency === "number" ? params.frequency : 2; var phase = typeof params.phase === "number" ? params.phase : 0; var pitchOffset = Math.sin(t * Math.PI * freq + phase) * amp; y = pitchToY(self.pitch + pitchOffset) - pitchToY(self.pitch); } else if (shape === 'diagonal') { // Diagonal: linear pitch change var slope = typeof params.slope === "number" ? params.slope : 0.1; var pitchOffset = t * slope; y = pitchToY(self.pitch + pitchOffset) - pitchToY(self.pitch); } else if (shape === 'custom') { // fallback: flat y = 0; } else { y = 0; } show = true; } else if (self.type === 'glide') { // Interpolate pitch for glide var pitch = lerp(startPitch, endPitch, t); var y0 = pitchToY(startPitch); var y1 = pitchToY(endPitch); y = lerp(y0, y1, t) - pitchToY(self.pitch); show = true; } // Only show every other segment for a "dotted" look, or randomize for zigzag if (show) { if (self.type === 'hold' && self._holdStyle === 2) { // For curvy, show every other segment for a dotted curve seg.visible = i % 2 === 0; } else if (self.type === 'hold' && self._holdStyle === 1) { // For diagonal, show every segment for a dashed line seg.visible = i % 2 === 0; } else if (self.type === 'glide') { // For glide, show every other segment for a dotted line seg.visible = i % 2 === 0; } else { seg.visible = false; } } else { seg.visible = false; } seg.x = x - barLen / 2 + 60; seg.y = y; seg.rotation = 0; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222244 }); /**** * Game Code ****/ // 10 slide up sounds, evenly split across the sample // 10 slide down sounds, evenly split across the sample // 10 different slide up and 10 different slide down assets // Music (background track) // Sounds (placeholders, actual sound assets will be loaded by LK) // Comedic feedback // Hit zone // Hold bar (for hold notes) // Note types // Trombone slide bar (vertical pitch bar) // Note: Asset creation is handled automatically by LK based on usage below. // Trombone Slide Showdown - Asset Initialization // --- Constants --- // Trombone pitch sound assets for different pitch levels function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } var gameWidth = 2048; var gameHeight = 2732; // Pitch bar (vertical, left side) var pitchBarHeight = 1800; var pitchBarY = (gameHeight - pitchBarHeight) / 2; var pitchBarX = 120; // Hit zone (where notes should be hit) var hitZoneX = 340; var hitZoneWidth = 140; // Note scroll area var noteStartX = gameWidth + 200; // Offscreen right var noteEndX = hitZoneX; // Hit zone // Note speed (pixels per ms) var noteSpeed = 1.2; // px/ms (tune for difficulty) // --- State --- var notes = []; // All notes in the song var activeNotes = []; // Notes currently on screen var currentTime = 0; // ms since song start var songStarted = false; var songEnded = false; var score = 0; var combo = 0; var maxCombo = 0; var accuracySum = 0; var accuracyCount = 0; // --- Song Selection Menu --- var songList = [{ name: "Showdown (Default)", id: "bgmusic", notes: [ // --- First phrase (0-20s) --- { type: 'tap', time: 1000, pitch: 0.2 }, { type: 'tap', time: 1800, pitch: 0.7 }, { type: 'tap', time: 2600, pitch: 0.5 }, { type: 'hold', time: 3400, pitch: 0.3, duration: 1200, holdShape: 'zigzag', // custom property for zigzag holdShapeParams: { amplitude: 0.12, frequency: 3, phase: 0 } }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({ type: 'hold' }, "type", 'glide'), "time", 5000), "pitch", 0.8), "duration", 1200), "glideTo", 0.2), { type: 'tap', time: 6700, pitch: 0.6 }, { type: 'tap', time: 7400, pitch: 0.4 }, { type: 'tap', time: 8100, pitch: 0.5 }, { time: 8800, pitch: 0.7, duration: 1000, holdShape: 'curvy', holdShapeParams: { amplitude: 0.09, frequency: 2, phase: Math.PI / 2 } }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({ type: 'hold' }, "type", 'glide'), "time", 10200), "pitch", 0.2), "duration", 1000), "glideTo", 0.8), // --- Second phrase (20-40s) --- { type: 'tap', time: 12000, pitch: 0.3 }, { type: 'tap', time: 12800, pitch: 0.6 }, { time: 13600, pitch: 0.5, duration: 1200, holdShape: 'diagonal', holdShapeParams: { slope: -0.18 } }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({ type: 'hold' }, "type", 'glide'), "time", 15200), "pitch", 0.7), "duration", 1200), "glideTo", 0.4), { type: 'tap', time: 17000, pitch: 0.2 }, { type: 'tap', time: 17800, pitch: 0.8 }, { time: 18600, pitch: 0.6, duration: 1000, holdShape: 'zigzag', holdShapeParams: { amplitude: 0.10, frequency: 4, phase: Math.PI / 3 } }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({ type: 'hold' }, "type", 'glide'), "time", 20000), "pitch", 0.3), "duration", 1000), "glideTo", 0.7), // --- Third phrase (40-60s) --- { type: 'tap', time: 21800, pitch: 0.5 }, { type: 'tap', time: 22600, pitch: 0.4 }, { time: 23400, pitch: 0.2, duration: 1200, holdShape: 'curvy', holdShapeParams: { amplitude: 0.13, frequency: 1.5, phase: Math.PI / 4 } }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({ type: 'hold' }, "type", 'glide'), "time", 25000), "pitch", 0.8), "duration", 1200), "glideTo", 0.1), { type: 'tap', time: 26800, pitch: 0.6 }, { type: 'tap', time: 27600, pitch: 0.3 }, { time: 28400, pitch: 0.7, duration: 1000, holdShape: 'diagonal', holdShapeParams: { slope: 0.15 } }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({ type: 'hold' }, "type", 'glide'), "time", 29800), "pitch", 0.2), "duration", 1000), "glideTo", 0.9), // --- Fourth phrase (60-80s) --- { type: 'tap', time: 31600, pitch: 0.4 }, { type: 'tap', time: 32400, pitch: 0.8 }, { time: 33200, pitch: 0.5, duration: 1200, holdShape: 'zigzag', holdShapeParams: { amplitude: 0.11, frequency: 2.5, phase: Math.PI / 6 } }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({ type: 'hold' }, "type", 'glide'), "time", 34800), "pitch", 0.6), "duration", 1200), "glideTo", 0.2), { type: 'tap', time: 36600, pitch: 0.7 }, { type: 'tap', time: 37400, pitch: 0.3 }, { time: 38200, pitch: 0.2, duration: 1000, holdShape: 'curvy', holdShapeParams: { amplitude: 0.08, frequency: 2.2, phase: Math.PI / 5 } }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({ type: 'hold' }, "type", 'glide'), "time", 39600), "pitch", 0.8), "duration", 1000), "glideTo", 0.5), // --- Fifth phrase (80-100s) --- { type: 'tap', time: 41400, pitch: 0.6 }, { type: 'tap', time: 42200, pitch: 0.2 }, { time: 43000, pitch: 0.7, duration: 1200, holdShape: 'diagonal', holdShapeParams: { slope: -0.13 } }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({ type: 'hold' }, "type", 'glide'), "time", 44600), "pitch", 0.3), "duration", 1200), "glideTo", 0.8), { type: 'tap', time: 46400, pitch: 0.5 }, { type: 'tap', time: 47200, pitch: 0.4 }, { time: 48000, pitch: 0.6, duration: 1000, holdShape: 'zigzag', holdShapeParams: { amplitude: 0.09, frequency: 3.5, phase: Math.PI / 7 } }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({ type: 'hold' }, "type", 'glide'), "time", 49400), "pitch", 0.2), "duration", 1000), "glideTo", 0.7), // --- Final phrase (100-120s) --- { type: 'tap', time: 51200, pitch: 0.8 }, { type: 'tap', time: 52000, pitch: 0.3 }, { time: 52800, pitch: 0.5, duration: 1200, holdShape: 'curvy', holdShapeParams: { amplitude: 0.12, frequency: 1.7, phase: Math.PI / 8 } }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({ type: 'hold' }, "type", 'glide'), "time", 54400), "pitch", 0.7), "duration", 1200), "glideTo", 0.2), { type: 'tap', time: 56200, pitch: 0.6 }, { type: 'tap', time: 57000, pitch: 0.4 }, { time: 57800, pitch: 0.2, duration: 1000, holdShape: 'diagonal', holdShapeParams: { slope: 0.14 } }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({ type: 'hold' }, "type", 'glide'), "time", 59200), "pitch", 0.8), "duration", 1000), "glideTo", 0.5), // --- Big finish (last 10s) --- { type: 'tap', time: 61000, pitch: 0.5 }, { type: 'tap', time: 61800, pitch: 0.7 }, { time: 62600, pitch: 0.3, duration: 1200, holdShape: 'zigzag', holdShapeParams: { amplitude: 0.13, frequency: 2.8, phase: Math.PI / 9 } }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({ type: 'hold' }, "type", 'glide'), "time", 64200), "pitch", 0.6), "duration", 1200), "glideTo", 0.1), { type: 'tap', time: 66000, pitch: 0.8 }, { type: 'tap', time: 66800, pitch: 0.2 }, { time: 67600, pitch: 0.4, duration: 1000, holdShape: 'curvy', holdShapeParams: { amplitude: 0.1, frequency: 2.1, phase: Math.PI / 10 } }, { type: 'glide', time: 69000, pitch: 0.7, duration: 1000, glideTo: 0.5 }] } // Add more songs here as needed // { name: "Another Song", id: "music2", notes: [...] } ]; var menuContainer = new Container(); game.addChild(menuContainer); // Menu background REMOVED (pitchBarBg) var menuTitle = new Text2("Select a Song", { size: 120, fill: 0xFFE066 }); menuTitle.anchor.set(0.5, 0); menuTitle.x = gameWidth / 2; menuTitle.y = gameHeight / 2 - 350; menuContainer.addChild(menuTitle); var menuButtons = []; for (var i = 0; i < songList.length; i++) { (function (idx) { var song = songList[idx]; var btn = new Text2(song.name, { size: 90, fill: "#fff" }); btn.anchor.set(0.5, 0.5); btn.x = gameWidth / 2; btn.y = gameHeight / 2 - 100 + idx * 180; btn.interactive = true; btn.buttonMode = true; btn.songIndex = idx; btn.alpha = 0.92; btn.bg = LK.getAsset('holdBar', { anchorX: 0.5, anchorY: 0.5 }); btn.bg.width = 700; btn.bg.height = 140; btn.bg.x = btn.x; btn.bg.y = btn.y; btn.bg.alpha = 0.35; menuContainer.addChild(btn.bg); menuContainer.addChild(btn); menuButtons.push(btn); btn.down = function (x, y, obj) { selectSong(idx); }; })(i); } function selectSong(idx) { // Set up the selected song selectedSong = songList[idx]; notes = []; for (var n = 0; n < selectedSong.notes.length; n++) { // Deep copy to avoid mutation var noteData = {}; for (var k in selectedSong.notes[n]) noteData[k] = selectedSong.notes[n][k]; notes.push(noteData); } // Remove menu menuContainer.visible = false; menuContainer.interactive = false; menuActive = false; // Start game songStarted = false; songEnded = false; currentTime = 0; score = 0; combo = 0; maxCombo = 0; accuracySum = 0; accuracyCount = 0; // Show UI/gameplay elements pitchBarActive.visible = true; hitZone.visible = true; scoreTxt.visible = true; comboTxt.visible = true; feedbackTxt.visible = true; } var selectedSong = null; var menuActive = true; // --- UI Elements --- // Pitch bar background REMOVED // Pitch bar active marker (shows current player pitch) var pitchBarActive = LK.getAsset('pitchBarActive', { anchorX: 0.5, anchorY: 0.5 }); pitchBarActive.x = pitchBarX; pitchBarActive.y = pitchBarY + pitchBarHeight / 2; game.addChild(pitchBarActive); // Perfect zone indicator (shows the "perfect" pitch window) var perfectZoneBar = LK.getAsset('holdBar', { anchorX: 0.5, anchorY: 0.5 }); perfectZoneBar.width = 80; perfectZoneBar.height = 36; perfectZoneBar.alpha = 0.5; perfectZoneBar.tint = 0xfff700; perfectZoneBar.x = pitchBarX - 60; perfectZoneBar.y = pitchBarActive.y; game.addChild(perfectZoneBar); // Pitch deviation indicator (shows how far off pitch the player is) var pitchDeviationBar = LK.getAsset('holdBar', { anchorX: 0.5, anchorY: 0.5 }); pitchDeviationBar.width = 24; pitchDeviationBar.height = 120; pitchDeviationBar.alpha = 0.7; pitchDeviationBar.tint = 0xff3333; pitchDeviationBar.x = pitchBarX + 60; pitchDeviationBar.y = pitchBarActive.y; game.addChild(pitchDeviationBar); // Avatar reaction (mirrors performance quality) var avatarFace = LK.getAsset('noteTap', { anchorX: 0.5, anchorY: 0.5 }); avatarFace.x = gameWidth - 220; avatarFace.y = gameHeight - 320; avatarFace.scaleX = 2.2; avatarFace.scaleY = 2.2; avatarFace.alpha = 0.92; game.addChild(avatarFace); // Hit zone var hitZone = LK.getAsset('hitZone', { anchorX: 0.5, anchorY: 0.5 }); hitZone.x = hitZoneX; hitZone.y = gameHeight / 2; hitZone.alpha = 0.13; game.addChild(hitZone); // Score text var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Combo text var comboTxt = new Text2('', { size: 80, fill: 0xFFE066 }); comboTxt.anchor.set(0.5, 0); LK.gui.top.addChild(comboTxt); comboTxt.y = 130; // Feedback text (shows "Great!", "Miss!", etc) var feedbackTxt = new Text2('', { size: 120, fill: 0xFF66AA }); feedbackTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(feedbackTxt); // Hide gameplay UI until song is selected pitchBarActive.visible = false; hitZone.visible = false; scoreTxt.visible = false; comboTxt.visible = false; feedbackTxt.visible = false; // --- Player Pitch State --- var playerPitch = 0.5; // 0 (bottom) to 1 (top) var isSliding = false; // --- PitchBarActive Sound State --- var pitchBarSound = null; var pitchBarSoundId = null; var pitchBarSoundPlaying = false; var lastPitchBarPitch = null; var pitchBarSoundType = null; // "up" or "down" // Array for 10 slide sounds (slide0 to slide9) var slideSounds = []; for (var i = 0; i < 10; i++) { slideSounds.push(LK.getSound('slide' + i)); } // --- Song Data (set by menu) --- notes = []; // --- Helper Functions --- function clamp(val, min, max) { return val < min ? min : val > max ? max : val; } function lerp(a, b, t) { return a + (b - a) * t; } // Converts pitch (0-1) to y position on pitch bar function pitchToY(pitch) { return pitchBarY + (1 - pitch) * pitchBarHeight; } // Converts y position to pitch (0-1) function yToPitch(y) { var rel = (y - pitchBarY) / pitchBarHeight; return clamp(1 - rel, 0, 1); } // Spawns a note object and adds to game function spawnNote(noteData) { var note = new Note(); note.type = noteData.type; note.pitch = noteData.pitch; note.time = noteData.time; note.duration = noteData.duration || 0; note.glideTo = noteData.glideTo !== undefined ? noteData.glideTo : null; note.hit = false; note.missed = false; note.x = noteStartX; note.y = pitchToY(note.pitch); // For glide: cache start/end pitch if (note.type === 'glide') { note.glideStart = note.pitch; note.glideEnd = note.glideTo; } // All notes are now click-and-hold (handled in game.update logic) // Visuals are handled in Note class (rotated, horizontal bar) activeNotes.push(note); game.addChild(note); } // Shows feedback text and clears after a short time function showFeedback(text, color) { feedbackTxt.setText(text); feedbackTxt.setStyle({ fill: color }); feedbackTxt.alpha = 1; tween(feedbackTxt, { alpha: 0 }, { duration: 700, easing: tween.linear }); } // Spawns confetti at (x, y) function spawnConfetti(x, y) { for (var i = 0; i < 10; i++) { var c = new Confetti(); c.x = x; c.y = y; game.addChild(c); } } // Spawns fail splash at (x, y) function spawnFailSplash(x, y) { var s = new FailSplash(); s.x = x; s.y = y; game.addChild(s); } // --- Input Handling --- // The player can press/hold anywhere on the vertical pitch field to set pitch. // The vertical position of the cursor/finger determines the pitch (0-1). // The player must press/hold as the note enters the hit zone, and track pitch for glides/holds. var pitchFieldActive = false; // True if player is pressing/holding var pitchFieldY = pitchBarY + pitchBarHeight / 2; // Last y position of input game.down = function (x, y, obj) { if (menuActive) return; // Allow input anywhere on the screen to control pitch pitchFieldActive = true; isSliding = true; pitchFieldY = y; playerPitch = yToPitch(y); updatePitchBar(); // Resume the slide sound if it was paused, or play if not started // Set pitch (rate) based on playerPitch (0-1 mapped to 0.7-1.3) var minRate = 0.7; var maxRate = 1.3; var rate = minRate + (maxRate - minRate) * playerPitch; if (pitchBarSound && typeof pitchBarSound.resume === "function" && pitchBarSoundId !== null && pitchBarSoundPlaying) { pitchBarSound.resume(pitchBarSoundId); if (typeof pitchBarSound.setRate === "function") { pitchBarSound.setRate(pitchBarSoundId, rate); } } else if (pitchBarSound && !pitchBarSoundPlaying) { pitchBarSoundId = pitchBarSound.play({ loop: true, volume: 0.7, rate: rate }); pitchBarSoundPlaying = true; } }; game.move = function (x, y, obj) { if (menuActive) return; if (pitchFieldActive) { pitchFieldY = y; playerPitch = yToPitch(y); updatePitchBar(); // Update slide sound pitch (rate) in real time if (pitchBarSound && typeof pitchBarSound.setRate === "function" && pitchBarSoundId !== null) { var minRate = 0.7; var maxRate = 1.3; var rate = minRate + (maxRate - minRate) * playerPitch; pitchBarSound.setRate(pitchBarSoundId, rate); } } }; game.up = function (x, y, obj) { if (menuActive) return; pitchFieldActive = false; isSliding = false; // Pause the slide sound if it's playing (do not stop, so it can be resumed) if (pitchBarSound && pitchBarSoundPlaying && typeof pitchBarSound.pause === "function" && pitchBarSoundId !== null) { pitchBarSound.pause(pitchBarSoundId); } }; // Update pitch bar marker position function updatePitchBar() { pitchBarActive.y = pitchToY(playerPitch); } // --- Game Loop --- game.update = function () { if (menuActive) { // Stop pitchBarActive sound if playing if (pitchBarSound && pitchBarSoundPlaying) { pitchBarSound.stop(); pitchBarSoundPlaying = false; pitchBarSoundId = null; } // Block game update until song is selected return; } // --- PitchBarActive Sound Logic --- // Only play slide sound while holding (isSliding) and pitchBarActive is visible if (pitchBarActive.visible && isSliding) { // Track last Y for pitchBarActive to determine direction if (typeof pitchBarActive.lastY === "undefined") { pitchBarActive.lastY = pitchBarActive.y; } var movingDown = false; var movingUp = false; if (pitchBarActive.y > pitchBarActive.lastY + 1) { movingDown = true; } else if (pitchBarActive.y < pitchBarActive.lastY - 1) { movingUp = true; } // Choose sound type and volume var slideVolume = 0.7; var newSoundType = null; if (movingDown) { slideVolume = 1.0; newSoundType = "down"; } else if (movingUp) { slideVolume = 0.35; newSoundType = "up"; } else { // Not moving, keep last type or default to down newSoundType = pitchBarSoundType || "down"; } // Divide pitch bar into 10 equal horizontal regions, label bottom as slide0 and top as slide9 var idx = 0; if (pitchBarActive && typeof pitchBarActive.y === "number") { // Calculate relative position from bottom (0) to top (1) var rel = (pitchBarActive.y - pitchBarY) / pitchBarHeight; rel = Math.max(0, Math.min(1, rel)); idx = Math.floor((1 - rel) * 10); // 0 = bottom, 9 = top if (idx < 0) idx = 0; if (idx > 9) idx = 9; // Label the region for debug/UX if (!game.slideSoundTxt) { game.slideSoundTxt = new Text2('', { size: 60, fill: "#fff" }); game.slideSoundTxt.anchor.set(0.5, 0.5); game.slideSoundTxt.x = pitchBarActive.x + 220; game.slideSoundTxt.y = pitchBarActive.y; game.addChild(game.slideSoundTxt); } var soundLabel = "slide" + idx; game.slideSoundTxt.setText(soundLabel); game.slideSoundTxt.x = pitchBarActive.x + 220; game.slideSoundTxt.y = pitchBarActive.y; game.slideSoundTxt.visible = true; } if (typeof game.lastSlideSoundIdx === "undefined") { game.lastSlideSoundIdx = -1; } if (typeof game.lastSlideSoundType === "undefined") { game.lastSlideSoundType = null; } if (pitchBarSoundType !== newSoundType || game.lastSlideSoundIdx !== idx) { // Stop previous sound if playing if (pitchBarSound && pitchBarSoundPlaying && typeof pitchBarSound.stop === "function" && pitchBarSoundId !== null) { pitchBarSound.stop(pitchBarSoundId); pitchBarSoundPlaying = false; pitchBarSoundId = null; } // Set new sound asset: pick one of 10 based on pitchBarActive position pitchBarSound = slideSounds[idx]; pitchBarSoundType = newSoundType; // Show which slide sound is mapped to pitchBarActive if (!game.slideSoundTxt) { game.slideSoundTxt = new Text2('', { size: 60, fill: "#fff" }); game.slideSoundTxt.anchor.set(0.5, 0.5); game.slideSoundTxt.x = pitchBarActive.x + 220; game.slideSoundTxt.y = pitchBarActive.y; game.addChild(game.slideSoundTxt); } var soundLabel = "slide" + idx; game.slideSoundTxt.setText(soundLabel); game.slideSoundTxt.x = pitchBarActive.x + 220; game.slideSoundTxt.y = pitchBarActive.y; game.slideSoundTxt.visible = true; // Play the new sound immediately if holding and region changed if (isSliding && pitchBarActive.visible) { var minRate = 0.7; var maxRate = 1.3; var rate = minRate + (maxRate - minRate) * playerPitch; var playOptions = { loop: false, volume: slideVolume, rate: rate }; pitchBarSoundId = pitchBarSound.play(playOptions); pitchBarSoundPlaying = true; game.lastSlideSoundIdx = idx; game.lastSlideSoundType = newSoundType; } // Do not replay sound here if still holding in the same region } pitchBarActive.lastY = pitchBarActive.y; // If the sound has ended but we're still holding, replay it at the current position if (pitchBarSound && typeof pitchBarSound.isPlaying === "function" && pitchBarSoundId !== null) { // Only replay if the sound has ended if (!pitchBarSound.isPlaying(pitchBarSoundId)) { // Only replay if we are still holding in the same slide number and type as last play if (typeof game.lastSlideSoundIdx === "undefined") { game.lastSlideSoundIdx = -1; } if (typeof game.lastSlideSoundType === "undefined") { game.lastSlideSoundType = null; } // Replay if we are still in the same slide number and type as last play, and still holding if (game.lastSlideSoundIdx === idx && game.lastSlideSoundType === newSoundType && isSliding) { // Stop any previous instance before replaying to prevent overlap if (typeof pitchBarSound.stop === "function" && pitchBarSoundId !== null) { pitchBarSound.stop(pitchBarSoundId); } // Replay the sound if ended and still holding, at the last known position if possible var minRate = 0.7; var maxRate = 1.3; var rate = minRate + (maxRate - minRate) * playerPitch; var playOptions = { loop: false, volume: slideVolume, rate: rate }; // Always restart from the beginning for infinite replay while holding pitchBarSoundId = pitchBarSound.play(playOptions); pitchBarSoundPlaying = true; // game.lastSlideSoundIdx and game.lastSlideSoundType remain unchanged } // If we are not in the same slide number and type, do not replay } else { pitchBarSoundPlaying = true; // Adjust volume in real time if possible if (typeof pitchBarSound.setVolume === "function") { pitchBarSound.setVolume(pitchBarSoundId, slideVolume); } // --- REPLAY ONLY WHEN SOUND ENDED AND STILL HOLDING --- // Do not replay unless the sound has ended and we are still holding in the same region // --- END REPLAY ONLY WHEN SOUND ENDED AND STILL HOLDING --- } } // Do not start a new sound here if already playing or paused; only replay when ended and still holding in the same region // Only set rate if sound is playing or paused, do not start a new one here if (typeof pitchBarSound.setRate === "function" && pitchBarSoundId !== null) { // playerPitch is 0 (bottom) to 1 (top) // Map to rate: 0.7 (low) to 1.3 (high) var minRate = 0.7; var maxRate = 1.3; var rate = minRate + (maxRate - minRate) * playerPitch; pitchBarSound.setRate(pitchBarSoundId, rate); } } else { // Stop sound immediately if not holding or not visible (no delay) if (pitchBarSound && pitchBarSoundPlaying && typeof pitchBarSound.stop === "function" && pitchBarSoundId !== null) { pitchBarSound.stop(pitchBarSoundId); pitchBarSoundPlaying = false; pitchBarSoundId = null; } // Hide slide sound label if present if (game.slideSoundTxt) { game.slideSoundTxt.visible = false; } } if (!songStarted) { // Start music and timer LK.playMusic(selectedSong ? selectedSong.id : 'bgmusic'); songStarted = true; currentTime = 0; score = 0; combo = 0; maxCombo = 0; accuracySum = 0; accuracyCount = 0; scoreTxt.setText('0'); comboTxt.setText(''); feedbackTxt.setText(''); // Remove any old notes for (var i = activeNotes.length - 1; i >= 0; i--) { activeNotes[i].destroy(); activeNotes.splice(i, 1); } } // Advance time currentTime += 1000 / 60; // 60 FPS // --- Visual pitch deviation indicator and avatar reaction --- var minPitchDiff = 1.0; var bestType = null; var bestPitch = null; for (var i = 0; i < activeNotes.length; i++) { var note = activeNotes[i]; if (!note.hit && !note.missed) { // Only consider notes in the hit zone var inHitZone = Math.abs(note.x - hitZoneX) < hitZoneWidth / 2; if (inHitZone) { var targetPitch = note.pitch; if (note.type === 'glide') { var glideT = clamp((currentTime - note.time) / note.duration, 0, 1); targetPitch = lerp(note.glideStart, note.glideEnd, glideT); } var diff = Math.abs(playerPitch - targetPitch); if (diff < minPitchDiff) { minPitchDiff = diff; bestType = note.type; bestPitch = targetPitch; } } } } // Show deviation bar only if a note is in the hit zone if (minPitchDiff < 1.0) { pitchDeviationBar.visible = true; pitchDeviationBar.y = pitchBarActive.y; // Color: green if close, yellow if moderate, red if far if (minPitchDiff < 0.07) { pitchDeviationBar.tint = 0x44dd88; } else if (minPitchDiff < 0.15) { pitchDeviationBar.tint = 0xffe066; } else { pitchDeviationBar.tint = 0xff3333; } // Height: larger if more off pitchDeviationBar.height = 120 + minPitchDiff * 400; // Perfect zone indicator follows the current note's target pitch if (bestPitch !== null) { perfectZoneBar.visible = true; perfectZoneBar.y = pitchToY(bestPitch); } else { perfectZoneBar.visible = false; } } else { pitchDeviationBar.visible = false; perfectZoneBar.visible = false; } // Avatar reaction: happy if close, worried if off, shocked if very off if (minPitchDiff < 0.07) { avatarFace.tint = 0x44dd88; // happy avatarFace.scaleX = 2.2; avatarFace.scaleY = 2.2; } else if (minPitchDiff < 0.15) { avatarFace.tint = 0xffe066; // worried avatarFace.scaleX = 2.0; avatarFace.scaleY = 2.0; } else if (minPitchDiff < 1.0) { avatarFace.tint = 0xff3333; // shocked avatarFace.scaleX = 2.4; avatarFace.scaleY = 2.4; } else { avatarFace.tint = 0xffffff; avatarFace.scaleX = 2.2; avatarFace.scaleY = 2.2; } // Spawn notes as they come into view, only when their time is reached (like Trombone Champ) for (var i = 0; i < notes.length; i++) { var noteData = notes[i]; // Only spawn if not already spawned and the note's scheduled time minus travel time is reached if (!noteData.spawned && noteData.time - (noteStartX - hitZoneX) / noteSpeed <= currentTime) { spawnNote(noteData); noteData.spawned = true; } } // Update notes for (var i = activeNotes.length - 1; i >= 0; i--) { var note = activeNotes[i]; // Calculate note's current x based on time var noteTime = note.time; var t = noteTime - currentTime; note.x = hitZoneX + t * noteSpeed; // For all notes: y position is fixed to their initial pitch, even for glides (no vertical movement) if (!note.hit && !note.missed) { note.y = pitchToY(note.pitch); } // If hit or missed, do not update y (freeze at last value) // Remove notes that have gone offscreen left if (note.x < -200) { note.destroy(); activeNotes.splice(i, 1); continue; } // Check for hit/miss if (!note.hit && !note.missed) { // Tap note: now must be click-and-hold in hit zone for a short time (like a mini-hold) if (note.type === 'tap') { var tapHoldDuration = 400; // ms, must match Note class var hitWindow = tapHoldDuration; // ms, tap is now a short hold var timeDiff = Math.abs(currentTime - note.time); var inHitZone = Math.abs(note.x - hitZoneX) < hitZoneWidth / 2; var pitchDiff = Math.abs(playerPitch - note.pitch); if (inHitZone && timeDiff < hitWindow) { if (isSliding && pitchDiff < 0.12) { // Good hold (tap is now a short hold) if (!note.holdStarted) { note.holdStarted = true; note.holdScore = 0; note.holdTicks = 0; note.holdReleased = false; // Track if player released at end // Do not play slide sound when catching tap notes; only focus on click/release for sound if (!note.slideSoundPlayed) { note.slideSoundPlayed = true; } } note.holdScore += 1; note.holdTicks += 1; note.noteAsset.scaleX = 1.2; note.noteAsset.scaleY = 1.2; } else if (note.holdStarted) { note.noteAsset.scaleX = 1; note.noteAsset.scaleY = 1; } // Track if player released before end of tap hold if (note.holdStarted && !isSliding && !note.holdReleased) { note.holdReleased = true; } } // End of tap "hold" (after hitWindow) if (currentTime > note.time + hitWindow && !note.hit) { note.hit = true; var tapAcc = note.holdTicks ? note.holdScore / note.holdTicks : 0; // Only allow combo if player started at correct time AND released at end (not still holding) if (tapAcc > 0.7 && note.holdStarted && note.holdReleased) { // Combo multiplier: +10% per 10 combo, up to 2x var comboMult = 1 + Math.floor(combo / 10) * 0.1; if (comboMult > 2) comboMult = 2; // Perfect zone: if tapAcc > 0.97, +50 bonus var baseScore = 100; if (tapAcc > 0.97) baseScore += 50; score += Math.round(baseScore * comboMult); combo += 1; if (combo > maxCombo) maxCombo = combo; accuracySum += tapAcc; accuracyCount++; scoreTxt.setText(score + ''); comboTxt.setText(combo > 1 ? combo + ' Combo!' : ''); // Feedback text based on accuracy var fbText = ''; var fbColor = "#ffe066"; if (tapAcc > 0.97) { fbText = 'Perfect!'; fbColor = "#fff700"; } else if (tapAcc > 0.90) { fbText = 'Awesome!'; fbColor = "#aaffaa"; } else if (tapAcc > 0.80) { fbText = 'Good!'; fbColor = "#66ccff"; } else { fbText = 'Bad!'; fbColor = "#ffcc66"; } showFeedback(fbText, fbColor); spawnConfetti(note.x, note.y); LK.getSound('hit').play(); } else { combo = 0; comboTxt.setText(''); showFeedback('Miss!', "#ff3333"); spawnFailSplash(note.x, note.y); LK.getSound('miss').play(); } } // Missed if passed hit window and not started if (currentTime > note.time + hitWindow + 200 && !note.hit) { note.missed = true; combo = 0; comboTxt.setText(''); showFeedback('Miss!', "#ff3333"); spawnFailSplash(note.x, note.y); LK.getSound('miss').play(); } } // Hold note: must hold correct pitch during duration (horizontal, click-and-hold) else if (note.type === 'hold') { var holdStart = note.time; var holdEnd = note.time + note.duration; var inHoldZone = currentTime >= holdStart - 180 && currentTime <= holdEnd + 180; var inHitZone = Math.abs(note.x - hitZoneX) < hitZoneWidth / 2; // --- HEAD CATCH LOGIC --- // Only allow hold scoring if the head of the note is caught in the hit zone at the correct time and pitch if (!note.headCaught) { // Check if the head of the note (the first frame it enters the hit zone) is caught var headInZone = Math.abs(note.x - hitZoneX) < hitZoneWidth / 2; var headTimeOk = Math.abs(currentTime - holdStart) < 180; var headPitchOk = Math.abs(playerPitch - note.pitch) < 0.13 && isSliding; if (headInZone && headTimeOk && headPitchOk) { note.headCaught = true; note.holdStarted = true; note.holdScore = 0; note.holdTicks = 0; // Do not play slide sound when catching hold note head; only focus on click/release for sound if (!note.slideSoundPlayed) { note.slideSoundPlayed = true; } note.headCatchTime = currentTime; note.noteAsset.scaleX = 1.2; note.noteAsset.scaleY = 1.2; } } // Only allow hold scoring if head was caught if (note.headCaught && inHoldZone && inHitZone) { if (isSliding && Math.abs(playerPitch - note.pitch) < 0.13) { // Good hold note.holdScore += 1; note.holdTicks += 1; note.noteAsset.scaleX = 1.2; note.noteAsset.scaleY = 1.2; } else { note.noteAsset.scaleX = 1; note.noteAsset.scaleY = 1; } // Track if player released before end of hold if (!isSliding && !note.holdReleased) { note.holdReleased = true; } } else if (note.holdStarted) { note.noteAsset.scaleX = 1; note.noteAsset.scaleY = 1; } // End of hold: only if head was caught if (currentTime > holdEnd && !note.hit) { note.hit = true; var holdAcc = note.holdTicks ? note.holdScore / note.holdTicks : 0; // Only allow combo if head was caught, accuracy is good, and player released at end if (note.headCaught && holdAcc > 0.7 && note.holdReleased) { score += 180; combo += 1; if (combo > maxCombo) maxCombo = combo; accuracySum += holdAcc; accuracyCount++; scoreTxt.setText(score + ''); comboTxt.setText(combo > 1 ? combo + ' Combo!' : ''); // Feedback text based on accuracy var fbText = ''; var fbColor = "#44dd88"; if (holdAcc > 0.97) { fbText = 'Perfect!'; fbColor = "#fff700"; } else if (holdAcc > 0.90) { fbText = 'Awesome!'; fbColor = "#aaffaa"; } else if (holdAcc > 0.80) { fbText = 'Good!'; fbColor = "#66ccff"; } else { fbText = 'Bad!'; fbColor = "#ffcc66"; } showFeedback(fbText, fbColor); spawnConfetti(note.x, note.y); LK.getSound('tromboneGood').play(); } else { combo = 0; comboTxt.setText(''); showFeedback('Miss!', "#ff3333"); spawnFailSplash(note.x, note.y); LK.getSound('miss').play(); } } // Missed if passed hold window and not started or head not caught if (currentTime > holdEnd + 200 && !note.hit) { note.missed = true; combo = 0; comboTxt.setText(''); showFeedback('Miss!', "#ff3333"); spawnFailSplash(note.x, note.y); LK.getSound('miss').play(); } } // Glide note: must follow pitch from start to end (horizontal, click-and-hold) else if (note.type === 'glide') { var glideStart = note.time; var glideEnd = note.time + note.duration; var inGlideZone = currentTime >= glideStart - 180 && currentTime <= glideEnd + 180; var inHitZone = Math.abs(note.x - hitZoneX) < hitZoneWidth / 2; if (inGlideZone && inHitZone) { var glideT = clamp((currentTime - glideStart) / note.duration, 0, 1); var targetPitch = lerp(note.glideStart, note.glideEnd, glideT); var pitchDiff = Math.abs(playerPitch - targetPitch); if (isSliding && pitchDiff < 0.15) { // Good glide if (!note.glideStarted) { note.glideStarted = true; note.glideScore = 0; note.glideTicks = 0; note.glideReleased = false; // Track if player released at end // Do not play slide sound when catching glide note start; only focus on click/release for sound if (!note.slideSoundPlayed) { note.slideSoundPlayed = true; } } note.glideScore += 1; note.glideTicks += 1; note.noteAsset.scaleX = 1.2; note.noteAsset.scaleY = 1.2; // Track if player released before end of glide if (note.glideStarted && !isSliding && !note.glideReleased) { note.glideReleased = true; } } else if (note.glideStarted) { note.noteAsset.scaleX = 1; note.noteAsset.scaleY = 1; } } // End of glide if (currentTime > glideEnd && !note.hit) { note.hit = true; var glideAcc = note.glideTicks ? note.glideScore / note.glideTicks : 0; // Only allow combo if accuracy is good, and player released at end if (glideAcc > 0.7 && note.glideStarted && note.glideReleased) { score += 200; combo += 1; if (combo > maxCombo) maxCombo = combo; accuracySum += glideAcc; accuracyCount++; scoreTxt.setText(score + ''); comboTxt.setText(combo > 1 ? combo + ' Combo!' : ''); // Feedback text based on accuracy var fbText = ''; var fbColor = "#ff66aa"; if (glideAcc > 0.97) { fbText = 'Perfect!'; fbColor = "#fff700"; } else if (glideAcc > 0.90) { fbText = 'Awesome!'; fbColor = "#aaffaa"; } else if (glideAcc > 0.80) { fbText = 'Good!'; fbColor = "#66ccff"; } else { fbText = 'Bad!'; fbColor = "#ffcc66"; } showFeedback(fbText, fbColor); spawnConfetti(note.x, note.y); LK.getSound('tromboneGood').play(); } else { combo = 0; comboTxt.setText(''); showFeedback('Miss!', "#ff3333"); spawnFailSplash(note.x, note.y); LK.getSound('miss').play(); } } // Missed if passed glide window and not started if (currentTime > glideEnd + 200 && !note.hit) { note.missed = true; combo = 0; comboTxt.setText(''); showFeedback('Miss!', "#ff3333"); spawnFailSplash(note.x, note.y); LK.getSound('miss').play(); } } } } // Remove hit/missed notes after a delay for (var i = activeNotes.length - 1; i >= 0; i--) { var note = activeNotes[i]; if ((note.hit || note.missed) && currentTime - note.time > 800) { // Reset slideSoundPlayed so it can be triggered again for new notes note.slideSoundPlayed = false; note.destroy(); activeNotes.splice(i, 1); } } // End of song if (!songEnded && currentTime > notes[notes.length - 1].time + 3000) { songEnded = true; // Calculate accuracy and rank var acc = accuracyCount ? Math.round(accuracySum / accuracyCount * 100) : 0; var rank = "D"; if (acc >= 98 && score > 1200) { rank = "S"; } else if (acc >= 90 && score > 1000) { rank = "A"; } else if (acc >= 80 && score > 800) { rank = "B"; } else if (acc >= 60 && score > 500) { rank = "C"; } // --- LIKE POINT END SECTION --- // This is the "end section of like point" where you can finalize points, score, and show results. // (You can add any additional logic here for 'like point' if needed.) // Hide gameplay UI pitchBarActive.visible = false; hitZone.visible = false; scoreTxt.visible = false; comboTxt.visible = false; feedbackTxt.visible = false; // Show result overlay if (typeof resultOverlay !== "undefined" && resultOverlay) { resultOverlay.destroy(); } var resultOverlay = new Container(); game.addChild(resultOverlay); // Dim background REMOVED (pitchBarBg) // Title var resultTitle = new Text2("Episode Complete!", { size: 120, fill: 0xFFE066 }); resultTitle.anchor.set(0.5, 0); resultTitle.x = gameWidth / 2; resultTitle.y = gameHeight / 2 - 480; resultOverlay.addChild(resultTitle); // Rank var rankText = new Text2("Rank: " + rank, { size: 200, fill: rank === "S" ? "#ffd700" : rank === "A" ? "#aaffaa" : rank === "B" ? "#66ccff" : rank === "C" ? "#ffcc66" : "#ff6666" }); rankText.anchor.set(0.5, 0); rankText.x = gameWidth / 2; rankText.y = gameHeight / 2 - 260; resultOverlay.addChild(rankText); // Score, Combo, Accuracy var resultStats = new Text2("Score: " + score + "\nMax Combo: " + maxCombo + "\nAccuracy: " + acc + "%", { size: 90, fill: "#fff" }); resultStats.anchor.set(0.5, 0); resultStats.x = gameWidth / 2; resultStats.y = gameHeight / 2 - 10; resultOverlay.addChild(resultStats); // Menu button var menuBtnBg = LK.getAsset('holdBar', { anchorX: 0.5, anchorY: 0.5 }); menuBtnBg.width = 500; menuBtnBg.height = 120; menuBtnBg.x = gameWidth / 2; menuBtnBg.y = gameHeight / 2 + 350; menuBtnBg.alpha = 0.4; resultOverlay.addChild(menuBtnBg); var menuBtn = new Text2("Back to Menu", { size: 90, fill: 0xFFE066 }); menuBtn.anchor.set(0.5, 0.5); menuBtn.x = gameWidth / 2; menuBtn.y = gameHeight / 2 + 350; menuBtn.interactive = true; menuBtn.buttonMode = true; resultOverlay.addChild(menuBtn); menuBtn.down = function (x, y, obj) { // Remove overlay, show menu, reset state resultOverlay.destroy(); menuContainer.visible = true; menuContainer.interactive = true; menuActive = true; // Hide gameplay UI pitchBarActive.visible = false; hitZone.visible = false; scoreTxt.visible = false; comboTxt.visible = false; feedbackTxt.visible = false; // Remove all notes for (var i = activeNotes.length - 1; i >= 0; i--) { activeNotes[i].destroy(); activeNotes.splice(i, 1); } // Reset song state songStarted = false; songEnded = false; currentTime = 0; score = 0; combo = 0; maxCombo = 0; accuracySum = 0; accuracyCount = 0; }; // Block further game update until menu is shown again return; } }; // --- Start music --- // Now handled in game.update after song selection
===================================================================
--- original.js
+++ change.js
@@ -143,23 +143,53 @@
// Hide curveBars for tap notes
seg.visible = false;
continue;
} else if (self.type === 'hold') {
- // For hold notes, sometimes diagonal, sometimes curvy
- if (!self._holdStyle) {
- if (self.duration > 1100) {
- self._holdStyle = 2; // curvy for long holds
+ // For hold notes, use holdShape and holdShapeParams for custom shapes
+ var shape = self.holdShape || (self.holdShapeParams ? 'custom' : null);
+ var params = self.holdShapeParams || {};
+ if (!shape) {
+ // fallback: alternate diagonal/curvy
+ if (!self._holdStyle) {
+ if (self.duration > 1100) {
+ self._holdStyle = 2; // curvy for long holds
+ } else {
+ self._holdStyle = Math.floor(self.pitch * 10) % 2 + 1; // alternate diagonal/curvy
+ }
+ }
+ if (self._holdStyle === 1) {
+ // Diagonal: y increases/decreases linearly with t
+ var diagAmount = 60;
+ y = (self.pitch < 0.5 ? 1 : -1) * t * diagAmount;
+ } else if (self._holdStyle === 2) {
+ // Curvy: sine wave, but more pronounced
+ y = Math.sin(t * Math.PI * 1.5) * 32;
} else {
- self._holdStyle = Math.floor(self.pitch * 10) % 2 + 1; // alternate diagonal/curvy
+ y = 0;
}
- }
- if (self._holdStyle === 1) {
- // Diagonal: y increases/decreases linearly with t
- var diagAmount = 60;
- y = (self.pitch < 0.5 ? 1 : -1) * t * diagAmount;
- } else if (self._holdStyle === 2) {
- // Curvy: sine wave, but more pronounced
- y = Math.sin(t * Math.PI * 1.5) * 32;
+ } else if (shape === 'zigzag') {
+ // Zigzag: alternate up/down with frequency and amplitude
+ var amp = typeof params.amplitude === "number" ? params.amplitude : 0.1;
+ var freq = typeof params.frequency === "number" ? params.frequency : 3;
+ var phase = typeof params.phase === "number" ? params.phase : 0;
+ // Zigzag in pitch space, then convert to y
+ var pitchOffset = Math.sin(t * Math.PI * freq + phase) * amp;
+ y = pitchToY(self.pitch + pitchOffset) - pitchToY(self.pitch);
+ } else if (shape === 'curvy') {
+ // Curvy: smooth sine wave
+ var amp = typeof params.amplitude === "number" ? params.amplitude : 0.1;
+ var freq = typeof params.frequency === "number" ? params.frequency : 2;
+ var phase = typeof params.phase === "number" ? params.phase : 0;
+ var pitchOffset = Math.sin(t * Math.PI * freq + phase) * amp;
+ y = pitchToY(self.pitch + pitchOffset) - pitchToY(self.pitch);
+ } else if (shape === 'diagonal') {
+ // Diagonal: linear pitch change
+ var slope = typeof params.slope === "number" ? params.slope : 0.1;
+ var pitchOffset = t * slope;
+ y = pitchToY(self.pitch + pitchOffset) - pitchToY(self.pitch);
+ } else if (shape === 'custom') {
+ // fallback: flat
+ y = 0;
} else {
y = 0;
}
show = true;
@@ -219,8 +249,38 @@
// Note: Asset creation is handled automatically by LK based on usage below.
// Trombone Slide Showdown - Asset Initialization
// --- Constants ---
// Trombone pitch sound assets for different pitch levels
+function _typeof(o) {
+ "@babel/helpers - typeof";
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
+ return typeof o;
+ } : function (o) {
+ return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
+ }, _typeof(o);
+}
+function _defineProperty(e, r, t) {
+ return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
+ value: t,
+ enumerable: !0,
+ configurable: !0,
+ writable: !0
+ }) : e[r] = t, e;
+}
+function _toPropertyKey(t) {
+ var i = _toPrimitive(t, "string");
+ return "symbol" == _typeof(i) ? i : i + "";
+}
+function _toPrimitive(t, r) {
+ if ("object" != _typeof(t) || !t) return t;
+ var e = t[Symbol.toPrimitive];
+ if (void 0 !== e) {
+ var i = e.call(t, r || "default");
+ if ("object" != _typeof(i)) return i;
+ throw new TypeError("@@toPrimitive must return a primitive value.");
+ }
+ return ("string" === r ? String : Number)(t);
+}
var gameWidth = 2048;
var gameHeight = 2732;
// Pitch bar (vertical, left side)
var pitchBarHeight = 1800;
@@ -266,16 +326,19 @@
}, {
type: 'hold',
time: 3400,
pitch: 0.3,
- duration: 1200
- }, {
- type: 'glide',
- time: 5000,
- pitch: 0.8,
duration: 1200,
- glideTo: 0.2
- }, {
+ holdShape: 'zigzag',
+ // custom property for zigzag
+ holdShapeParams: {
+ amplitude: 0.12,
+ frequency: 3,
+ phase: 0
+ }
+ }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({
+ type: 'hold'
+ }, "type", 'glide'), "time", 5000), "pitch", 0.8), "duration", 1200), "glideTo", 0.2), {
type: 'tap',
time: 6700,
pitch: 0.6
}, {
@@ -286,19 +349,20 @@
type: 'tap',
time: 8100,
pitch: 0.5
}, {
- type: 'hold',
time: 8800,
pitch: 0.7,
- duration: 1000
- }, {
- type: 'glide',
- time: 10200,
- pitch: 0.2,
duration: 1000,
- glideTo: 0.8
- },
+ holdShape: 'curvy',
+ holdShapeParams: {
+ amplitude: 0.09,
+ frequency: 2,
+ phase: Math.PI / 2
+ }
+ }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({
+ type: 'hold'
+ }, "type", 'glide'), "time", 10200), "pitch", 0.2), "duration", 1000), "glideTo", 0.8),
// --- Second phrase (20-40s) ---
{
type: 'tap',
time: 12000,
@@ -307,38 +371,38 @@
type: 'tap',
time: 12800,
pitch: 0.6
}, {
- type: 'hold',
time: 13600,
pitch: 0.5,
- duration: 1200
- }, {
- type: 'glide',
- time: 15200,
- pitch: 0.7,
duration: 1200,
- glideTo: 0.4
- }, {
+ holdShape: 'diagonal',
+ holdShapeParams: {
+ slope: -0.18
+ }
+ }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({
+ type: 'hold'
+ }, "type", 'glide'), "time", 15200), "pitch", 0.7), "duration", 1200), "glideTo", 0.4), {
type: 'tap',
time: 17000,
pitch: 0.2
}, {
type: 'tap',
time: 17800,
pitch: 0.8
}, {
- type: 'hold',
time: 18600,
pitch: 0.6,
- duration: 1000
- }, {
- type: 'glide',
- time: 20000,
- pitch: 0.3,
duration: 1000,
- glideTo: 0.7
- },
+ holdShape: 'zigzag',
+ holdShapeParams: {
+ amplitude: 0.10,
+ frequency: 4,
+ phase: Math.PI / 3
+ }
+ }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({
+ type: 'hold'
+ }, "type", 'glide'), "time", 20000), "pitch", 0.3), "duration", 1000), "glideTo", 0.7),
// --- Third phrase (40-60s) ---
{
type: 'tap',
time: 21800,
@@ -347,38 +411,38 @@
type: 'tap',
time: 22600,
pitch: 0.4
}, {
- type: 'hold',
time: 23400,
pitch: 0.2,
- duration: 1200
- }, {
- type: 'glide',
- time: 25000,
- pitch: 0.8,
duration: 1200,
- glideTo: 0.1
- }, {
+ holdShape: 'curvy',
+ holdShapeParams: {
+ amplitude: 0.13,
+ frequency: 1.5,
+ phase: Math.PI / 4
+ }
+ }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({
+ type: 'hold'
+ }, "type", 'glide'), "time", 25000), "pitch", 0.8), "duration", 1200), "glideTo", 0.1), {
type: 'tap',
time: 26800,
pitch: 0.6
}, {
type: 'tap',
time: 27600,
pitch: 0.3
}, {
- type: 'hold',
time: 28400,
pitch: 0.7,
- duration: 1000
- }, {
- type: 'glide',
- time: 29800,
- pitch: 0.2,
duration: 1000,
- glideTo: 0.9
- },
+ holdShape: 'diagonal',
+ holdShapeParams: {
+ slope: 0.15
+ }
+ }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({
+ type: 'hold'
+ }, "type", 'glide'), "time", 29800), "pitch", 0.2), "duration", 1000), "glideTo", 0.9),
// --- Fourth phrase (60-80s) ---
{
type: 'tap',
time: 31600,
@@ -387,38 +451,40 @@
type: 'tap',
time: 32400,
pitch: 0.8
}, {
- type: 'hold',
time: 33200,
pitch: 0.5,
- duration: 1200
- }, {
- type: 'glide',
- time: 34800,
- pitch: 0.6,
duration: 1200,
- glideTo: 0.2
- }, {
+ holdShape: 'zigzag',
+ holdShapeParams: {
+ amplitude: 0.11,
+ frequency: 2.5,
+ phase: Math.PI / 6
+ }
+ }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({
+ type: 'hold'
+ }, "type", 'glide'), "time", 34800), "pitch", 0.6), "duration", 1200), "glideTo", 0.2), {
type: 'tap',
time: 36600,
pitch: 0.7
}, {
type: 'tap',
time: 37400,
pitch: 0.3
}, {
- type: 'hold',
time: 38200,
pitch: 0.2,
- duration: 1000
- }, {
- type: 'glide',
- time: 39600,
- pitch: 0.8,
duration: 1000,
- glideTo: 0.5
- },
+ holdShape: 'curvy',
+ holdShapeParams: {
+ amplitude: 0.08,
+ frequency: 2.2,
+ phase: Math.PI / 5
+ }
+ }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({
+ type: 'hold'
+ }, "type", 'glide'), "time", 39600), "pitch", 0.8), "duration", 1000), "glideTo", 0.5),
// --- Fifth phrase (80-100s) ---
{
type: 'tap',
time: 41400,
@@ -427,38 +493,38 @@
type: 'tap',
time: 42200,
pitch: 0.2
}, {
- type: 'hold',
time: 43000,
pitch: 0.7,
- duration: 1200
- }, {
- type: 'glide',
- time: 44600,
- pitch: 0.3,
duration: 1200,
- glideTo: 0.8
- }, {
+ holdShape: 'diagonal',
+ holdShapeParams: {
+ slope: -0.13
+ }
+ }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({
+ type: 'hold'
+ }, "type", 'glide'), "time", 44600), "pitch", 0.3), "duration", 1200), "glideTo", 0.8), {
type: 'tap',
time: 46400,
pitch: 0.5
}, {
type: 'tap',
time: 47200,
pitch: 0.4
}, {
- type: 'hold',
time: 48000,
pitch: 0.6,
- duration: 1000
- }, {
- type: 'glide',
- time: 49400,
- pitch: 0.2,
duration: 1000,
- glideTo: 0.7
- },
+ holdShape: 'zigzag',
+ holdShapeParams: {
+ amplitude: 0.09,
+ frequency: 3.5,
+ phase: Math.PI / 7
+ }
+ }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({
+ type: 'hold'
+ }, "type", 'glide'), "time", 49400), "pitch", 0.2), "duration", 1000), "glideTo", 0.7),
// --- Final phrase (100-120s) ---
{
type: 'tap',
time: 51200,
@@ -467,38 +533,38 @@
type: 'tap',
time: 52000,
pitch: 0.3
}, {
- type: 'hold',
time: 52800,
pitch: 0.5,
- duration: 1200
- }, {
- type: 'glide',
- time: 54400,
- pitch: 0.7,
duration: 1200,
- glideTo: 0.2
- }, {
+ holdShape: 'curvy',
+ holdShapeParams: {
+ amplitude: 0.12,
+ frequency: 1.7,
+ phase: Math.PI / 8
+ }
+ }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({
+ type: 'hold'
+ }, "type", 'glide'), "time", 54400), "pitch", 0.7), "duration", 1200), "glideTo", 0.2), {
type: 'tap',
time: 56200,
pitch: 0.6
}, {
type: 'tap',
time: 57000,
pitch: 0.4
}, {
- type: 'hold',
time: 57800,
pitch: 0.2,
- duration: 1000
- }, {
- type: 'glide',
- time: 59200,
- pitch: 0.8,
duration: 1000,
- glideTo: 0.5
- },
+ holdShape: 'diagonal',
+ holdShapeParams: {
+ slope: 0.14
+ }
+ }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({
+ type: 'hold'
+ }, "type", 'glide'), "time", 59200), "pitch", 0.8), "duration", 1000), "glideTo", 0.5),
// --- Big finish (last 10s) ---
{
type: 'tap',
time: 61000,
@@ -507,31 +573,37 @@
type: 'tap',
time: 61800,
pitch: 0.7
}, {
- type: 'hold',
time: 62600,
pitch: 0.3,
- duration: 1200
- }, {
- type: 'glide',
- time: 64200,
- pitch: 0.6,
duration: 1200,
- glideTo: 0.1
- }, {
+ holdShape: 'zigzag',
+ holdShapeParams: {
+ amplitude: 0.13,
+ frequency: 2.8,
+ phase: Math.PI / 9
+ }
+ }, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({
+ type: 'hold'
+ }, "type", 'glide'), "time", 64200), "pitch", 0.6), "duration", 1200), "glideTo", 0.1), {
type: 'tap',
time: 66000,
pitch: 0.8
}, {
type: 'tap',
time: 66800,
pitch: 0.2
}, {
- type: 'hold',
time: 67600,
pitch: 0.4,
- duration: 1000
+ duration: 1000,
+ holdShape: 'curvy',
+ holdShapeParams: {
+ amplitude: 0.1,
+ frequency: 2.1,
+ phase: Math.PI / 10
+ }
}, {
type: 'glide',
time: 69000,
pitch: 0.7,
make a circle red. In-Game asset. 2d. High contrast. No shadows
give me black circle. In-Game asset. 2d. High contrast. No shadows
make a episode complete background for trambone game but without any text.. In-Game asset. 2d. High contrast. No shadows
make a backgroud shaped pear but dont do pear. In-Game asset. 2d. High contrast. No shadows
make a fully white eye like a just oval but without pupil. In-Game asset. 2d. High contrast. No shadows
nose. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
make a just face witout eyes and eyebrows and hairs and nose and mouth. In-Game asset. 2d. High contrast. No shadows
make a straight closed mouth. In-Game asset. 2d. High contrast. No shadows
make a laughed mouth. In-Game asset. 2d. High contrast. No shadows
make a red curtain but just curtain like real 3d. In-Game asset. High contrast. shadow
make a selection menu background but without any text.. In-Game asset. 2d. High contrast. No shadows
make a background like trambone champ game. In-Game asset. 2d. High contrast. No shadows
make a 2d trambone image. In-Game asset. 2d. High contrast