User prompt
remove the slide and slide_down_0 sounds from sound assets
User prompt
remove the slide and slide_down_0 from sound assets
User prompt
rebuild that slide sounds to 0 to 9 there has to be just 10 of them and rebuild it for game aswell remove the other slide sound just rename the new ones like slide0 to slide9
User prompt
rebuild that slide sounds to 0 to 9 there has to be just 10 of them and rebuild it for game aswell
User prompt
just write on game where is slide down0 or 1 or 2 or up0 or 2 or 4 for all to pitchbaractive
User prompt
create a 10 diffirent slide up or down asset for when it will take it take it
User prompt
the slide sound has no diffirent between moving up or down fix it add which library do u need
User prompt
when pitchbaractive goes down side the slide sound must be tough sound but if its getting upper make small voice
User prompt
Please fix the bug: 'pitchBarBg is not defined' in or related to this line: 'pitchBarBg.visible = false;' Line Number: 711
User prompt
remove the pitchbarbg from the game
User prompt
Please fix the bug: 'Uncaught ReferenceError: comboTxt is not defined' in or related to this line: 'comboTxt.visible = true;' Line Number: 638
User prompt
Please fix the bug: 'Uncaught ReferenceError: scoreTxt is not defined' in or related to this line: 'scoreTxt.visible = true;' Line Number: 637
User prompt
Please fix the bug: 'Uncaught ReferenceError: hitZone is not defined' in or related to this line: 'hitZone.visible = true;' Line Number: 636
User prompt
Please fix the bug: 'Uncaught ReferenceError: pitchBarBg is not defined' in or related to this line: 'pitchBarBg.visible = true;' Line Number: 634
User prompt
on screen we have to see just pitchbaractive and confetti holdbar and emotions face bar and note tap remove the others in assets and screen
User prompt
sometimes the slide sound plays 2 or 3 times at the same time make it fix
User prompt
when i dont click the screen stop the slide sound there is no delay for it
User prompt
but if i have still click the screen yhe slide sound is not replaying make sure
User prompt
when the slice sound ended but i ha ve still clicking the screen replay it but make sure when i dont hold the button stop it
User prompt
where is the end section of like point make it
User prompt
make that slide sound has diffirent pitch
User prompt
stop that slide sound when i dont click the screen
User prompt
pause that slide sound when i dont click the screen
User prompt
i can hear the slide sound 2 or 3 times a moment fix it with pause
User prompt
in catching when im clicking the button and holding play it but when i released to hold pause the playing
/****
* 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, sometimes diagonal, sometimes 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;
}
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
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
}, {
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
}, {
type: 'hold',
time: 8800,
pitch: 0.7,
duration: 1000
}, {
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
}, {
type: 'hold',
time: 13600,
pitch: 0.5,
duration: 1200
}, {
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
},
// --- Third phrase (40-60s) ---
{
type: 'tap',
time: 21800,
pitch: 0.5
}, {
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
}, {
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
},
// --- Fourth phrase (60-80s) ---
{
type: 'tap',
time: 31600,
pitch: 0.4
}, {
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
}, {
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
},
// --- Fifth phrase (80-100s) ---
{
type: 'tap',
time: 41400,
pitch: 0.6
}, {
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
}, {
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
},
// --- Final phrase (100-120s) ---
{
type: 'tap',
time: 51200,
pitch: 0.8
}, {
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
}, {
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
},
// --- Big finish (last 10s) ---
{
type: 'tap',
time: 61000,
pitch: 0.5
}, {
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
}, {
type: 'tap',
time: 66000,
pitch: 0.8
}, {
type: 'tap',
time: 66800,
pitch: 0.2
}, {
type: 'hold',
time: 67600,
pitch: 0.4,
duration: 1000
}, {
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";
}
// If direction changed, switch sound asset
if (pitchBarSoundType !== newSoundType || !pitchBarSound) {
// 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
var idx = 0;
if (pitchBarActive && typeof pitchBarActive.y === "number") {
// Map y to 0-9 (bottom to top)
var rel = (pitchBarActive.y - pitchBarY) / pitchBarHeight;
rel = Math.max(0, Math.min(1, rel));
idx = Math.floor((1 - rel) * 9.999); // 0 = bottom, 9 = top
}
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;
}
pitchBarActive.lastY = pitchBarActive.y;
// If the sound has ended but we're still holding, replay it
if (pitchBarSound && typeof pitchBarSound.isPlaying === "function" && pitchBarSoundId !== null) {
if (!pitchBarSound.isPlaying(pitchBarSoundId)) {
// 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
var minRate = 0.7;
var maxRate = 1.3;
var rate = minRate + (maxRate - minRate) * playerPitch;
pitchBarSoundId = pitchBarSound.play({
loop: true,
volume: slideVolume,
rate: rate
});
pitchBarSoundPlaying = true;
} else {
pitchBarSoundPlaying = true;
// Adjust volume in real time if possible
if (typeof pitchBarSound.setVolume === "function") {
pitchBarSound.setVolume(pitchBarSoundId, slideVolume);
}
}
} else if (pitchBarSound && !pitchBarSoundPlaying) {
// Start playing if not already
var minRate = 0.7;
var maxRate = 1.3;
var rate = minRate + (maxRate - minRate) * playerPitch;
// Stop any previous instance before playing to prevent overlap
if (typeof pitchBarSound.stop === "function" && pitchBarSoundId !== null) {
pitchBarSound.stop(pitchBarSoundId);
}
pitchBarSoundId = pitchBarSound.play({
loop: true,
volume: slideVolume,
rate: rate
});
pitchBarSoundPlaying = true;
}
// 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;
LK.getSound('slide').play();
}
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;
}
}
// 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;
if (tapAcc > 0.7) {
// 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;
LK.getSound('slide').play();
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;
}
} 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;
if (note.headCaught && holdAcc > 0.7) {
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;
LK.getSound('slide').play();
}
note.glideScore += 1;
note.glideTicks += 1;
note.noteAsset.scaleX = 1.2;
note.noteAsset.scaleY = 1.2;
} 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;
if (glideAcc > 0.7) {
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) {
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
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