User prompt
Arrojar un 10 % menos de proyectiles en los últimos dos niveles
Code edit (3 edits merged)
Please save this source code
User prompt
Bajar el texto del bridge la mitad de lo que se subio la última vez
User prompt
karina no debe solaparse con el diaper, el texto del bridge debe estar arriba 3 veces el alto del texto ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
tiene que sonar un asset llamado miraConan cuando el flyasset aparezca en escena, tambien debe sonar otro asset llamando success level cuando atraviese el bridge
User prompt
sigue estas instrucciones par el diaper: 8 segundos despues que javier desaparece, el diaper debe verse parpadeando inmovil al lado del coche despues de estar inmovil 3 segundos debe dejar de parpadear al dejar de parpadear debe caer y dejarse llevar por la ruta ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
la letra del bridge debe estar mas arriba, el diaper debe estar al lado del coche unos 3 segundos y luego caer ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
debe quedarse unos 3 segundos antes de dejarse caer, el bridge debe ser mas grande y alto ya que las columnas quedan dentro de la ruta y las letras deben estar en la parte superior del bridge ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
el diaper debe despegarse de la izquierda del coche, un 100% del ancho del diaper aprox, y debe mostrarse en frente del coche, ahora el coche lo tapa ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
el cartel de la ruta se ve muy petiso y los pilares estan dentro de la carretera, debe escalarse, y la frase nivel numero: frase, debe estar en la parte superior del cartel y dejando 100px de margen superior, el pañal sigue sin verse correctamente, puedes repensarlo por completo siguiendo las indicaciones que te he dado anteriormente ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (2 edits merged)
Please save this source code
User prompt
tambien debe estar mas arriba unos 50px ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
el pañal debe estar mas a laizquierda de la pantalla cuando parpadea, separada del truck unos 100 px ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
El problema es que no se alcanza a ver porque queda detrás del vehículo, debe estar más alejado del vehículo. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
El pañal lo puedes repensar totalmente al funcionamiento porque lo está arrojando directamente. La idea es que aparezca parpadeando al lado del vehículo y una vez que pasen dos segundos ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
El pañal debe ser lanzado con mas delay con respecto a la aparición de milei, para facilitar la jugabilidad debe aparecer a la izquierda del coche xon una separación de 30 y antes de caer al piso debe mostrarse parpadeando 2 segundos y al caer dejar de parpadear ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Las piedras deben ser menos frecuentes, por algun motivo en el nivel 4 he perdido y aun me quedaba energia puedes revisar eso?
User prompt
Please fix the bug: 'ReferenceError: levelTxt is not defined' in or related to this line: 'levelTxt.setText('Nivel: ' + currentLevel);' Line Number: 1116
Code edit (1 edits merged)
Please save this source code
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Bridge = Container.expand(function (levelNumber, levelName) { var self = Container.call(this); // Bridge structure - bigger and much higher var bridgeGraphics = self.attachAsset('bridge_structure', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1 }); // Level text on bridge var levelText = new Text2('NIVEL ' + levelNumber + ': ' + levelName, { size: 240, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0.5); levelText.x = 0; levelText.y = 0; self.addChild(levelText); self.active = true; self.soundPlayed = false; self.update = function () { if (self.active && gameStarted && motorcycle.speed > 0) { // Move bridge toward player like other road elements var speedFactor = 1 + 2.5 * Math.max(0.01, Math.min(1, 1 - Math.max(0, roadY - self.y) / horizonDistance)); self.y += motorcycle.speed * 3.2 * speedFactor; // Play sound when bridge is close enough if (!self.soundPlayed && self.y > roadY - 800) { var bridgeSound = LK.getSound('bridge_level'); if (bridgeSound && bridgeSound.play) { bridgeSound.play(); } self.soundPlayed = true; } // Remove when bridge passes if (self.y > roadY + 400) { self.destroy(); // Remove from bridges array for (var i = bridges.length - 1; i >= 0; i--) { if (bridges[i] === self) { bridges.splice(i, 1); break; } } } } }; return self; }); var DiaperItem = Container.expand(function () { var self = Container.call(this); var itemGraphics = self.attachAsset('diaper', { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = 0; self.velocityY = 0; self.gravity = 0; self.bounceCount = 0; self.maxBounces = 3; self.isOnGround = false; self.groundY = roadY - 120; // No tint - keep original diaper colors self.update = function () { self.x += self.velocityX; self.y += self.velocityY; // Apply gravity when in air - no bouncing self.velocityY += self.gravity; // Stop any blinking animations once diaper has gravity (is falling) if (self.gravity > 0) { tween.stop(self, { alpha: true }); self.alpha = 1; // Ensure it's fully visible when falling } // Move with road segments like other items when game is active if (gameStarted && motorcycle.speed > 0) { var speedFactor = 1 + 2.5 * Math.max(0.01, Math.min(1, 1 - Math.max(0, roadY - self.y) / horizonDistance)); self.y += motorcycle.speed * 3.2 * speedFactor; } }; return self; }); var FlyingAsset = Container.expand(function () { var self = Container.call(this); var flyingGraphics = self.attachAsset('flying_asset', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -8; // Moving from right to left self.update = function () { self.x += self.speed; // Remove when off screen to the left (exits screen) if (self.x < -200) { self.destroy(); // Remove from array and stop psychedelic effect for (var i = flyingAssets.length - 1; i >= 0; i--) { if (flyingAssets[i] === self) { flyingAssets.splice(i, 1); // Check if this was the last flying asset to stop psychedelic effect if (flyingAssets.length === 0) { // All flying assets are gone, stop psychedelic effect stopPsychedelicEffect(); } break; } } } }; return self; }); var HealingPill = Container.expand(function () { var self = Container.call(this); var pillGraphics = self.attachAsset('healing_pill', { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = 0; self.velocityY = 0; self.gravity = 0.15; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; self.velocityY += self.gravity; self.rotation += 0.1; }; return self; }); var Motorcycle = Container.expand(function () { var self = Container.call(this); var motorcycleGraphics = self.attachAsset('motorcycle', { anchorX: 0.5, anchorY: 0.5 }); self.health = 3; self.power = 100; self.speed = 0; self.maxSpeed = 3; self.x = 0; // Custom hitbox for motorcycle - top 30% of height and 15% narrower self.getHitbox = function () { var fullWidth = motorcycleGraphics.width * Math.abs(self.scaleX); var fullHeight = motorcycleGraphics.height * Math.abs(self.scaleY); var hitboxWidth = fullWidth * 0.85; // 15% narrower var hitboxHeight = fullHeight * 0.30; // Top 30% of height var hitboxOffsetY = -fullHeight * 0.35; // Offset upward to top portion return { x: self.x - hitboxWidth / 2, y: self.y + hitboxOffsetY - hitboxHeight / 2, width: hitboxWidth, height: hitboxHeight }; }; // Custom intersects method using the smaller hitbox self.intersectsProjectile = function (projectile) { var hitbox = self.getHitbox(); var projHalfWidth = (projectile.width || 50) * Math.abs(projectile.scaleX || 1) / 2; var projHalfHeight = (projectile.height || 50) * Math.abs(projectile.scaleY || 1) / 2; return projectile.x + projHalfWidth > hitbox.x && projectile.x - projHalfWidth < hitbox.x + hitbox.width && projectile.y + projHalfHeight > hitbox.y && projectile.y - projHalfHeight < hitbox.y + hitbox.height; }; // Custom hitbox for diaper collection - bottom part of motorcycle self.getDiaperHitbox = function () { var fullWidth = motorcycleGraphics.width * Math.abs(self.scaleX); var fullHeight = motorcycleGraphics.height * Math.abs(self.scaleY); var hitboxWidth = fullWidth * 0.8; // 80% of width var hitboxHeight = fullHeight * 0.25; // Bottom 25% of height var hitboxOffsetY = fullHeight * 0.35; // Offset downward to bottom portion return { x: self.x - hitboxWidth / 2, y: self.y + hitboxOffsetY - hitboxHeight / 2, width: hitboxWidth, height: hitboxHeight }; }; // Custom intersects method for diaper items using bottom hitbox self.intersectsDiaper = function (diaperItem) { var hitbox = self.getDiaperHitbox(); var diaperHalfWidth = (diaperItem.width || 80) * Math.abs(diaperItem.scaleX || 1) / 2; var diaperHalfHeight = (diaperItem.height || 66) * Math.abs(diaperItem.scaleY || 1) / 2; return diaperItem.x + diaperHalfWidth > hitbox.x && diaperItem.x - diaperHalfWidth < hitbox.x + hitbox.width && diaperItem.y + diaperHalfHeight > hitbox.y && diaperItem.y - diaperHalfHeight < hitbox.y + hitbox.height; }; self.update = function () { if (gameStarted) { if (self.speed < self.maxSpeed) { self.speed += 0.02; } roadOffset += self.speed; // Generate curves dynamically lastCurveChange++; if (lastCurveChange >= curveChangeInterval) { // Randomly decide if we should change curves if (Math.random() < 0.7) { // 70% chance to change curve var curveIntensity = 0.3 + Math.random() * 0.7; // Random intensity 0.3 to 1.0 var curveDirection = Math.random() < 0.5 ? -1 : 1; // Random direction targetCurve = curveDirection * curveIntensity; // Calculate target road center based on curve targetRoadCenterX = 1024 + targetCurve * 200; // Road can shift up to 200px left/right } else { // Straighten the road targetCurve = 0; targetRoadCenterX = 1024; } lastCurveChange = 0; curveChangeInterval = 120 + Math.random() * 240; // 2-6 seconds between changes } // Smoothly transition current curve toward target var curveDiff = targetCurve - currentCurve; currentCurve += curveDiff * curveTransitionSpeed; // Smoothly move road center var centerDiff = targetRoadCenterX - roadCenterX; roadCenterX += centerDiff * curveTransitionSpeed; // Apply subtle automatic road following to motorcycle var curveOffset = currentCurve * 200; // Same curve offset as road segments var roadCenterAtMotorcycle = roadCenterX + curveOffset; var targetMotorcycleX = roadCenterAtMotorcycle + (self.x - roadCenterAtMotorcycle) * 0.98; // Gradually pull toward road center // Only apply road following if not currently being dragged by user if (!isDragging) { var followStrength = 0.02; // How strongly motorcycle follows road var followDiff = targetMotorcycleX - self.x; self.x += followDiff * followStrength; } } }; self.takeDamage = function (damageAmount) { damageAmount = damageAmount || 3; self.power = Math.max(0, self.power - damageAmount); LK.effects.flashObject(self, 0xff0000, 500); // Track hits for scoring system hitsReceived++; storage.hitsReceived = hitsReceived; if (self.power <= 0) { LK.showGameOver(); } }; self.restorePower = function (amount) { amount = amount || 3; self.power = Math.min(100, self.power + amount); var powerSound = LK.getSound('power_up'); if (powerSound && powerSound.play) { powerSound.play(); } }; return self; }); var PowerItem = Container.expand(function () { var self = Container.call(this); var itemGraphics = self.attachAsset('power_item', { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = 0; self.velocityY = 0; self.gravity = 0.15; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; self.velocityY += self.gravity; }; return self; }); // isCheese representa queso, isLettuce representa lechuga var Projectile = Container.expand(function (isHeavy, isCheese, isLettuce) { var self = Container.call(this); self.isHeavy = !!isHeavy; self.isCheese = !!isCheese; // true si es queso self.isLettuce = !!isLettuce; // true si es lechuga var assetName; if (self.isCheese) { assetName = 'queso'; // asset para queso } else if (self.isLettuce) { assetName = 'lechuga'; // asset para lechuga } else if (self.isHeavy) { assetName = 'heavy_projectile'; } else { assetName = 'projectile'; } var projectileGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = 0; self.velocityY = 0; self.gravity = 0; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; self.velocityY += self.gravity; self.rotation += 0.1; }; return self; }); var RoadsideObject = Container.expand(function (type) { var self = Container.call(this); self.objectType = type || 'person'; var objectGraphics = self.attachAsset(self.objectType, { anchorX: 0.5, anchorY: 1.0 }); self.throwTimer = 0; // lateral offset base by type (trees further from road than people) self.sideOffsetBase = 120; if (type === 'tree') { self.sideOffsetBase = 400; } if (type === 'sign') { self.sideOffsetBase = 160; } // Configurar delay de lanzamiento por tipo - comenzar con delays mucho mayores var baseMin = 1600, // Doubled from 800 baseRand = 480; // Doubled from 240 if (type === 'person') { baseMin = 1680; // Doubled from 840 baseRand = 640; // Doubled from 320 } if (type === 'person2') { baseMin = 1440; // Doubled from 720 baseRand = 560; // Doubled from 280 } if (type === 'person3') { baseMin = 1200; // Doubled from 600 baseRand = 720; // Doubled from 360 } self.throwDelay = Math.random() * baseRand + baseMin; self.isLeftSide = true; self.originalY = 0; self.hasThrownThisCycle = false; self.minThrowDistanceFromMotorcycleFactor = 0.6; self.update = function () { if (self.objectType === 'person' || self.objectType === 'person2' || self.objectType === 'person3') { self.throwTimer++; if (self.throwTimer >= self.throwDelay && gameStarted && !self.hasThrownThisCycle) { // lanzar solo cuando el objeto este en el rango vertical permitido y respetando cooldown global if (self.y >= throwMinY && self.y <= throwMaxY && LK.ticks - lastProjectileThrowTick >= minFramesBetweenProjectileThrows) { self.throwObject(); lastProjectileThrowTick = LK.ticks; self.hasThrownThisCycle = true; self.throwTimer = 0; // Recalcular segfn tipo var nMin = 400, nRand = 120; if (self.objectType === 'person') { nMin = 420; nRand = 160; } if (self.objectType === 'person2') { nMin = 360; nRand = 140; } if (self.objectType === 'person3') { nMin = 300; nRand = 180; } self.throwDelay = Math.random() * nRand + nMin; } } } if (gameStarted && motorcycle.speed > 0) { // Enhanced movement with better perspective scaling var distBefore = Math.max(0, roadY - self.y); var perspBefore = Math.max(0.01, Math.min(1, 1 - distBefore / horizonDistance)); var speedFactor = 1 + 2.5 * perspBefore; // Slightly increased speed factor self.y += motorcycle.speed * 3.2 * speedFactor; // Increased base movement speed // Calculate perspective with extended range for smoother transitions var currentDistance = roadY - self.y; var perspective = 1 - currentDistance / horizonDistance; // Allow objects to appear much earlier (when perspective is negative) and scale properly if (perspective > -2.0) { // Extended range for earlier appearance // Calculate road width with minimum constraint for stability var roadWidth = 2048 * Math.max(0.005, perspective); var sideOffset = self.sideOffsetBase * Math.max(0.005, perspective); // Apply curve effect to roadside objects var curveOffset = currentCurve * (1 - perspective) * 300; var objectCenterX = roadCenterX + curveOffset; // Position objects with extended side margins // Additional margin for better visibility if (self.isLeftSide) { self.x = objectCenterX - roadWidth / 2 - sideOffset; } else { self.x = objectCenterX + roadWidth / 2 + sideOffset; } // Enhanced scaling that allows very small objects in distance var finalScale = Math.max(0.0005, perspective); // Even smaller minimum scale if (finalScale < 0.01) { finalScale = 0.01; } // Prevent invisible objects self.scaleX = (self.isLeftSide ? -1 : 1) * finalScale; self.scaleY = finalScale; } if (gameStarted && (self.objectType === 'person' || self.objectType === 'person2' || self.objectType === 'person3') && !self.hasThrownThisCycle) { // chequeo continuo: solo lanzar en el rango vertical permitido y respetando cooldown global if (self.y >= throwMinY && self.y <= throwMaxY && LK.ticks - lastProjectileThrowTick >= minFramesBetweenProjectileThrows) { self.throwObject(); lastProjectileThrowTick = LK.ticks; self.hasThrownThisCycle = true; self.throwTimer = 0; var nMin2 = 400, nRand2 = 120; if (self.objectType === 'person') { nMin2 = 420; nRand2 = 160; } if (self.objectType === 'person2') { nMin2 = 360; nRand2 = 140; } if (self.objectType === 'person3') { nMin2 = 300; nRand2 = 180; } self.throwDelay = Math.random() * nRand2 + nMin2; } } // Reset when completely off screen with much larger margin if (self.y > roadY + 1600 || perspective < -2.0) { // Extended thresholds self.y = self.originalY; var resetDistance = roadY - self.originalY; var resetPerspective = Math.max(0.005, Math.min(1, 1 - resetDistance / horizonDistance)); var resetRoadWidth = 2048 * resetPerspective; var resetSideOffset = self.sideOffsetBase * resetPerspective; self.scaleX = (self.isLeftSide ? -1 : 1) * resetPerspective; self.scaleY = resetPerspective; // Apply curve effect to reset position var resetCurveOffset = currentCurve * (1 - resetPerspective) * 300; var resetObjectCenterX = roadCenterX + resetCurveOffset; if (self.isLeftSide) { self.x = resetObjectCenterX - resetRoadWidth / 2 - resetSideOffset; } else { self.x = resetObjectCenterX + resetRoadWidth / 2 + resetSideOffset; } self.hasThrownThisCycle = false; } } }; self.throwObject = function () { // Start with fewer projectiles, increase with distance var currentDistance = Math.floor(roadOffset / 10); var distanceMilestone = Math.floor(currentDistance / 500); var maxProjectiles = 2 + Math.min(distanceMilestone, 6); // Start with 2, cap at 8 total if (projectiles.length >= maxProjectiles) { return; } // Heavy projectiles (piedra) start rare and increase frequency after 500m milestones var heavyChance = 0.02; // Start very low if (distanceMilestone > 0) { heavyChance = 0.05 + (distanceMilestone - 1) * 0.05; // Increase after first 500m heavyChance = Math.min(heavyChance, 0.2); // Cap at 20% } var isHeavy = Math.random() < heavyChance; var remainingChance = Math.random(); var isCheese = !isHeavy && remainingChance < 0.33; // 33% chance for cheese when not heavy var isLettuce = !isHeavy && !isCheese && remainingChance < 0.66; // 33% chance for lettuce when not heavy or cheese (which means broccoli gets the remaining 33%) var projectile = new Projectile(isHeavy, isCheese, isLettuce); projectile.x = self.x; projectile.y = self.y - 20; projectile.hasHitMotorcycle = false; // Reset collision flag for new projectile // Calculate how close motorcycle is to road edges var roadLeft = 200; var roadRight = 1848; var roadWidth = roadRight - roadLeft; var motorcycleDistanceFromLeft = motorcycle.x - roadLeft; var motorcycleDistanceFromRight = roadRight - motorcycle.x; var minDistanceToEdge = Math.min(motorcycleDistanceFromLeft, motorcycleDistanceFromRight); var edgeProximityFactor = Math.max(0, 1 - minDistanceToEdge / (roadWidth * 0.25)); // 0 at center, 1 at edges // Add inaccuracy based on edge proximity - more inaccuracy when closer to edges var baseInaccuracy = (Math.random() - 0.5) * 260; var edgeInaccuracy = (Math.random() - 0.5) * 400 * edgeProximityFactor; // Extra inaccuracy near edges var totalInaccuracy = baseInaccuracy + edgeInaccuracy; // Also add some leading/lagging based on motorcycle movement direction var leadingFactor = 0; if (motorcycle.lastX !== undefined) { var motorcycleDirection = motorcycle.x - motorcycle.lastX; leadingFactor = motorcycleDirection * (0.5 + edgeProximityFactor * 1.5); // More leading/lagging near edges } var targetX = motorcycle.x + totalInaccuracy + leadingFactor; // Reduce horizontal accuracy factor when near edges to make projectiles easier to dodge var baseHorizontalFactor = isHeavy ? 0.009 : 0.008; var edgeAccuracyReduction = 0.7 * edgeProximityFactor; // Reduce accuracy up to 70% near edges var horizontalFactor = baseHorizontalFactor * (1 - edgeAccuracyReduction); projectile.velocityX = (targetX - projectile.x) * horizontalFactor; var diffMultiplier = difficultyMultipliers[Math.min(currentLevel, maxLevel)]; var heightMultiplier = 2.0 - diffMultiplier.throwHeight; // Invert: easier levels (1.0) get higher arc (1.0), harder levels (1.4) get lower arc (0.6) var initialUpImpulse = isHeavy ? (-18 - Math.random() * 2) * heightMultiplier : (-20 - Math.random() * 3) * heightMultiplier; projectile.velocityY = initialUpImpulse; projectile.gravity = isHeavy ? 0.35 : 0.28; projectiles.push(projectile); game.addChild(projectile); var throwSound = LK.getSound('throw'); if (throwSound && throwSound.play) { throwSound.play(); } }; return self; }); var Truck = Container.expand(function () { var self = Container.call(this); var truckGraphics = self.attachAsset('truck', { anchorX: 0.5, anchorY: 0.5 }); // karina indicator above the truck before dropping power item var karinaSprite = self.attachAsset('karina', { anchorX: 0.5, anchorY: 1.0 }); var javierSprite = self.attachAsset('javier', { anchorX: 0.5, anchorY: 1.0 }); self.karinaBaseY = -450; self.karinaUpY = self.karinaBaseY - 30; karinaSprite.y = self.karinaBaseY; karinaSprite.scaleX = 3.0; karinaSprite.scaleY = 3.0; karinaSprite.visible = false; javierSprite.y = self.karinaBaseY; javierSprite.scaleX = 3.0; javierSprite.scaleY = 3.0; javierSprite.visible = false; self.speed = -6; self.active = true; self.dropTimer = 0; self.dropDelay = 120; self.nextIndicatorIsKarina = true; self.randomizeDropDelay = function () { var factor = 3 + Math.random() * 8; // 3x a 5x self.dropDelay = Math.floor(120 * factor); }; self.randomizeDropDelay(); self.targetY = horizonY + (roadY - horizonY) * 0.25; self.baseXFactorFromCenter = 0.42; self.oscAmp = 30; self.oscSpeed = 0.02; self.oscPhase = Math.random() * Math.PI * 2; self.smoothX = 0; self.curveDelayTimer = 0; self.inCurveDelay = false; self.karinaLead = 30; // frames before drop to show karina self.karinaAnimating = false; self.javierVisibleUntil = -1; self.update = function () { if (self.active) { if (self.y > self.targetY) { self.y += self.speed; if (self.y <= self.targetY) { self.y = self.targetY; } } else { self.y = self.targetY; } var currentDistance = roadY - self.y; var perspective = Math.max(0.01, Math.min(1, 1 - currentDistance / (roadY - horizonY))); var roadWidthAtY = 2048 * perspective; // Calculate current road center including curve effects var curveOffset = currentCurve * (1 - perspective) * 300; var roadCenterAtTruck = roadCenterX + curveOffset; // Smooth X movement with curve delays var targetX = roadCenterAtTruck; var curveStrength = Math.abs(currentCurve); // Add delay when entering curves if (curveStrength > 0.3 && !self.inCurveDelay) { self.inCurveDelay = true; self.curveDelayTimer = 20 + Math.random() * 30; // Brief delay } else if (curveStrength < 0.1) { self.inCurveDelay = false; self.curveDelayTimer = 0; } // Apply delay if (self.curveDelayTimer > 0) { self.curveDelayTimer--; } else { // Smooth movement toward target position var xDiff = targetX - self.x; self.x += xDiff * 0.08; // Smooth movement factor } var distanceToHorizon = roadY - horizonY; var currentDistance = roadY - self.y; var perspective = Math.max(0, 1 - currentDistance / distanceToHorizon); self.scaleX = perspective; self.scaleY = perspective; if (gameStarted) { self.dropTimer++; if (!karinaSprite.visible && !javierSprite.visible && self.dropTimer >= self.dropDelay - self.karinaLead) { var indicatorSprite = self.nextIndicatorIsKarina ? karinaSprite : javierSprite; indicatorSprite.visible = true; if (self.nextIndicatorIsKarina) { var s1 = LK.getSound('altacoimera'); if (s1) { s1.play(); } } else { var sE = LK.getSound('espert'); var sA = LK.getSound('afuera'); sE.play(); LK.setTimeout(function () { sA.play(); }, 500); self.javierVisibleUntil = LK.ticks + 120; } if (!self.karinaAnimating) { self.karinaAnimating = true; var upDur = Math.max(6, Math.floor(self.karinaLead * 0.4)); var downDur = Math.max(6, self.karinaLead - upDur); tween(indicatorSprite, { y: self.karinaUpY }, { duration: upDur, easing: tween.easeOut, onFinish: function onFinish() { tween(indicatorSprite, { y: self.karinaBaseY }, { duration: downDur, easing: tween.easeIn, onFinish: function onFinish() { self.karinaAnimating = false; } }); } }); } } if (self.dropTimer >= self.dropDelay) { if (self.nextIndicatorIsKarina) { self.dropPowerItem(); self.dropTimer = 0; karinaSprite.visible = false; javierSprite.visible = false; karinaSprite.y = self.karinaBaseY; javierSprite.y = self.karinaBaseY; self.karinaAnimating = false; self.randomizeDropDelay(); self.nextIndicatorIsKarina = !self.nextIndicatorIsKarina; } else { if (LK.ticks >= self.javierVisibleUntil) { // Javier throws healing pill self.throwHealingPill(); self.dropTimer = 0; karinaSprite.visible = false; javierSprite.visible = false; karinaSprite.y = self.karinaBaseY; javierSprite.y = self.karinaBaseY; self.karinaAnimating = false; self.randomizeDropDelay(); self.nextIndicatorIsKarina = !self.nextIndicatorIsKarina; } } } } } }; self.dropPowerItem = function () { var powerItem = new PowerItem(); // Start from karina sprite position (global position within truck) var karinaWorldX = self.x + karinaSprite.x * self.scaleX; var karinaWorldY = self.y + karinaSprite.y * self.scaleY; powerItem.x = karinaWorldX; powerItem.y = karinaWorldY; // choose random point on the road (not targeting the motorcycle) var roadLeft = 200; var roadRight = 1848; var margin = 60; var targetX = roadLeft + margin + Math.random() * Math.max(0, roadRight - roadLeft - margin * 2); // Parabola estilo proyectiles laterales var horizontalFactor = 0.007; powerItem.velocityX = (targetX - powerItem.x) * horizontalFactor; var isHighArc = Math.random() < 0.4; powerItem.velocityY = isHighArc ? -10 - Math.random() * 3 : -8 - Math.random() * 2; powerItem.gravity = 0.28; powerItems.push(powerItem); game.addChild(powerItem); }; self.throwHealingPill = function () { var healingPill = new HealingPill(); // Start from javier sprite position (global position within truck) var javierWorldX = self.x + javierSprite.x * self.scaleX; var javierWorldY = self.y + javierSprite.y * self.scaleY; healingPill.x = javierWorldX; healingPill.y = javierWorldY; // Target motorcycle position with some accuracy var targetX = motorcycle.x + (Math.random() - 0.5) * 200; // Some inaccuracy var horizontalFactor = 0.008; healingPill.velocityX = (targetX - healingPill.x) * horizontalFactor; healingPill.velocityY = -12 - Math.random() * 2; healingPill.gravity = 0.25; healingPills.push(healingPill); game.addChild(healingPill); // Play pill throw sound var pillSound = LK.getSound('pill_throw'); if (pillSound && pillSound.play) { pillSound.play(); } // Play sound and drop diaper item after longer delay LK.setTimeout(function () { var diaperSound = LK.getSound('diaper_drop'); if (diaperSound && diaperSound.play) { diaperSound.play(); } self.dropDiaperItem(); }, 2500); }; self.dropDiaperItem = function () { var diaperItem = new DiaperItem(); // Start from further left of truck window - more offset to the left side diaperItem.x = self.x - self.scaleX * 450; // Further left window position diaperItem.y = self.y - 50; // Slightly above truck center // Initially set no movement - diaper will blink in place diaperItem.velocityX = 0; diaperItem.velocityY = 0; diaperItem.gravity = 0; // Make it visible and properly scaled diaperItem.scaleX = 2.0; diaperItem.scaleY = 2.0; // Ensure no tint applied diaperItem.tint = 0xFFFFFF; // Blink for 1.5 seconds (1500ms) with 5 blink cycles tween(diaperItem, { alpha: 0 }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { tween(diaperItem, { alpha: 1 }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { tween(diaperItem, { alpha: 0 }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { tween(diaperItem, { alpha: 1 }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { tween(diaperItem, { alpha: 0 }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { tween(diaperItem, { alpha: 1 }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { tween(diaperItem, { alpha: 0 }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { tween(diaperItem, { alpha: 1 }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { tween(diaperItem, { alpha: 0 }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { tween(diaperItem, { alpha: 1 }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { // After blinking for 1.5 seconds, finally give it movement diaperItem.velocityX = (Math.random() - 0.5) * 4; // Small random horizontal velocity diaperItem.velocityY = 3 + Math.random() * 2; // Downward velocity diaperItem.gravity = 0.3; // Apply gravity } }); } }); } }); } }); } }); } }); } }); } }); } }); } }); diaperItems.push(diaperItem); // Add diaper after road segments but before motorcycle to ensure proper z-order var roadSegmentCount = roadSegments.length; var insertIndex = Math.min(roadSegmentCount + 1, game.children.length); game.addChildAt(diaperItem, insertIndex); }; self.createFlyingAsset = function () { var flyingAsset = new FlyingAsset(); // Start from right side of screen, in the sky flyingAsset.x = 2048 + 200; // Start off-screen right flyingAsset.y = 400 + Math.random() * 300; // Random height in sky flyingAsset.scaleX = 0.8 + Math.random() * 0.4; // Random scale flyingAsset.scaleY = flyingAsset.scaleX; flyingAssets.push(flyingAsset); game.addChild(flyingAsset); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87ceeb }); /**** * Game Code ****/ var roadY = 2732; // El horizonte debe estar en la parte superior del cesped // Cesped: y=2500, scaleY=3.45, height=500, anchorY=0.5 // Parte superior = y - (height * scaleY * anchorY) = 2500 - (500 * 3.45 * 0.5) = 2500 - 862.5 = 1637.5 var horizonY = 1638; var roadOffset = 0; // Curve system variables var currentCurve = 0; // Current road curvature (-1 to 1, left to right) var targetCurve = 0; // Target curvature to transition to var curveTransitionSpeed = 0.02; // How fast curves transition var roadCenterX = 1024; // Current center of the road var targetRoadCenterX = 1024; // Target center position var lastCurveChange = 0; // Timer for curve changes var curveChangeInterval = 180; // Frames between potential curve changes (3 seconds at 60fps) var motorcycleTilt = 0; // Current motorcycle tilt angle var maxTilt = 0.3; // Maximum tilt in radians var gameStarted = false; var gameCompleting = false; var bridges = []; var levelNames = { 1: 'HUIDA INICIAL', 2: 'CARRETERA PELIGROSA', 3: 'ZONA DE CONFLICTO', 4: 'TERRITORIO HOSTIL', 5: 'ESCAPE FINAL' }; // Psychedelic overlay system var psychedelicOverlay = null; var psychedelicActive = false; var psychedelicColors = [0x9370DB, 0x8A2BE2, 0x9966CC, 0xBA55D3, 0xDA70D6, 0xDDA0DD, 0xE6E6FA, 0xF0E68C, 0xEEE8AA, 0xD2B48C, 0xF5DEB3, 0xFFE4B5, 0xFFB6C1, 0xFFC0CB, 0x87CEEB, 0xADD8E6, 0xB0E0E6, 0xAFEEEE, 0x98FB98, 0x90EE90, 0xF0FFF0, 0xF5F5DC]; var phraseTimer = 0; var phraseInterval = 600; // 10 seconds at 60 FPS var phraseAssets = ['frase1', 'frase2', 'frase3', 'frase4', 'frase5', 'frase6', 'frase7']; // evitar repetir la misma frase consecutivamente var lastPhraseIndex = -1; var trucksCleared = false; // Level progression system var currentLevel = 1; var maxLevel = 5; var levelThresholdKm = 1000; // Each level requires 1000km var lastLevelCheck = 0; var hitsReceived = storage.hitsReceived || 0; var itemsCollected = storage.itemsCollected || 0; var levelsCompleted = storage.levelsCompleted || 0; // Level badge display var levelBadge = null; var levelBadgeTimer = 0; var levelBadgeDisplayTime = 180; // 3 seconds at 60fps // Difficulty scaling per level - max difficulty capped at level 4 var difficultyMultipliers = { 1: { throwFreq: 1.0, throwHeight: 1.0, score: 1.0 }, 2: { throwFreq: 1.1, throwHeight: 1.05, score: 1.3 }, 3: { throwFreq: 1.3, throwHeight: 1.15, score: 1.8 }, 4: { throwFreq: 1.6, throwHeight: 1.3, score: 2.5 }, 5: { throwFreq: 1.6, throwHeight: 1.3, score: 2.5 } }; var roadSegments = []; var roadLines = []; var leftSidewalks = []; var rightSidewalks = []; var totalSegments = 90; var horizonDistance = roadY - horizonY; var segmentSpacing = horizonDistance / totalSegments; var throwMinY = horizonY + horizonDistance * 0.35; var throwMaxY = horizonY + horizonDistance * 0.60; // cooldown global entre lanzamientos de proyectiles para espaciar var lastProjectileThrowTick = -10000; var minFramesBetweenProjectileThrows = 90; // Start with much higher cooldown, will decrease with distance // Agregar margen inferior para evitar que la ruta se corte var bottomMargin = 400; var extendedRoadY = roadY + bottomMargin; LK.playMusic('alta'); game.addChild(LK.getAsset('cesped', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 2500, scaleX: 5, scaleY: 3.45 })); game.addChild(LK.getAsset('skyline', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 739, scaleX: 21, scaleY: 18 })); for (var i = 0; i < totalSegments; i++) { var distance = i * segmentSpacing; var yPosition = horizonY + distance; var perspective = Math.max(0.01, distance / horizonDistance); var roadWidth = 2048 * perspective; var segmentHeight = segmentSpacing * 1.2 * perspective; if (yPosition <= roadY) { var roadSeg = game.addChild(LK.getAsset('road_segment', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: yPosition, scaleX: roadWidth / 100, scaleY: segmentSpacing / 5 * perspective })); roadSegments.push(roadSeg); } } var powerBarBg = LK.getAsset('power_bar_bg', { anchorX: 0.5, anchorY: 1.0, x: 600, y: -300 }); LK.gui.center.addChild(powerBarBg); var powerBarFill = LK.getAsset('power_bar_fill', { anchorX: 0.5, anchorY: 1.0, x: 600, y: -300 }); LK.gui.center.addChild(powerBarFill); var powerItems = []; var roadsideObjects = []; var projectiles = []; var healingPills = []; var flyingAssets = []; var diaperItems = []; // tipos base para objetos laterales var personVariants = ['person', 'person2', 'person3']; var otherVariants = ['tree', 'sign']; // cantidad por lado y distancia extendida para mayor espacio var objectsPerSide = 60; var extendedDistanceFactor = 6.4; // secuencias izquierda/derecha desfasadas para no coincidir al mismo tiempo var leftTypes = []; var rightTypes = []; for (var oi = 0; oi < objectsPerSide; oi++) { if (oi % 2 === 0) { leftTypes.push(personVariants[oi / 2 % personVariants.length | 0]); rightTypes.push(otherVariants[(oi / 2 + 1) % otherVariants.length | 0]); } else { leftTypes.push(otherVariants[(oi / 2 | 0) % otherVariants.length]); rightTypes.push(personVariants[(oi / 2 + 1) % personVariants.length | 0]); } } for (var i = 0; i < objectsPerSide; i++) { var leftType = leftTypes[i]; var leftObject = game.addChild(new RoadsideObject(leftType)); // mayor distancia y aparicion mas temprana var extendedDistance = horizonDistance * extendedDistanceFactor; var distance = i / objectsPerSide * extendedDistance; var yPos = Math.max(horizonY, extendedRoadY - distance); var perspective = 1 - Math.min(distance, horizonDistance) / horizonDistance; var roadWidth = 2048 * Math.max(0.005, perspective); // posicion izquierda leftObject.x = 1024 - roadWidth / 2 - 120 * Math.max(0.005, perspective); leftObject.y = yPos; leftObject.originalY = yPos; leftObject.scaleX = -Math.max(0.01, perspective); leftObject.scaleY = Math.max(0.01, perspective); leftObject.isLeftSide = true; roadsideObjects.push(leftObject); } for (var i = 0; i < objectsPerSide; i++) { var rightType = rightTypes[i]; var rightObject = game.addChild(new RoadsideObject(rightType)); // mayor distancia y escalon en Y para no coincidir con la izquierda var extendedDistance = horizonDistance * extendedDistanceFactor; var distance = (i + 0.5) / objectsPerSide * extendedDistance; var yPos = Math.max(horizonY, extendedRoadY - distance); var perspective = 1 - Math.min(distance, horizonDistance) / horizonDistance; var roadWidth = 2048 * Math.max(0.005, perspective); // posicion derecha rightObject.x = 1024 + roadWidth / 2 + 120 * Math.max(0.005, perspective); rightObject.y = yPos; rightObject.originalY = yPos; rightObject.scaleX = Math.max(0.01, perspective); rightObject.scaleY = Math.max(0.01, perspective); rightObject.isLeftSide = false; roadsideObjects.push(rightObject); } var scoreTxt = new Text2('Score: 0 | Metros: 0 | Nivel: 1', { size: 100, fill: 0x000000 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); function checkLevelProgression() { var currentDistance = Math.floor(roadOffset / 10); var targetKm = currentLevel * levelThresholdKm; if (currentDistance >= targetKm && currentLevel < maxLevel) { // Level completed - create bridge for new level currentLevel++; levelsCompleted++; storage.currentLevel = currentLevel; storage.levelsCompleted = levelsCompleted; // Create bridge for new level createLevelBridge(currentLevel); // Update level text levelTxt.setText('Nivel: ' + currentLevel); // Calculate and update score based on performance updateLevelScore(); } } function createLevelBridge(levelNumber) { var levelName = levelNames[levelNumber] || 'NUEVO NIVEL'; var bridge = new Bridge(levelNumber, levelName); // Position bridge in the distance, approaching the player bridge.x = roadCenterX; bridge.y = horizonY - 200; // Start in distance // Scale based on distance perspective var initialPerspective = 0.1; bridge.scaleX = initialPerspective; bridge.scaleY = initialPerspective; bridges.push(bridge); game.addChild(bridge); } function showLevelBadge(completedLevel) { // Show level splash instead of badge showLevelSplash(completedLevel); } function showFloatingScore(points, x, y) { // Create floating score text var floatingText = new Text2(points, { size: 120, fill: 0xFFD700 }); floatingText.anchor.set(0.5, 0.5); floatingText.x = x; floatingText.y = y; floatingText.alpha = 1.0; game.addChild(floatingText); // Animate floating text upward with fade out tween(floatingText, { y: y - 150, alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(floatingText); } }); } function updateLevelScore() { var currentDistance = Math.floor(roadOffset / 10); // Ensure currentLevel is valid and has a default value var validLevel = currentLevel || 1; if (validLevel < 1 || validLevel > maxLevel) { validLevel = 1; } var diffMultiplier = difficultyMultipliers[validLevel]; // Fallback to level 1 multipliers if lookup fails if (!diffMultiplier) { diffMultiplier = difficultyMultipliers[1]; } // Additional safety check - if still undefined, create default multiplier if (!diffMultiplier || typeof diffMultiplier.score === 'undefined') { diffMultiplier = { throwFreq: 1.0, throwHeight: 1.0, score: 1.0 }; } // Score based on distance, difficulty, and hits received var baseScore = currentDistance * diffMultiplier.score; var hitPenalty = hitsReceived * 10; var finalScore = Math.max(0, baseScore - hitPenalty); LK.setScore(Math.floor(finalScore)); } var healthTxt = new Text2('Power: 3', { size: 60, fill: 0xFF0000 }); healthTxt.anchor.set(0, 0); healthTxt.x = 0; healthTxt.y = 0; var motorcycle = game.addChild(new Motorcycle()); motorcycle.x = 800; motorcycle.y = roadY - 250; motorcycle.lastEdgeTick = -1000; LK.gui.topRight.addChild(healthTxt); var dragStartX = 0; var isDragging = false; // Splash setup var splash = LK.getAsset('splash', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, scaleX: 1, scaleY: 1 }); var splashActive = true; LK.gui.center.addChild(splash); game.down = function (x, y, obj) { if (splashActive) { var clickSnd = LK.getSound('cloaca'); if (clickSnd) { clickSnd.play(); } // Reset game stats for new game currentLevel = 1; hitsReceived = 0; itemsCollected = 0; storage.currentLevel = currentLevel; storage.hitsReceived = hitsReceived; storage.itemsCollected = itemsCollected; // dismiss splash and switch music LK.gui.center.removeChild(splash); splashActive = false; LK.stopMusic(); LK.playMusic('moto'); // Start playing gritos during gameplay LK.playMusic('gritos'); if (trucksCleared) { gameStarted = true; } return; } if (gameStarted) { isDragging = true; dragStartX = x; } }; game.move = function (x, y, obj) { if (splashActive) { return; } if (isDragging && gameStarted) { var deltaX = x - dragStartX; var newX = motorcycle.x + deltaX * 0.5; var hitEdge = false; // Calculate road bounds based on current curve position var curveOffset = currentCurve * 200; // Same curve offset as road segments var roadCenterAtMotorcycle = roadCenterX + curveOffset; var roadWidth = 1648; // Road width at motorcycle position var roadLeft = roadCenterAtMotorcycle - roadWidth / 2; var roadRight = roadCenterAtMotorcycle + roadWidth / 2; var edgeCooldownTicks = 30; if (newX < roadLeft) { newX = roadLeft; hitEdge = true; } if (newX > roadRight) { newX = roadRight; hitEdge = true; } if (newX !== motorcycle.x) { motorcycle.x = newX; } if (hitEdge && LK.ticks - motorcycle.lastEdgeTick > edgeCooldownTicks) { motorcycle.lastEdgeTick = LK.ticks; var edgeSnd = LK.getSound('edge'); if (edgeSnd && edgeSnd.play) { edgeSnd.play(); } } dragStartX = x; } }; game.up = function (x, y, obj) { if (splashActive) { return; } isDragging = false; }; // Function to start psychedelic overlay effect function startPsychedelicEffect() { if (!psychedelicActive) { psychedelicActive = true; // Create full-screen psychedelic overlay if (!psychedelicOverlay) { psychedelicOverlay = LK.getAsset('road_segment', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, scaleX: 100, scaleY: 100 }); psychedelicOverlay.alpha = 0.15; game.addChild(psychedelicOverlay); } } } // Global function to stop psychedelic effect function stopPsychedelicEffect() { psychedelicActive = false; // Remove overlay if (psychedelicOverlay) { psychedelicOverlay.destroy(); psychedelicOverlay = null; } // Reset all tints when psychedelic effect stops for (var p = 0; p < projectiles.length; p++) { projectiles[p].tint = 0xFFFFFF; // Reset projectile gravity to normal values } for (var r = 0; r < roadsideObjects.length; r++) { roadsideObjects[r].tint = 0xFFFFFF; } for (var rs = 0; rs < roadSegments.length; rs++) { roadSegments[rs].tint = 0xFFFFFF; } for (var pi = 0; pi < powerItems.length; pi++) { powerItems[pi].tint = 0xFFFFFF; } for (var t = 0; t < trucks.length; t++) { trucks[t].tint = 0xFFFFFF; } for (var di = 0; di < diaperItems.length; di++) { diaperItems[di].tint = 0xFFFFFF; } for (var hi = 0; hi < healingPills.length; hi++) { healingPills[hi].tint = 0xFFFFFF; } for (var b = 0; b < bridges.length; b++) { bridges[b].tint = 0xFFFFFF; } // Reset motorcycle tint to white motorcycle.tint = 0xFFFFFF; } var trucks = []; var truck1 = game.addChild(new Truck()); truck1.x = 1024; truck1.y = roadY - 600; trucks.push(truck1); game.update = function () { if (!trucksCleared) { var allTrucksCleared = true; for (var i = 0; i < trucks.length; i++) { if (trucks[i].y > roadY - 300) { allTrucksCleared = false; break; } } if (allTrucksCleared) { trucksCleared = true; if (!splashActive) { gameStarted = true; LK.stopMusic(); LK.playMusic('moto'); // Start playing gritos during gameplay LK.playMusic('gritos'); } } } if (gameStarted && motorcycle.speed > 0) { var movementFactor = 3; var offset = roadOffset * movementFactor % horizonDistance; for (var i = 0; i < roadSegments.length; i++) { var distanceAlong = (i * segmentSpacing + offset) % horizonDistance; var yPosition = horizonY + distanceAlong; var perspective = Math.max(0.01, Math.min(1.08, distanceAlong / horizonDistance)); var roadWidth = 2048 * perspective; // Apply curve effect to road segments var curveOffset = currentCurve * (1 - perspective) * 300; // Stronger curve effect in distance var segmentCenterX = roadCenterX + curveOffset; roadSegments[i].x = segmentCenterX; roadSegments[i].y = yPosition; roadSegments[i].scaleX = roadWidth / 100; roadSegments[i].scaleY = horizonDistance / totalSegments / 5 * perspective; // Add slight rotation to road segments for banking effect roadSegments[i].rotation = currentCurve * 0.02 * (1 - perspective); } for (var k = 0; k < leftSidewalks.length; k++) { var swDistance = (k * segmentSpacing + offset) % horizonDistance; var swY = roadY - swDistance; var swPerspective = Math.max(0.01, Math.min(1.08, 1 - swDistance / horizonDistance)); var swRoadWidth = 2048 * swPerspective; var segmentHeight = horizonDistance / totalSegments * 1.2 * swPerspective; leftSidewalks[k].y = swY; rightSidewalks[k].y = swY; leftSidewalks[k].x = 1024 - swRoadWidth / 2 - 80 * swPerspective; leftSidewalks[k].scaleX = swPerspective * 2; leftSidewalks[k].scaleY = segmentHeight / 40; rightSidewalks[k].x = 1024 + swRoadWidth / 2 + 80 * swPerspective; rightSidewalks[k].scaleX = swPerspective * 2; rightSidewalks[k].scaleY = segmentHeight / 40; } } if (gameStarted) { var distance = Math.floor(roadOffset / 10); scoreTxt.setText('Score: ' + LK.getScore() + ' | Metros: ' + distance + ' | Nivel: ' + currentLevel); } healthTxt.setText('Power: ' + Math.round(motorcycle.power) + '%'); if (gameStarted) { if (motorcycle.lastX === undefined) { motorcycle.lastX = motorcycle.x; motorcycle.isFlipping = false; } // Calculate motorcycle banking/tilt based on movement and curve var movementDelta = motorcycle.x - motorcycle.lastX; var curveTilt = currentCurve * 0.15; // Road curve affects tilt var movementTilt = movementDelta * 0.008; // Movement affects tilt var targetTilt = curveTilt + movementTilt; // Clamp tilt to maximum values targetTilt = Math.max(-maxTilt, Math.min(maxTilt, targetTilt)); // Smoothly transition motorcycle tilt var tiltDiff = targetTilt - motorcycleTilt; motorcycleTilt += tiltDiff * 0.1; motorcycle.rotation = motorcycleTilt; var screenCenter = 1024; var crossedToRight = motorcycle.lastX < screenCenter && motorcycle.x >= screenCenter; var crossedToLeft = motorcycle.lastX > screenCenter && motorcycle.x <= screenCenter; if ((crossedToRight || crossedToLeft) && !motorcycle.isFlipping) { motorcycle.isFlipping = true; tween(motorcycle, { scaleX: -motorcycle.scaleX }, { duration: 0, easing: tween.easeOut, onFinish: function onFinish() { motorcycle.isFlipping = false; } }); } motorcycle.lastX = motorcycle.x; } var powerPercentage = motorcycle.power / 100; powerBarFill.scaleY = powerPercentage; if (powerPercentage > 0.6) { powerBarFill.tint = 0x99ff99; } else if (powerPercentage > 0.3) { powerBarFill.tint = 0xffff99; } else { powerBarFill.tint = 0xff9999; } for (var i = projectiles.length - 1; i >= 0; i--) { var projectile = projectiles[i]; if (projectile.x < -100 || projectile.x > 2148 || projectile.y > extendedRoadY + 100) { projectile.destroy(); projectiles.splice(i, 1); continue; } if (gameStarted && motorcycle.intersectsProjectile(projectile) && !projectile.hasHitMotorcycle) { var damageAmount = projectile.isHeavy ? 15 : 3; motorcycle.takeDamage(damageAmount); var impactSnd; if (projectile.isHeavy) { impactSnd = LK.getSound('piedra'); } else if (projectile.isCheese) { var hitSnd = LK.getSound('hit'); if (hitSnd && hitSnd.play) { hitSnd.play(); } impactSnd = LK.getSound('queso'); } else { impactSnd = LK.getSound('hit'); } if (impactSnd && impactSnd.play) { impactSnd.play(); } // Bounce projectile away from motorcycle var bounceForceX = (projectile.x - motorcycle.x) * 0.3 * 0.2; // 20% of current force var bounceForceY = (-Math.abs(projectile.velocityY) * 0.8 - 5) * 0.2; // 20% of current force projectile.velocityX = bounceForceX; projectile.velocityY = bounceForceY; projectile.gravity = 0.8; // Increased gravity after hitting motorcycle projectile.hasHitMotorcycle = true; // Mark as having hit motorcycle continue; } if (projectile.y >= extendedRoadY - 20 && projectile.velocityY > 0) { projectile.destroy(); projectiles.splice(i, 1); } } for (var i = powerItems.length - 1; i >= 0; i--) { var powerItem = powerItems[i]; if (powerItem.x < -100 || powerItem.x > 2148 || powerItem.y > extendedRoadY + 100) { powerItem.destroy(); powerItems.splice(i, 1); continue; } if (gameStarted && powerItem.intersects(motorcycle)) { motorcycle.restorePower(3); itemsCollected++; storage.itemsCollected = itemsCollected; // Add 97 points and show floating score LK.setScore(LK.getScore() + 97); showFloatingScore('+97 pts.', powerItem.x, powerItem.y); powerItem.destroy(); powerItems.splice(i, 1); continue; } if (powerItem.y >= extendedRoadY - 20 && powerItem.velocityY > 0) { powerItem.destroy(); powerItems.splice(i, 1); } } // Handle healing pills for (var i = healingPills.length - 1; i >= 0; i--) { var healingPill = healingPills[i]; if (healingPill.x < -100 || healingPill.x > 2148 || healingPill.y > extendedRoadY + 100) { healingPill.destroy(); healingPills.splice(i, 1); continue; } if (gameStarted && healingPill.intersects(motorcycle)) { // Recover 12% of energy motorcycle.restorePower(12); itemsCollected++; storage.itemsCollected = itemsCollected; // Add 100 points and show floating score LK.setScore(LK.getScore() + 100); showFloatingScore('+100 pts.', healingPill.x, healingPill.y); // Create flying asset in sky when pill is collected var flyingAsset = new FlyingAsset(); flyingAsset.x = 2048 + 200; // Start off-screen right flyingAsset.y = 400 + Math.random() * 300; // Random height in sky flyingAsset.scaleX = 0.8 + Math.random() * 0.4; // Random scale flyingAsset.scaleY = flyingAsset.scaleX; flyingAssets.push(flyingAsset); game.addChild(flyingAsset); // Start psychedelic effect when flying asset is created startPsychedelicEffect(); healingPill.destroy(); healingPills.splice(i, 1); continue; } if (healingPill.y >= extendedRoadY - 20 && healingPill.velocityY > 0) { healingPill.destroy(); healingPills.splice(i, 1); } } // Handle diaper items for (var i = diaperItems.length - 1; i >= 0; i--) { var diaperItem = diaperItems[i]; // Remove when diaper exits screen (like road segments) if (diaperItem.x < -200 || diaperItem.x > 2248 || diaperItem.y > extendedRoadY + 200) { diaperItem.destroy(); diaperItems.splice(i, 1); continue; } if (gameStarted && motorcycle.intersectsDiaper(diaperItem)) { // Subtract 100 points and show floating score LK.setScore(Math.max(0, LK.getScore() - 100)); showFloatingScore('-100 pts.', diaperItem.x, diaperItem.y); diaperItem.destroy(); diaperItems.splice(i, 1); continue; } } // Random phrase system - play one of six phrases every 10 seconds if (gameStarted) { phraseTimer++; if (phraseTimer >= phraseInterval) { // seleccionar una frase diferente de la ultima var randomPhraseIndex = Math.floor(Math.random() * phraseAssets.length); if (phraseAssets.length > 1 && randomPhraseIndex === lastPhraseIndex) { // reintentar una vez para evitar repeticion inmediata randomPhraseIndex = (randomPhraseIndex + 1 + Math.floor(Math.random() * (phraseAssets.length - 1))) % phraseAssets.length; } var phraseSoundId = phraseAssets[randomPhraseIndex]; var phraseSound = LK.getSound(phraseSoundId); if (phraseSound && phraseSound.play) { phraseSound.play(); } lastPhraseIndex = randomPhraseIndex; phraseTimer = 0; // reset timer } } // Check level progression if (gameStarted) { checkLevelProgression(); updateLevelScore(); } // Check for game completion (level 5 completed and distance > 5000m) var currentDistance = Math.floor(roadOffset / 10); if (gameStarted && currentLevel >= maxLevel && currentDistance > 5000 && !gameCompleting) { gameCompleting = true; // Move truck and motorcycle to horizon tween(motorcycle, { y: horizonY + 50, scaleX: 0.1, scaleY: 0.1 }, { duration: 3000, easing: tween.easeInOut }); for (var t = 0; t < trucks.length; t++) { tween(trucks[t], { y: horizonY, scaleX: 0.05, scaleY: 0.05 }, { duration: 3500, easing: tween.easeInOut, onFinish: function onFinish() { // Show you win after vehicles reach horizon LK.setTimeout(function () { LK.showYouWin(); }, 1000); } }); } } // Handle bridges - update perspective scaling as they approach for (var b = 0; b < bridges.length; b++) { var bridge = bridges[b]; var currentDistance = roadY - bridge.y; var perspective = Math.max(0.01, Math.min(1.2, 1 - currentDistance / horizonDistance)); // Apply curve effect to bridge position var curveOffset = currentCurve * (1 - perspective) * 300; bridge.x = roadCenterX + curveOffset; // Scale bridge based on perspective bridge.scaleX = perspective; bridge.scaleY = perspective; // Ensure bridge spans the road width var roadWidth = 2048 * perspective; if (bridge.scaleX < roadWidth / 2048) { bridge.scaleX = roadWidth / 2048; } } // Handle psychedelic overlay effect if (psychedelicActive && psychedelicOverlay && flyingAssets.length > 0) { // Slower color cycling for sedating psychedelic effect var slowTicks = Math.floor(LK.ticks / 20); // Much slower cycling var overlayColor = psychedelicColors[slowTicks % psychedelicColors.length]; psychedelicOverlay.tint = overlayColor; // Apply color cycling to all game objects including motorcycle (Rita) var mediumTicks = Math.floor(LK.ticks / 15); // Slower cycling for (var p = 0; p < projectiles.length; p++) { var projColor = psychedelicColors[(mediumTicks + p * 3) % psychedelicColors.length]; projectiles[p].tint = projColor; } for (var r = 0; r < roadsideObjects.length; r++) { if (roadsideObjects[r].y > horizonY && roadsideObjects[r].y < roadY) { var objColor = psychedelicColors[(mediumTicks + r * 5) % psychedelicColors.length]; roadsideObjects[r].tint = objColor; } } for (var rs = 0; rs < roadSegments.length; rs++) { var roadColor = psychedelicColors[(mediumTicks + rs * 2) % psychedelicColors.length]; roadSegments[rs].tint = roadColor; } for (var pi = 0; pi < powerItems.length; pi++) { var powerColor = psychedelicColors[(mediumTicks + pi * 6) % psychedelicColors.length]; powerItems[pi].tint = powerColor; } for (var t = 0; t < trucks.length; t++) { var truckColor = psychedelicColors[(mediumTicks + t * 4) % psychedelicColors.length]; trucks[t].tint = truckColor; } for (var di = 0; di < diaperItems.length; di++) { var diaperColor = psychedelicColors[(mediumTicks + di * 7) % psychedelicColors.length]; diaperItems[di].tint = diaperColor; } for (var hi = 0; hi < healingPills.length; hi++) { var pillColor = psychedelicColors[(mediumTicks + hi * 8) % psychedelicColors.length]; healingPills[hi].tint = pillColor; } for (var b = 0; b < bridges.length; b++) { var bridgeColor = psychedelicColors[(mediumTicks + b * 9) % psychedelicColors.length]; bridges[b].tint = bridgeColor; } // Keep flying assets at original color (no tint) to make them stand out for (var f = 0; f < flyingAssets.length; f++) { flyingAssets[f].tint = 0xFFFFFF; } // Apply rainbow colors to motorcycle (Rita) during psychedelic effect var motorcycleColor = psychedelicColors[(mediumTicks + 10) % psychedelicColors.length]; motorcycle.tint = motorcycleColor; } // Handle level badge display timer if (levelBadge && levelBadgeTimer > 0) { levelBadgeTimer--; if (levelBadgeTimer <= 0) { tween(levelBadge, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 500, easing: tween.easeIn, onFinish: function onFinish() { if (levelBadge) { LK.gui.center.removeChild(levelBadge); levelBadge = null; } } }); } } if (gameStarted && LK.ticks % 600 == 0) { // Calculate current distance in meters var currentDistance = Math.floor(roadOffset / 10); // Apply level-based difficulty multiplier to throwing frequency var diffMultiplier = difficultyMultipliers[Math.min(currentLevel, maxLevel)]; var levelFreqMultiplier = diffMultiplier.throwFreq; // Only increase throwing frequency every 500 meters, scaled by level var distanceMilestone = Math.floor(currentDistance / 500); // Gradually decrease global cooldown with distance and level if (distanceMilestone > 0) { var newCooldown = Math.floor((90 - distanceMilestone * 8) / levelFreqMultiplier); minFramesBetweenProjectileThrows = Math.max(15, newCooldown); // Lower minimum for higher levels } // Start with fewer projectiles and gradually increase, scaled by level var baseDecrease = Math.min(distanceMilestone * 3 * levelFreqMultiplier, 35); // Higher cap with level scaling for (var j = 0; j < roadsideObjects.length; j++) { if (roadsideObjects[j].objectType === 'person' || roadsideObjects[j].objectType === 'person2' || roadsideObjects[j].objectType === 'person3') { var dec = Math.floor((2 + baseDecrease) * levelFreqMultiplier); // Level-scaled decrease if (roadsideObjects[j].objectType === 'person3') { dec = Math.floor((3 + baseDecrease) * levelFreqMultiplier); } if (roadsideObjects[j].objectType === 'person2') { dec = Math.floor((2 + baseDecrease) * levelFreqMultiplier); } roadsideObjects[j].throwDelay = Math.max(20, roadsideObjects[j].throwDelay - dec); // Lower minimum delay for higher levels } } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Bridge = Container.expand(function (levelNumber, levelName) {
var self = Container.call(this);
// Bridge structure - bigger and much higher
var bridgeGraphics = self.attachAsset('bridge_structure', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1
});
// Level text on bridge
var levelText = new Text2('NIVEL ' + levelNumber + ': ' + levelName, {
size: 240,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = 0;
levelText.y = 0;
self.addChild(levelText);
self.active = true;
self.soundPlayed = false;
self.update = function () {
if (self.active && gameStarted && motorcycle.speed > 0) {
// Move bridge toward player like other road elements
var speedFactor = 1 + 2.5 * Math.max(0.01, Math.min(1, 1 - Math.max(0, roadY - self.y) / horizonDistance));
self.y += motorcycle.speed * 3.2 * speedFactor;
// Play sound when bridge is close enough
if (!self.soundPlayed && self.y > roadY - 800) {
var bridgeSound = LK.getSound('bridge_level');
if (bridgeSound && bridgeSound.play) {
bridgeSound.play();
}
self.soundPlayed = true;
}
// Remove when bridge passes
if (self.y > roadY + 400) {
self.destroy();
// Remove from bridges array
for (var i = bridges.length - 1; i >= 0; i--) {
if (bridges[i] === self) {
bridges.splice(i, 1);
break;
}
}
}
}
};
return self;
});
var DiaperItem = Container.expand(function () {
var self = Container.call(this);
var itemGraphics = self.attachAsset('diaper', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.gravity = 0;
self.bounceCount = 0;
self.maxBounces = 3;
self.isOnGround = false;
self.groundY = roadY - 120;
// No tint - keep original diaper colors
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Apply gravity when in air - no bouncing
self.velocityY += self.gravity;
// Stop any blinking animations once diaper has gravity (is falling)
if (self.gravity > 0) {
tween.stop(self, {
alpha: true
});
self.alpha = 1; // Ensure it's fully visible when falling
}
// Move with road segments like other items when game is active
if (gameStarted && motorcycle.speed > 0) {
var speedFactor = 1 + 2.5 * Math.max(0.01, Math.min(1, 1 - Math.max(0, roadY - self.y) / horizonDistance));
self.y += motorcycle.speed * 3.2 * speedFactor;
}
};
return self;
});
var FlyingAsset = Container.expand(function () {
var self = Container.call(this);
var flyingGraphics = self.attachAsset('flying_asset', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -8; // Moving from right to left
self.update = function () {
self.x += self.speed;
// Remove when off screen to the left (exits screen)
if (self.x < -200) {
self.destroy();
// Remove from array and stop psychedelic effect
for (var i = flyingAssets.length - 1; i >= 0; i--) {
if (flyingAssets[i] === self) {
flyingAssets.splice(i, 1);
// Check if this was the last flying asset to stop psychedelic effect
if (flyingAssets.length === 0) {
// All flying assets are gone, stop psychedelic effect
stopPsychedelicEffect();
}
break;
}
}
}
};
return self;
});
var HealingPill = Container.expand(function () {
var self = Container.call(this);
var pillGraphics = self.attachAsset('healing_pill', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.gravity = 0.15;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += self.gravity;
self.rotation += 0.1;
};
return self;
});
var Motorcycle = Container.expand(function () {
var self = Container.call(this);
var motorcycleGraphics = self.attachAsset('motorcycle', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 3;
self.power = 100;
self.speed = 0;
self.maxSpeed = 3;
self.x = 0;
// Custom hitbox for motorcycle - top 30% of height and 15% narrower
self.getHitbox = function () {
var fullWidth = motorcycleGraphics.width * Math.abs(self.scaleX);
var fullHeight = motorcycleGraphics.height * Math.abs(self.scaleY);
var hitboxWidth = fullWidth * 0.85; // 15% narrower
var hitboxHeight = fullHeight * 0.30; // Top 30% of height
var hitboxOffsetY = -fullHeight * 0.35; // Offset upward to top portion
return {
x: self.x - hitboxWidth / 2,
y: self.y + hitboxOffsetY - hitboxHeight / 2,
width: hitboxWidth,
height: hitboxHeight
};
};
// Custom intersects method using the smaller hitbox
self.intersectsProjectile = function (projectile) {
var hitbox = self.getHitbox();
var projHalfWidth = (projectile.width || 50) * Math.abs(projectile.scaleX || 1) / 2;
var projHalfHeight = (projectile.height || 50) * Math.abs(projectile.scaleY || 1) / 2;
return projectile.x + projHalfWidth > hitbox.x && projectile.x - projHalfWidth < hitbox.x + hitbox.width && projectile.y + projHalfHeight > hitbox.y && projectile.y - projHalfHeight < hitbox.y + hitbox.height;
};
// Custom hitbox for diaper collection - bottom part of motorcycle
self.getDiaperHitbox = function () {
var fullWidth = motorcycleGraphics.width * Math.abs(self.scaleX);
var fullHeight = motorcycleGraphics.height * Math.abs(self.scaleY);
var hitboxWidth = fullWidth * 0.8; // 80% of width
var hitboxHeight = fullHeight * 0.25; // Bottom 25% of height
var hitboxOffsetY = fullHeight * 0.35; // Offset downward to bottom portion
return {
x: self.x - hitboxWidth / 2,
y: self.y + hitboxOffsetY - hitboxHeight / 2,
width: hitboxWidth,
height: hitboxHeight
};
};
// Custom intersects method for diaper items using bottom hitbox
self.intersectsDiaper = function (diaperItem) {
var hitbox = self.getDiaperHitbox();
var diaperHalfWidth = (diaperItem.width || 80) * Math.abs(diaperItem.scaleX || 1) / 2;
var diaperHalfHeight = (diaperItem.height || 66) * Math.abs(diaperItem.scaleY || 1) / 2;
return diaperItem.x + diaperHalfWidth > hitbox.x && diaperItem.x - diaperHalfWidth < hitbox.x + hitbox.width && diaperItem.y + diaperHalfHeight > hitbox.y && diaperItem.y - diaperHalfHeight < hitbox.y + hitbox.height;
};
self.update = function () {
if (gameStarted) {
if (self.speed < self.maxSpeed) {
self.speed += 0.02;
}
roadOffset += self.speed;
// Generate curves dynamically
lastCurveChange++;
if (lastCurveChange >= curveChangeInterval) {
// Randomly decide if we should change curves
if (Math.random() < 0.7) {
// 70% chance to change curve
var curveIntensity = 0.3 + Math.random() * 0.7; // Random intensity 0.3 to 1.0
var curveDirection = Math.random() < 0.5 ? -1 : 1; // Random direction
targetCurve = curveDirection * curveIntensity;
// Calculate target road center based on curve
targetRoadCenterX = 1024 + targetCurve * 200; // Road can shift up to 200px left/right
} else {
// Straighten the road
targetCurve = 0;
targetRoadCenterX = 1024;
}
lastCurveChange = 0;
curveChangeInterval = 120 + Math.random() * 240; // 2-6 seconds between changes
}
// Smoothly transition current curve toward target
var curveDiff = targetCurve - currentCurve;
currentCurve += curveDiff * curveTransitionSpeed;
// Smoothly move road center
var centerDiff = targetRoadCenterX - roadCenterX;
roadCenterX += centerDiff * curveTransitionSpeed;
// Apply subtle automatic road following to motorcycle
var curveOffset = currentCurve * 200; // Same curve offset as road segments
var roadCenterAtMotorcycle = roadCenterX + curveOffset;
var targetMotorcycleX = roadCenterAtMotorcycle + (self.x - roadCenterAtMotorcycle) * 0.98; // Gradually pull toward road center
// Only apply road following if not currently being dragged by user
if (!isDragging) {
var followStrength = 0.02; // How strongly motorcycle follows road
var followDiff = targetMotorcycleX - self.x;
self.x += followDiff * followStrength;
}
}
};
self.takeDamage = function (damageAmount) {
damageAmount = damageAmount || 3;
self.power = Math.max(0, self.power - damageAmount);
LK.effects.flashObject(self, 0xff0000, 500);
// Track hits for scoring system
hitsReceived++;
storage.hitsReceived = hitsReceived;
if (self.power <= 0) {
LK.showGameOver();
}
};
self.restorePower = function (amount) {
amount = amount || 3;
self.power = Math.min(100, self.power + amount);
var powerSound = LK.getSound('power_up');
if (powerSound && powerSound.play) {
powerSound.play();
}
};
return self;
});
var PowerItem = Container.expand(function () {
var self = Container.call(this);
var itemGraphics = self.attachAsset('power_item', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.gravity = 0.15;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += self.gravity;
};
return self;
});
// isCheese representa queso, isLettuce representa lechuga
var Projectile = Container.expand(function (isHeavy, isCheese, isLettuce) {
var self = Container.call(this);
self.isHeavy = !!isHeavy;
self.isCheese = !!isCheese; // true si es queso
self.isLettuce = !!isLettuce; // true si es lechuga
var assetName;
if (self.isCheese) {
assetName = 'queso'; // asset para queso
} else if (self.isLettuce) {
assetName = 'lechuga'; // asset para lechuga
} else if (self.isHeavy) {
assetName = 'heavy_projectile';
} else {
assetName = 'projectile';
}
var projectileGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.gravity = 0;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += self.gravity;
self.rotation += 0.1;
};
return self;
});
var RoadsideObject = Container.expand(function (type) {
var self = Container.call(this);
self.objectType = type || 'person';
var objectGraphics = self.attachAsset(self.objectType, {
anchorX: 0.5,
anchorY: 1.0
});
self.throwTimer = 0;
// lateral offset base by type (trees further from road than people)
self.sideOffsetBase = 120;
if (type === 'tree') {
self.sideOffsetBase = 400;
}
if (type === 'sign') {
self.sideOffsetBase = 160;
}
// Configurar delay de lanzamiento por tipo - comenzar con delays mucho mayores
var baseMin = 1600,
// Doubled from 800
baseRand = 480; // Doubled from 240
if (type === 'person') {
baseMin = 1680; // Doubled from 840
baseRand = 640; // Doubled from 320
}
if (type === 'person2') {
baseMin = 1440; // Doubled from 720
baseRand = 560; // Doubled from 280
}
if (type === 'person3') {
baseMin = 1200; // Doubled from 600
baseRand = 720; // Doubled from 360
}
self.throwDelay = Math.random() * baseRand + baseMin;
self.isLeftSide = true;
self.originalY = 0;
self.hasThrownThisCycle = false;
self.minThrowDistanceFromMotorcycleFactor = 0.6;
self.update = function () {
if (self.objectType === 'person' || self.objectType === 'person2' || self.objectType === 'person3') {
self.throwTimer++;
if (self.throwTimer >= self.throwDelay && gameStarted && !self.hasThrownThisCycle) {
// lanzar solo cuando el objeto este en el rango vertical permitido y respetando cooldown global
if (self.y >= throwMinY && self.y <= throwMaxY && LK.ticks - lastProjectileThrowTick >= minFramesBetweenProjectileThrows) {
self.throwObject();
lastProjectileThrowTick = LK.ticks;
self.hasThrownThisCycle = true;
self.throwTimer = 0;
// Recalcular segfn tipo
var nMin = 400,
nRand = 120;
if (self.objectType === 'person') {
nMin = 420;
nRand = 160;
}
if (self.objectType === 'person2') {
nMin = 360;
nRand = 140;
}
if (self.objectType === 'person3') {
nMin = 300;
nRand = 180;
}
self.throwDelay = Math.random() * nRand + nMin;
}
}
}
if (gameStarted && motorcycle.speed > 0) {
// Enhanced movement with better perspective scaling
var distBefore = Math.max(0, roadY - self.y);
var perspBefore = Math.max(0.01, Math.min(1, 1 - distBefore / horizonDistance));
var speedFactor = 1 + 2.5 * perspBefore; // Slightly increased speed factor
self.y += motorcycle.speed * 3.2 * speedFactor; // Increased base movement speed
// Calculate perspective with extended range for smoother transitions
var currentDistance = roadY - self.y;
var perspective = 1 - currentDistance / horizonDistance;
// Allow objects to appear much earlier (when perspective is negative) and scale properly
if (perspective > -2.0) {
// Extended range for earlier appearance
// Calculate road width with minimum constraint for stability
var roadWidth = 2048 * Math.max(0.005, perspective);
var sideOffset = self.sideOffsetBase * Math.max(0.005, perspective);
// Apply curve effect to roadside objects
var curveOffset = currentCurve * (1 - perspective) * 300;
var objectCenterX = roadCenterX + curveOffset;
// Position objects with extended side margins
// Additional margin for better visibility
if (self.isLeftSide) {
self.x = objectCenterX - roadWidth / 2 - sideOffset;
} else {
self.x = objectCenterX + roadWidth / 2 + sideOffset;
}
// Enhanced scaling that allows very small objects in distance
var finalScale = Math.max(0.0005, perspective); // Even smaller minimum scale
if (finalScale < 0.01) {
finalScale = 0.01;
} // Prevent invisible objects
self.scaleX = (self.isLeftSide ? -1 : 1) * finalScale;
self.scaleY = finalScale;
}
if (gameStarted && (self.objectType === 'person' || self.objectType === 'person2' || self.objectType === 'person3') && !self.hasThrownThisCycle) {
// chequeo continuo: solo lanzar en el rango vertical permitido y respetando cooldown global
if (self.y >= throwMinY && self.y <= throwMaxY && LK.ticks - lastProjectileThrowTick >= minFramesBetweenProjectileThrows) {
self.throwObject();
lastProjectileThrowTick = LK.ticks;
self.hasThrownThisCycle = true;
self.throwTimer = 0;
var nMin2 = 400,
nRand2 = 120;
if (self.objectType === 'person') {
nMin2 = 420;
nRand2 = 160;
}
if (self.objectType === 'person2') {
nMin2 = 360;
nRand2 = 140;
}
if (self.objectType === 'person3') {
nMin2 = 300;
nRand2 = 180;
}
self.throwDelay = Math.random() * nRand2 + nMin2;
}
}
// Reset when completely off screen with much larger margin
if (self.y > roadY + 1600 || perspective < -2.0) {
// Extended thresholds
self.y = self.originalY;
var resetDistance = roadY - self.originalY;
var resetPerspective = Math.max(0.005, Math.min(1, 1 - resetDistance / horizonDistance));
var resetRoadWidth = 2048 * resetPerspective;
var resetSideOffset = self.sideOffsetBase * resetPerspective;
self.scaleX = (self.isLeftSide ? -1 : 1) * resetPerspective;
self.scaleY = resetPerspective;
// Apply curve effect to reset position
var resetCurveOffset = currentCurve * (1 - resetPerspective) * 300;
var resetObjectCenterX = roadCenterX + resetCurveOffset;
if (self.isLeftSide) {
self.x = resetObjectCenterX - resetRoadWidth / 2 - resetSideOffset;
} else {
self.x = resetObjectCenterX + resetRoadWidth / 2 + resetSideOffset;
}
self.hasThrownThisCycle = false;
}
}
};
self.throwObject = function () {
// Start with fewer projectiles, increase with distance
var currentDistance = Math.floor(roadOffset / 10);
var distanceMilestone = Math.floor(currentDistance / 500);
var maxProjectiles = 2 + Math.min(distanceMilestone, 6); // Start with 2, cap at 8 total
if (projectiles.length >= maxProjectiles) {
return;
}
// Heavy projectiles (piedra) start rare and increase frequency after 500m milestones
var heavyChance = 0.02; // Start very low
if (distanceMilestone > 0) {
heavyChance = 0.05 + (distanceMilestone - 1) * 0.05; // Increase after first 500m
heavyChance = Math.min(heavyChance, 0.2); // Cap at 20%
}
var isHeavy = Math.random() < heavyChance;
var remainingChance = Math.random();
var isCheese = !isHeavy && remainingChance < 0.33; // 33% chance for cheese when not heavy
var isLettuce = !isHeavy && !isCheese && remainingChance < 0.66; // 33% chance for lettuce when not heavy or cheese (which means broccoli gets the remaining 33%)
var projectile = new Projectile(isHeavy, isCheese, isLettuce);
projectile.x = self.x;
projectile.y = self.y - 20;
projectile.hasHitMotorcycle = false; // Reset collision flag for new projectile
// Calculate how close motorcycle is to road edges
var roadLeft = 200;
var roadRight = 1848;
var roadWidth = roadRight - roadLeft;
var motorcycleDistanceFromLeft = motorcycle.x - roadLeft;
var motorcycleDistanceFromRight = roadRight - motorcycle.x;
var minDistanceToEdge = Math.min(motorcycleDistanceFromLeft, motorcycleDistanceFromRight);
var edgeProximityFactor = Math.max(0, 1 - minDistanceToEdge / (roadWidth * 0.25)); // 0 at center, 1 at edges
// Add inaccuracy based on edge proximity - more inaccuracy when closer to edges
var baseInaccuracy = (Math.random() - 0.5) * 260;
var edgeInaccuracy = (Math.random() - 0.5) * 400 * edgeProximityFactor; // Extra inaccuracy near edges
var totalInaccuracy = baseInaccuracy + edgeInaccuracy;
// Also add some leading/lagging based on motorcycle movement direction
var leadingFactor = 0;
if (motorcycle.lastX !== undefined) {
var motorcycleDirection = motorcycle.x - motorcycle.lastX;
leadingFactor = motorcycleDirection * (0.5 + edgeProximityFactor * 1.5); // More leading/lagging near edges
}
var targetX = motorcycle.x + totalInaccuracy + leadingFactor;
// Reduce horizontal accuracy factor when near edges to make projectiles easier to dodge
var baseHorizontalFactor = isHeavy ? 0.009 : 0.008;
var edgeAccuracyReduction = 0.7 * edgeProximityFactor; // Reduce accuracy up to 70% near edges
var horizontalFactor = baseHorizontalFactor * (1 - edgeAccuracyReduction);
projectile.velocityX = (targetX - projectile.x) * horizontalFactor;
var diffMultiplier = difficultyMultipliers[Math.min(currentLevel, maxLevel)];
var heightMultiplier = 2.0 - diffMultiplier.throwHeight; // Invert: easier levels (1.0) get higher arc (1.0), harder levels (1.4) get lower arc (0.6)
var initialUpImpulse = isHeavy ? (-18 - Math.random() * 2) * heightMultiplier : (-20 - Math.random() * 3) * heightMultiplier;
projectile.velocityY = initialUpImpulse;
projectile.gravity = isHeavy ? 0.35 : 0.28;
projectiles.push(projectile);
game.addChild(projectile);
var throwSound = LK.getSound('throw');
if (throwSound && throwSound.play) {
throwSound.play();
}
};
return self;
});
var Truck = Container.expand(function () {
var self = Container.call(this);
var truckGraphics = self.attachAsset('truck', {
anchorX: 0.5,
anchorY: 0.5
});
// karina indicator above the truck before dropping power item
var karinaSprite = self.attachAsset('karina', {
anchorX: 0.5,
anchorY: 1.0
});
var javierSprite = self.attachAsset('javier', {
anchorX: 0.5,
anchorY: 1.0
});
self.karinaBaseY = -450;
self.karinaUpY = self.karinaBaseY - 30;
karinaSprite.y = self.karinaBaseY;
karinaSprite.scaleX = 3.0;
karinaSprite.scaleY = 3.0;
karinaSprite.visible = false;
javierSprite.y = self.karinaBaseY;
javierSprite.scaleX = 3.0;
javierSprite.scaleY = 3.0;
javierSprite.visible = false;
self.speed = -6;
self.active = true;
self.dropTimer = 0;
self.dropDelay = 120;
self.nextIndicatorIsKarina = true;
self.randomizeDropDelay = function () {
var factor = 3 + Math.random() * 8; // 3x a 5x
self.dropDelay = Math.floor(120 * factor);
};
self.randomizeDropDelay();
self.targetY = horizonY + (roadY - horizonY) * 0.25;
self.baseXFactorFromCenter = 0.42;
self.oscAmp = 30;
self.oscSpeed = 0.02;
self.oscPhase = Math.random() * Math.PI * 2;
self.smoothX = 0;
self.curveDelayTimer = 0;
self.inCurveDelay = false;
self.karinaLead = 30; // frames before drop to show karina
self.karinaAnimating = false;
self.javierVisibleUntil = -1;
self.update = function () {
if (self.active) {
if (self.y > self.targetY) {
self.y += self.speed;
if (self.y <= self.targetY) {
self.y = self.targetY;
}
} else {
self.y = self.targetY;
}
var currentDistance = roadY - self.y;
var perspective = Math.max(0.01, Math.min(1, 1 - currentDistance / (roadY - horizonY)));
var roadWidthAtY = 2048 * perspective;
// Calculate current road center including curve effects
var curveOffset = currentCurve * (1 - perspective) * 300;
var roadCenterAtTruck = roadCenterX + curveOffset;
// Smooth X movement with curve delays
var targetX = roadCenterAtTruck;
var curveStrength = Math.abs(currentCurve);
// Add delay when entering curves
if (curveStrength > 0.3 && !self.inCurveDelay) {
self.inCurveDelay = true;
self.curveDelayTimer = 20 + Math.random() * 30; // Brief delay
} else if (curveStrength < 0.1) {
self.inCurveDelay = false;
self.curveDelayTimer = 0;
}
// Apply delay
if (self.curveDelayTimer > 0) {
self.curveDelayTimer--;
} else {
// Smooth movement toward target position
var xDiff = targetX - self.x;
self.x += xDiff * 0.08; // Smooth movement factor
}
var distanceToHorizon = roadY - horizonY;
var currentDistance = roadY - self.y;
var perspective = Math.max(0, 1 - currentDistance / distanceToHorizon);
self.scaleX = perspective;
self.scaleY = perspective;
if (gameStarted) {
self.dropTimer++;
if (!karinaSprite.visible && !javierSprite.visible && self.dropTimer >= self.dropDelay - self.karinaLead) {
var indicatorSprite = self.nextIndicatorIsKarina ? karinaSprite : javierSprite;
indicatorSprite.visible = true;
if (self.nextIndicatorIsKarina) {
var s1 = LK.getSound('altacoimera');
if (s1) {
s1.play();
}
} else {
var sE = LK.getSound('espert');
var sA = LK.getSound('afuera');
sE.play();
LK.setTimeout(function () {
sA.play();
}, 500);
self.javierVisibleUntil = LK.ticks + 120;
}
if (!self.karinaAnimating) {
self.karinaAnimating = true;
var upDur = Math.max(6, Math.floor(self.karinaLead * 0.4));
var downDur = Math.max(6, self.karinaLead - upDur);
tween(indicatorSprite, {
y: self.karinaUpY
}, {
duration: upDur,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(indicatorSprite, {
y: self.karinaBaseY
}, {
duration: downDur,
easing: tween.easeIn,
onFinish: function onFinish() {
self.karinaAnimating = false;
}
});
}
});
}
}
if (self.dropTimer >= self.dropDelay) {
if (self.nextIndicatorIsKarina) {
self.dropPowerItem();
self.dropTimer = 0;
karinaSprite.visible = false;
javierSprite.visible = false;
karinaSprite.y = self.karinaBaseY;
javierSprite.y = self.karinaBaseY;
self.karinaAnimating = false;
self.randomizeDropDelay();
self.nextIndicatorIsKarina = !self.nextIndicatorIsKarina;
} else {
if (LK.ticks >= self.javierVisibleUntil) {
// Javier throws healing pill
self.throwHealingPill();
self.dropTimer = 0;
karinaSprite.visible = false;
javierSprite.visible = false;
karinaSprite.y = self.karinaBaseY;
javierSprite.y = self.karinaBaseY;
self.karinaAnimating = false;
self.randomizeDropDelay();
self.nextIndicatorIsKarina = !self.nextIndicatorIsKarina;
}
}
}
}
}
};
self.dropPowerItem = function () {
var powerItem = new PowerItem();
// Start from karina sprite position (global position within truck)
var karinaWorldX = self.x + karinaSprite.x * self.scaleX;
var karinaWorldY = self.y + karinaSprite.y * self.scaleY;
powerItem.x = karinaWorldX;
powerItem.y = karinaWorldY;
// choose random point on the road (not targeting the motorcycle)
var roadLeft = 200;
var roadRight = 1848;
var margin = 60;
var targetX = roadLeft + margin + Math.random() * Math.max(0, roadRight - roadLeft - margin * 2);
// Parabola estilo proyectiles laterales
var horizontalFactor = 0.007;
powerItem.velocityX = (targetX - powerItem.x) * horizontalFactor;
var isHighArc = Math.random() < 0.4;
powerItem.velocityY = isHighArc ? -10 - Math.random() * 3 : -8 - Math.random() * 2;
powerItem.gravity = 0.28;
powerItems.push(powerItem);
game.addChild(powerItem);
};
self.throwHealingPill = function () {
var healingPill = new HealingPill();
// Start from javier sprite position (global position within truck)
var javierWorldX = self.x + javierSprite.x * self.scaleX;
var javierWorldY = self.y + javierSprite.y * self.scaleY;
healingPill.x = javierWorldX;
healingPill.y = javierWorldY;
// Target motorcycle position with some accuracy
var targetX = motorcycle.x + (Math.random() - 0.5) * 200; // Some inaccuracy
var horizontalFactor = 0.008;
healingPill.velocityX = (targetX - healingPill.x) * horizontalFactor;
healingPill.velocityY = -12 - Math.random() * 2;
healingPill.gravity = 0.25;
healingPills.push(healingPill);
game.addChild(healingPill);
// Play pill throw sound
var pillSound = LK.getSound('pill_throw');
if (pillSound && pillSound.play) {
pillSound.play();
}
// Play sound and drop diaper item after longer delay
LK.setTimeout(function () {
var diaperSound = LK.getSound('diaper_drop');
if (diaperSound && diaperSound.play) {
diaperSound.play();
}
self.dropDiaperItem();
}, 2500);
};
self.dropDiaperItem = function () {
var diaperItem = new DiaperItem();
// Start from further left of truck window - more offset to the left side
diaperItem.x = self.x - self.scaleX * 450; // Further left window position
diaperItem.y = self.y - 50; // Slightly above truck center
// Initially set no movement - diaper will blink in place
diaperItem.velocityX = 0;
diaperItem.velocityY = 0;
diaperItem.gravity = 0;
// Make it visible and properly scaled
diaperItem.scaleX = 2.0;
diaperItem.scaleY = 2.0;
// Ensure no tint applied
diaperItem.tint = 0xFFFFFF;
// Blink for 1.5 seconds (1500ms) with 5 blink cycles
tween(diaperItem, {
alpha: 0
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(diaperItem, {
alpha: 1
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(diaperItem, {
alpha: 0
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(diaperItem, {
alpha: 1
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(diaperItem, {
alpha: 0
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(diaperItem, {
alpha: 1
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(diaperItem, {
alpha: 0
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(diaperItem, {
alpha: 1
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(diaperItem, {
alpha: 0
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(diaperItem, {
alpha: 1
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
// After blinking for 1.5 seconds, finally give it movement
diaperItem.velocityX = (Math.random() - 0.5) * 4; // Small random horizontal velocity
diaperItem.velocityY = 3 + Math.random() * 2; // Downward velocity
diaperItem.gravity = 0.3; // Apply gravity
}
});
}
});
}
});
}
});
}
});
}
});
}
});
}
});
}
});
}
});
diaperItems.push(diaperItem);
// Add diaper after road segments but before motorcycle to ensure proper z-order
var roadSegmentCount = roadSegments.length;
var insertIndex = Math.min(roadSegmentCount + 1, game.children.length);
game.addChildAt(diaperItem, insertIndex);
};
self.createFlyingAsset = function () {
var flyingAsset = new FlyingAsset();
// Start from right side of screen, in the sky
flyingAsset.x = 2048 + 200; // Start off-screen right
flyingAsset.y = 400 + Math.random() * 300; // Random height in sky
flyingAsset.scaleX = 0.8 + Math.random() * 0.4; // Random scale
flyingAsset.scaleY = flyingAsset.scaleX;
flyingAssets.push(flyingAsset);
game.addChild(flyingAsset);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb
});
/****
* Game Code
****/
var roadY = 2732;
// El horizonte debe estar en la parte superior del cesped
// Cesped: y=2500, scaleY=3.45, height=500, anchorY=0.5
// Parte superior = y - (height * scaleY * anchorY) = 2500 - (500 * 3.45 * 0.5) = 2500 - 862.5 = 1637.5
var horizonY = 1638;
var roadOffset = 0;
// Curve system variables
var currentCurve = 0; // Current road curvature (-1 to 1, left to right)
var targetCurve = 0; // Target curvature to transition to
var curveTransitionSpeed = 0.02; // How fast curves transition
var roadCenterX = 1024; // Current center of the road
var targetRoadCenterX = 1024; // Target center position
var lastCurveChange = 0; // Timer for curve changes
var curveChangeInterval = 180; // Frames between potential curve changes (3 seconds at 60fps)
var motorcycleTilt = 0; // Current motorcycle tilt angle
var maxTilt = 0.3; // Maximum tilt in radians
var gameStarted = false;
var gameCompleting = false;
var bridges = [];
var levelNames = {
1: 'HUIDA INICIAL',
2: 'CARRETERA PELIGROSA',
3: 'ZONA DE CONFLICTO',
4: 'TERRITORIO HOSTIL',
5: 'ESCAPE FINAL'
};
// Psychedelic overlay system
var psychedelicOverlay = null;
var psychedelicActive = false;
var psychedelicColors = [0x9370DB, 0x8A2BE2, 0x9966CC, 0xBA55D3, 0xDA70D6, 0xDDA0DD, 0xE6E6FA, 0xF0E68C, 0xEEE8AA, 0xD2B48C, 0xF5DEB3, 0xFFE4B5, 0xFFB6C1, 0xFFC0CB, 0x87CEEB, 0xADD8E6, 0xB0E0E6, 0xAFEEEE, 0x98FB98, 0x90EE90, 0xF0FFF0, 0xF5F5DC];
var phraseTimer = 0;
var phraseInterval = 600; // 10 seconds at 60 FPS
var phraseAssets = ['frase1', 'frase2', 'frase3', 'frase4', 'frase5', 'frase6', 'frase7'];
// evitar repetir la misma frase consecutivamente
var lastPhraseIndex = -1;
var trucksCleared = false;
// Level progression system
var currentLevel = 1;
var maxLevel = 5;
var levelThresholdKm = 1000; // Each level requires 1000km
var lastLevelCheck = 0;
var hitsReceived = storage.hitsReceived || 0;
var itemsCollected = storage.itemsCollected || 0;
var levelsCompleted = storage.levelsCompleted || 0;
// Level badge display
var levelBadge = null;
var levelBadgeTimer = 0;
var levelBadgeDisplayTime = 180; // 3 seconds at 60fps
// Difficulty scaling per level - max difficulty capped at level 4
var difficultyMultipliers = {
1: {
throwFreq: 1.0,
throwHeight: 1.0,
score: 1.0
},
2: {
throwFreq: 1.1,
throwHeight: 1.05,
score: 1.3
},
3: {
throwFreq: 1.3,
throwHeight: 1.15,
score: 1.8
},
4: {
throwFreq: 1.6,
throwHeight: 1.3,
score: 2.5
},
5: {
throwFreq: 1.6,
throwHeight: 1.3,
score: 2.5
}
};
var roadSegments = [];
var roadLines = [];
var leftSidewalks = [];
var rightSidewalks = [];
var totalSegments = 90;
var horizonDistance = roadY - horizonY;
var segmentSpacing = horizonDistance / totalSegments;
var throwMinY = horizonY + horizonDistance * 0.35;
var throwMaxY = horizonY + horizonDistance * 0.60;
// cooldown global entre lanzamientos de proyectiles para espaciar
var lastProjectileThrowTick = -10000;
var minFramesBetweenProjectileThrows = 90; // Start with much higher cooldown, will decrease with distance
// Agregar margen inferior para evitar que la ruta se corte
var bottomMargin = 400;
var extendedRoadY = roadY + bottomMargin;
LK.playMusic('alta');
game.addChild(LK.getAsset('cesped', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2500,
scaleX: 5,
scaleY: 3.45
}));
game.addChild(LK.getAsset('skyline', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 739,
scaleX: 21,
scaleY: 18
}));
for (var i = 0; i < totalSegments; i++) {
var distance = i * segmentSpacing;
var yPosition = horizonY + distance;
var perspective = Math.max(0.01, distance / horizonDistance);
var roadWidth = 2048 * perspective;
var segmentHeight = segmentSpacing * 1.2 * perspective;
if (yPosition <= roadY) {
var roadSeg = game.addChild(LK.getAsset('road_segment', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: yPosition,
scaleX: roadWidth / 100,
scaleY: segmentSpacing / 5 * perspective
}));
roadSegments.push(roadSeg);
}
}
var powerBarBg = LK.getAsset('power_bar_bg', {
anchorX: 0.5,
anchorY: 1.0,
x: 600,
y: -300
});
LK.gui.center.addChild(powerBarBg);
var powerBarFill = LK.getAsset('power_bar_fill', {
anchorX: 0.5,
anchorY: 1.0,
x: 600,
y: -300
});
LK.gui.center.addChild(powerBarFill);
var powerItems = [];
var roadsideObjects = [];
var projectiles = [];
var healingPills = [];
var flyingAssets = [];
var diaperItems = [];
// tipos base para objetos laterales
var personVariants = ['person', 'person2', 'person3'];
var otherVariants = ['tree', 'sign'];
// cantidad por lado y distancia extendida para mayor espacio
var objectsPerSide = 60;
var extendedDistanceFactor = 6.4;
// secuencias izquierda/derecha desfasadas para no coincidir al mismo tiempo
var leftTypes = [];
var rightTypes = [];
for (var oi = 0; oi < objectsPerSide; oi++) {
if (oi % 2 === 0) {
leftTypes.push(personVariants[oi / 2 % personVariants.length | 0]);
rightTypes.push(otherVariants[(oi / 2 + 1) % otherVariants.length | 0]);
} else {
leftTypes.push(otherVariants[(oi / 2 | 0) % otherVariants.length]);
rightTypes.push(personVariants[(oi / 2 + 1) % personVariants.length | 0]);
}
}
for (var i = 0; i < objectsPerSide; i++) {
var leftType = leftTypes[i];
var leftObject = game.addChild(new RoadsideObject(leftType));
// mayor distancia y aparicion mas temprana
var extendedDistance = horizonDistance * extendedDistanceFactor;
var distance = i / objectsPerSide * extendedDistance;
var yPos = Math.max(horizonY, extendedRoadY - distance);
var perspective = 1 - Math.min(distance, horizonDistance) / horizonDistance;
var roadWidth = 2048 * Math.max(0.005, perspective);
// posicion izquierda
leftObject.x = 1024 - roadWidth / 2 - 120 * Math.max(0.005, perspective);
leftObject.y = yPos;
leftObject.originalY = yPos;
leftObject.scaleX = -Math.max(0.01, perspective);
leftObject.scaleY = Math.max(0.01, perspective);
leftObject.isLeftSide = true;
roadsideObjects.push(leftObject);
}
for (var i = 0; i < objectsPerSide; i++) {
var rightType = rightTypes[i];
var rightObject = game.addChild(new RoadsideObject(rightType));
// mayor distancia y escalon en Y para no coincidir con la izquierda
var extendedDistance = horizonDistance * extendedDistanceFactor;
var distance = (i + 0.5) / objectsPerSide * extendedDistance;
var yPos = Math.max(horizonY, extendedRoadY - distance);
var perspective = 1 - Math.min(distance, horizonDistance) / horizonDistance;
var roadWidth = 2048 * Math.max(0.005, perspective);
// posicion derecha
rightObject.x = 1024 + roadWidth / 2 + 120 * Math.max(0.005, perspective);
rightObject.y = yPos;
rightObject.originalY = yPos;
rightObject.scaleX = Math.max(0.01, perspective);
rightObject.scaleY = Math.max(0.01, perspective);
rightObject.isLeftSide = false;
roadsideObjects.push(rightObject);
}
var scoreTxt = new Text2('Score: 0 | Metros: 0 | Nivel: 1', {
size: 100,
fill: 0x000000
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
function checkLevelProgression() {
var currentDistance = Math.floor(roadOffset / 10);
var targetKm = currentLevel * levelThresholdKm;
if (currentDistance >= targetKm && currentLevel < maxLevel) {
// Level completed - create bridge for new level
currentLevel++;
levelsCompleted++;
storage.currentLevel = currentLevel;
storage.levelsCompleted = levelsCompleted;
// Create bridge for new level
createLevelBridge(currentLevel);
// Update level text
levelTxt.setText('Nivel: ' + currentLevel);
// Calculate and update score based on performance
updateLevelScore();
}
}
function createLevelBridge(levelNumber) {
var levelName = levelNames[levelNumber] || 'NUEVO NIVEL';
var bridge = new Bridge(levelNumber, levelName);
// Position bridge in the distance, approaching the player
bridge.x = roadCenterX;
bridge.y = horizonY - 200; // Start in distance
// Scale based on distance perspective
var initialPerspective = 0.1;
bridge.scaleX = initialPerspective;
bridge.scaleY = initialPerspective;
bridges.push(bridge);
game.addChild(bridge);
}
function showLevelBadge(completedLevel) {
// Show level splash instead of badge
showLevelSplash(completedLevel);
}
function showFloatingScore(points, x, y) {
// Create floating score text
var floatingText = new Text2(points, {
size: 120,
fill: 0xFFD700
});
floatingText.anchor.set(0.5, 0.5);
floatingText.x = x;
floatingText.y = y;
floatingText.alpha = 1.0;
game.addChild(floatingText);
// Animate floating text upward with fade out
tween(floatingText, {
y: y - 150,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(floatingText);
}
});
}
function updateLevelScore() {
var currentDistance = Math.floor(roadOffset / 10);
// Ensure currentLevel is valid and has a default value
var validLevel = currentLevel || 1;
if (validLevel < 1 || validLevel > maxLevel) {
validLevel = 1;
}
var diffMultiplier = difficultyMultipliers[validLevel];
// Fallback to level 1 multipliers if lookup fails
if (!diffMultiplier) {
diffMultiplier = difficultyMultipliers[1];
}
// Additional safety check - if still undefined, create default multiplier
if (!diffMultiplier || typeof diffMultiplier.score === 'undefined') {
diffMultiplier = {
throwFreq: 1.0,
throwHeight: 1.0,
score: 1.0
};
}
// Score based on distance, difficulty, and hits received
var baseScore = currentDistance * diffMultiplier.score;
var hitPenalty = hitsReceived * 10;
var finalScore = Math.max(0, baseScore - hitPenalty);
LK.setScore(Math.floor(finalScore));
}
var healthTxt = new Text2('Power: 3', {
size: 60,
fill: 0xFF0000
});
healthTxt.anchor.set(0, 0);
healthTxt.x = 0;
healthTxt.y = 0;
var motorcycle = game.addChild(new Motorcycle());
motorcycle.x = 800;
motorcycle.y = roadY - 250;
motorcycle.lastEdgeTick = -1000;
LK.gui.topRight.addChild(healthTxt);
var dragStartX = 0;
var isDragging = false;
// Splash setup
var splash = LK.getAsset('splash', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
scaleX: 1,
scaleY: 1
});
var splashActive = true;
LK.gui.center.addChild(splash);
game.down = function (x, y, obj) {
if (splashActive) {
var clickSnd = LK.getSound('cloaca');
if (clickSnd) {
clickSnd.play();
}
// Reset game stats for new game
currentLevel = 1;
hitsReceived = 0;
itemsCollected = 0;
storage.currentLevel = currentLevel;
storage.hitsReceived = hitsReceived;
storage.itemsCollected = itemsCollected;
// dismiss splash and switch music
LK.gui.center.removeChild(splash);
splashActive = false;
LK.stopMusic();
LK.playMusic('moto');
// Start playing gritos during gameplay
LK.playMusic('gritos');
if (trucksCleared) {
gameStarted = true;
}
return;
}
if (gameStarted) {
isDragging = true;
dragStartX = x;
}
};
game.move = function (x, y, obj) {
if (splashActive) {
return;
}
if (isDragging && gameStarted) {
var deltaX = x - dragStartX;
var newX = motorcycle.x + deltaX * 0.5;
var hitEdge = false;
// Calculate road bounds based on current curve position
var curveOffset = currentCurve * 200; // Same curve offset as road segments
var roadCenterAtMotorcycle = roadCenterX + curveOffset;
var roadWidth = 1648; // Road width at motorcycle position
var roadLeft = roadCenterAtMotorcycle - roadWidth / 2;
var roadRight = roadCenterAtMotorcycle + roadWidth / 2;
var edgeCooldownTicks = 30;
if (newX < roadLeft) {
newX = roadLeft;
hitEdge = true;
}
if (newX > roadRight) {
newX = roadRight;
hitEdge = true;
}
if (newX !== motorcycle.x) {
motorcycle.x = newX;
}
if (hitEdge && LK.ticks - motorcycle.lastEdgeTick > edgeCooldownTicks) {
motorcycle.lastEdgeTick = LK.ticks;
var edgeSnd = LK.getSound('edge');
if (edgeSnd && edgeSnd.play) {
edgeSnd.play();
}
}
dragStartX = x;
}
};
game.up = function (x, y, obj) {
if (splashActive) {
return;
}
isDragging = false;
};
// Function to start psychedelic overlay effect
function startPsychedelicEffect() {
if (!psychedelicActive) {
psychedelicActive = true;
// Create full-screen psychedelic overlay
if (!psychedelicOverlay) {
psychedelicOverlay = LK.getAsset('road_segment', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
scaleX: 100,
scaleY: 100
});
psychedelicOverlay.alpha = 0.15;
game.addChild(psychedelicOverlay);
}
}
}
// Global function to stop psychedelic effect
function stopPsychedelicEffect() {
psychedelicActive = false;
// Remove overlay
if (psychedelicOverlay) {
psychedelicOverlay.destroy();
psychedelicOverlay = null;
}
// Reset all tints when psychedelic effect stops
for (var p = 0; p < projectiles.length; p++) {
projectiles[p].tint = 0xFFFFFF;
// Reset projectile gravity to normal values
}
for (var r = 0; r < roadsideObjects.length; r++) {
roadsideObjects[r].tint = 0xFFFFFF;
}
for (var rs = 0; rs < roadSegments.length; rs++) {
roadSegments[rs].tint = 0xFFFFFF;
}
for (var pi = 0; pi < powerItems.length; pi++) {
powerItems[pi].tint = 0xFFFFFF;
}
for (var t = 0; t < trucks.length; t++) {
trucks[t].tint = 0xFFFFFF;
}
for (var di = 0; di < diaperItems.length; di++) {
diaperItems[di].tint = 0xFFFFFF;
}
for (var hi = 0; hi < healingPills.length; hi++) {
healingPills[hi].tint = 0xFFFFFF;
}
for (var b = 0; b < bridges.length; b++) {
bridges[b].tint = 0xFFFFFF;
}
// Reset motorcycle tint to white
motorcycle.tint = 0xFFFFFF;
}
var trucks = [];
var truck1 = game.addChild(new Truck());
truck1.x = 1024;
truck1.y = roadY - 600;
trucks.push(truck1);
game.update = function () {
if (!trucksCleared) {
var allTrucksCleared = true;
for (var i = 0; i < trucks.length; i++) {
if (trucks[i].y > roadY - 300) {
allTrucksCleared = false;
break;
}
}
if (allTrucksCleared) {
trucksCleared = true;
if (!splashActive) {
gameStarted = true;
LK.stopMusic();
LK.playMusic('moto');
// Start playing gritos during gameplay
LK.playMusic('gritos');
}
}
}
if (gameStarted && motorcycle.speed > 0) {
var movementFactor = 3;
var offset = roadOffset * movementFactor % horizonDistance;
for (var i = 0; i < roadSegments.length; i++) {
var distanceAlong = (i * segmentSpacing + offset) % horizonDistance;
var yPosition = horizonY + distanceAlong;
var perspective = Math.max(0.01, Math.min(1.08, distanceAlong / horizonDistance));
var roadWidth = 2048 * perspective;
// Apply curve effect to road segments
var curveOffset = currentCurve * (1 - perspective) * 300; // Stronger curve effect in distance
var segmentCenterX = roadCenterX + curveOffset;
roadSegments[i].x = segmentCenterX;
roadSegments[i].y = yPosition;
roadSegments[i].scaleX = roadWidth / 100;
roadSegments[i].scaleY = horizonDistance / totalSegments / 5 * perspective;
// Add slight rotation to road segments for banking effect
roadSegments[i].rotation = currentCurve * 0.02 * (1 - perspective);
}
for (var k = 0; k < leftSidewalks.length; k++) {
var swDistance = (k * segmentSpacing + offset) % horizonDistance;
var swY = roadY - swDistance;
var swPerspective = Math.max(0.01, Math.min(1.08, 1 - swDistance / horizonDistance));
var swRoadWidth = 2048 * swPerspective;
var segmentHeight = horizonDistance / totalSegments * 1.2 * swPerspective;
leftSidewalks[k].y = swY;
rightSidewalks[k].y = swY;
leftSidewalks[k].x = 1024 - swRoadWidth / 2 - 80 * swPerspective;
leftSidewalks[k].scaleX = swPerspective * 2;
leftSidewalks[k].scaleY = segmentHeight / 40;
rightSidewalks[k].x = 1024 + swRoadWidth / 2 + 80 * swPerspective;
rightSidewalks[k].scaleX = swPerspective * 2;
rightSidewalks[k].scaleY = segmentHeight / 40;
}
}
if (gameStarted) {
var distance = Math.floor(roadOffset / 10);
scoreTxt.setText('Score: ' + LK.getScore() + ' | Metros: ' + distance + ' | Nivel: ' + currentLevel);
}
healthTxt.setText('Power: ' + Math.round(motorcycle.power) + '%');
if (gameStarted) {
if (motorcycle.lastX === undefined) {
motorcycle.lastX = motorcycle.x;
motorcycle.isFlipping = false;
}
// Calculate motorcycle banking/tilt based on movement and curve
var movementDelta = motorcycle.x - motorcycle.lastX;
var curveTilt = currentCurve * 0.15; // Road curve affects tilt
var movementTilt = movementDelta * 0.008; // Movement affects tilt
var targetTilt = curveTilt + movementTilt;
// Clamp tilt to maximum values
targetTilt = Math.max(-maxTilt, Math.min(maxTilt, targetTilt));
// Smoothly transition motorcycle tilt
var tiltDiff = targetTilt - motorcycleTilt;
motorcycleTilt += tiltDiff * 0.1;
motorcycle.rotation = motorcycleTilt;
var screenCenter = 1024;
var crossedToRight = motorcycle.lastX < screenCenter && motorcycle.x >= screenCenter;
var crossedToLeft = motorcycle.lastX > screenCenter && motorcycle.x <= screenCenter;
if ((crossedToRight || crossedToLeft) && !motorcycle.isFlipping) {
motorcycle.isFlipping = true;
tween(motorcycle, {
scaleX: -motorcycle.scaleX
}, {
duration: 0,
easing: tween.easeOut,
onFinish: function onFinish() {
motorcycle.isFlipping = false;
}
});
}
motorcycle.lastX = motorcycle.x;
}
var powerPercentage = motorcycle.power / 100;
powerBarFill.scaleY = powerPercentage;
if (powerPercentage > 0.6) {
powerBarFill.tint = 0x99ff99;
} else if (powerPercentage > 0.3) {
powerBarFill.tint = 0xffff99;
} else {
powerBarFill.tint = 0xff9999;
}
for (var i = projectiles.length - 1; i >= 0; i--) {
var projectile = projectiles[i];
if (projectile.x < -100 || projectile.x > 2148 || projectile.y > extendedRoadY + 100) {
projectile.destroy();
projectiles.splice(i, 1);
continue;
}
if (gameStarted && motorcycle.intersectsProjectile(projectile) && !projectile.hasHitMotorcycle) {
var damageAmount = projectile.isHeavy ? 15 : 3;
motorcycle.takeDamage(damageAmount);
var impactSnd;
if (projectile.isHeavy) {
impactSnd = LK.getSound('piedra');
} else if (projectile.isCheese) {
var hitSnd = LK.getSound('hit');
if (hitSnd && hitSnd.play) {
hitSnd.play();
}
impactSnd = LK.getSound('queso');
} else {
impactSnd = LK.getSound('hit');
}
if (impactSnd && impactSnd.play) {
impactSnd.play();
}
// Bounce projectile away from motorcycle
var bounceForceX = (projectile.x - motorcycle.x) * 0.3 * 0.2; // 20% of current force
var bounceForceY = (-Math.abs(projectile.velocityY) * 0.8 - 5) * 0.2; // 20% of current force
projectile.velocityX = bounceForceX;
projectile.velocityY = bounceForceY;
projectile.gravity = 0.8; // Increased gravity after hitting motorcycle
projectile.hasHitMotorcycle = true; // Mark as having hit motorcycle
continue;
}
if (projectile.y >= extendedRoadY - 20 && projectile.velocityY > 0) {
projectile.destroy();
projectiles.splice(i, 1);
}
}
for (var i = powerItems.length - 1; i >= 0; i--) {
var powerItem = powerItems[i];
if (powerItem.x < -100 || powerItem.x > 2148 || powerItem.y > extendedRoadY + 100) {
powerItem.destroy();
powerItems.splice(i, 1);
continue;
}
if (gameStarted && powerItem.intersects(motorcycle)) {
motorcycle.restorePower(3);
itemsCollected++;
storage.itemsCollected = itemsCollected;
// Add 97 points and show floating score
LK.setScore(LK.getScore() + 97);
showFloatingScore('+97 pts.', powerItem.x, powerItem.y);
powerItem.destroy();
powerItems.splice(i, 1);
continue;
}
if (powerItem.y >= extendedRoadY - 20 && powerItem.velocityY > 0) {
powerItem.destroy();
powerItems.splice(i, 1);
}
}
// Handle healing pills
for (var i = healingPills.length - 1; i >= 0; i--) {
var healingPill = healingPills[i];
if (healingPill.x < -100 || healingPill.x > 2148 || healingPill.y > extendedRoadY + 100) {
healingPill.destroy();
healingPills.splice(i, 1);
continue;
}
if (gameStarted && healingPill.intersects(motorcycle)) {
// Recover 12% of energy
motorcycle.restorePower(12);
itemsCollected++;
storage.itemsCollected = itemsCollected;
// Add 100 points and show floating score
LK.setScore(LK.getScore() + 100);
showFloatingScore('+100 pts.', healingPill.x, healingPill.y);
// Create flying asset in sky when pill is collected
var flyingAsset = new FlyingAsset();
flyingAsset.x = 2048 + 200; // Start off-screen right
flyingAsset.y = 400 + Math.random() * 300; // Random height in sky
flyingAsset.scaleX = 0.8 + Math.random() * 0.4; // Random scale
flyingAsset.scaleY = flyingAsset.scaleX;
flyingAssets.push(flyingAsset);
game.addChild(flyingAsset);
// Start psychedelic effect when flying asset is created
startPsychedelicEffect();
healingPill.destroy();
healingPills.splice(i, 1);
continue;
}
if (healingPill.y >= extendedRoadY - 20 && healingPill.velocityY > 0) {
healingPill.destroy();
healingPills.splice(i, 1);
}
}
// Handle diaper items
for (var i = diaperItems.length - 1; i >= 0; i--) {
var diaperItem = diaperItems[i];
// Remove when diaper exits screen (like road segments)
if (diaperItem.x < -200 || diaperItem.x > 2248 || diaperItem.y > extendedRoadY + 200) {
diaperItem.destroy();
diaperItems.splice(i, 1);
continue;
}
if (gameStarted && motorcycle.intersectsDiaper(diaperItem)) {
// Subtract 100 points and show floating score
LK.setScore(Math.max(0, LK.getScore() - 100));
showFloatingScore('-100 pts.', diaperItem.x, diaperItem.y);
diaperItem.destroy();
diaperItems.splice(i, 1);
continue;
}
}
// Random phrase system - play one of six phrases every 10 seconds
if (gameStarted) {
phraseTimer++;
if (phraseTimer >= phraseInterval) {
// seleccionar una frase diferente de la ultima
var randomPhraseIndex = Math.floor(Math.random() * phraseAssets.length);
if (phraseAssets.length > 1 && randomPhraseIndex === lastPhraseIndex) {
// reintentar una vez para evitar repeticion inmediata
randomPhraseIndex = (randomPhraseIndex + 1 + Math.floor(Math.random() * (phraseAssets.length - 1))) % phraseAssets.length;
}
var phraseSoundId = phraseAssets[randomPhraseIndex];
var phraseSound = LK.getSound(phraseSoundId);
if (phraseSound && phraseSound.play) {
phraseSound.play();
}
lastPhraseIndex = randomPhraseIndex;
phraseTimer = 0; // reset timer
}
}
// Check level progression
if (gameStarted) {
checkLevelProgression();
updateLevelScore();
}
// Check for game completion (level 5 completed and distance > 5000m)
var currentDistance = Math.floor(roadOffset / 10);
if (gameStarted && currentLevel >= maxLevel && currentDistance > 5000 && !gameCompleting) {
gameCompleting = true;
// Move truck and motorcycle to horizon
tween(motorcycle, {
y: horizonY + 50,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 3000,
easing: tween.easeInOut
});
for (var t = 0; t < trucks.length; t++) {
tween(trucks[t], {
y: horizonY,
scaleX: 0.05,
scaleY: 0.05
}, {
duration: 3500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Show you win after vehicles reach horizon
LK.setTimeout(function () {
LK.showYouWin();
}, 1000);
}
});
}
}
// Handle bridges - update perspective scaling as they approach
for (var b = 0; b < bridges.length; b++) {
var bridge = bridges[b];
var currentDistance = roadY - bridge.y;
var perspective = Math.max(0.01, Math.min(1.2, 1 - currentDistance / horizonDistance));
// Apply curve effect to bridge position
var curveOffset = currentCurve * (1 - perspective) * 300;
bridge.x = roadCenterX + curveOffset;
// Scale bridge based on perspective
bridge.scaleX = perspective;
bridge.scaleY = perspective;
// Ensure bridge spans the road width
var roadWidth = 2048 * perspective;
if (bridge.scaleX < roadWidth / 2048) {
bridge.scaleX = roadWidth / 2048;
}
}
// Handle psychedelic overlay effect
if (psychedelicActive && psychedelicOverlay && flyingAssets.length > 0) {
// Slower color cycling for sedating psychedelic effect
var slowTicks = Math.floor(LK.ticks / 20); // Much slower cycling
var overlayColor = psychedelicColors[slowTicks % psychedelicColors.length];
psychedelicOverlay.tint = overlayColor;
// Apply color cycling to all game objects including motorcycle (Rita)
var mediumTicks = Math.floor(LK.ticks / 15); // Slower cycling
for (var p = 0; p < projectiles.length; p++) {
var projColor = psychedelicColors[(mediumTicks + p * 3) % psychedelicColors.length];
projectiles[p].tint = projColor;
}
for (var r = 0; r < roadsideObjects.length; r++) {
if (roadsideObjects[r].y > horizonY && roadsideObjects[r].y < roadY) {
var objColor = psychedelicColors[(mediumTicks + r * 5) % psychedelicColors.length];
roadsideObjects[r].tint = objColor;
}
}
for (var rs = 0; rs < roadSegments.length; rs++) {
var roadColor = psychedelicColors[(mediumTicks + rs * 2) % psychedelicColors.length];
roadSegments[rs].tint = roadColor;
}
for (var pi = 0; pi < powerItems.length; pi++) {
var powerColor = psychedelicColors[(mediumTicks + pi * 6) % psychedelicColors.length];
powerItems[pi].tint = powerColor;
}
for (var t = 0; t < trucks.length; t++) {
var truckColor = psychedelicColors[(mediumTicks + t * 4) % psychedelicColors.length];
trucks[t].tint = truckColor;
}
for (var di = 0; di < diaperItems.length; di++) {
var diaperColor = psychedelicColors[(mediumTicks + di * 7) % psychedelicColors.length];
diaperItems[di].tint = diaperColor;
}
for (var hi = 0; hi < healingPills.length; hi++) {
var pillColor = psychedelicColors[(mediumTicks + hi * 8) % psychedelicColors.length];
healingPills[hi].tint = pillColor;
}
for (var b = 0; b < bridges.length; b++) {
var bridgeColor = psychedelicColors[(mediumTicks + b * 9) % psychedelicColors.length];
bridges[b].tint = bridgeColor;
}
// Keep flying assets at original color (no tint) to make them stand out
for (var f = 0; f < flyingAssets.length; f++) {
flyingAssets[f].tint = 0xFFFFFF;
}
// Apply rainbow colors to motorcycle (Rita) during psychedelic effect
var motorcycleColor = psychedelicColors[(mediumTicks + 10) % psychedelicColors.length];
motorcycle.tint = motorcycleColor;
}
// Handle level badge display timer
if (levelBadge && levelBadgeTimer > 0) {
levelBadgeTimer--;
if (levelBadgeTimer <= 0) {
tween(levelBadge, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
if (levelBadge) {
LK.gui.center.removeChild(levelBadge);
levelBadge = null;
}
}
});
}
}
if (gameStarted && LK.ticks % 600 == 0) {
// Calculate current distance in meters
var currentDistance = Math.floor(roadOffset / 10);
// Apply level-based difficulty multiplier to throwing frequency
var diffMultiplier = difficultyMultipliers[Math.min(currentLevel, maxLevel)];
var levelFreqMultiplier = diffMultiplier.throwFreq;
// Only increase throwing frequency every 500 meters, scaled by level
var distanceMilestone = Math.floor(currentDistance / 500);
// Gradually decrease global cooldown with distance and level
if (distanceMilestone > 0) {
var newCooldown = Math.floor((90 - distanceMilestone * 8) / levelFreqMultiplier);
minFramesBetweenProjectileThrows = Math.max(15, newCooldown); // Lower minimum for higher levels
}
// Start with fewer projectiles and gradually increase, scaled by level
var baseDecrease = Math.min(distanceMilestone * 3 * levelFreqMultiplier, 35); // Higher cap with level scaling
for (var j = 0; j < roadsideObjects.length; j++) {
if (roadsideObjects[j].objectType === 'person' || roadsideObjects[j].objectType === 'person2' || roadsideObjects[j].objectType === 'person3') {
var dec = Math.floor((2 + baseDecrease) * levelFreqMultiplier); // Level-scaled decrease
if (roadsideObjects[j].objectType === 'person3') {
dec = Math.floor((3 + baseDecrease) * levelFreqMultiplier);
}
if (roadsideObjects[j].objectType === 'person2') {
dec = Math.floor((2 + baseDecrease) * levelFreqMultiplier);
}
roadsideObjects[j].throwDelay = Math.max(20, roadsideObjects[j].throwDelay - dec); // Lower minimum delay for higher levels
}
}
}
};
Un grupo de gente manifestando a cuerpo completo mirando al frente enojada con banderas argentinas, todo en pixel art. In-Game asset. 2d. High contrast. No shadows
Una planta de lechuga en pixel art en el aire. In-Game asset. 2d. High contrast. No shadows
Un queso gruyere en pixel art. In-Game asset. 2d. High contrast. No shadows
moto
Music
gritos
Music
noEsUnaHuida
Sound effect
frase2
Sound effect
frase3
Sound effect
frase6
Sound effect
edge
Sound effect
frase1
Sound effect
frase4
Sound effect
frase5
Sound effect
hit
Sound effect
power_up
Sound effect
throw
Sound effect
piedra
Sound effect
altacoimera
Sound effect
alta
Music
cloaca
Sound effect
corruptosLoTuyos
Sound effect
frase7
Sound effect
queso
Sound effect
espert
Sound effect
afuera
Sound effect
level_complete
Sound effect
pill_throw
Sound effect
diaper_drop
Sound effect
bridge_level
Sound effect
miraConan
Sound effect