User prompt
El badget de los levels debe ser siempre el mismo, en el centro contendra un espacio para que vaya el numero con el nivel
User prompt
cuando esta del lado derecho la moto, la colicion con los objetos no funciona, del lado izquierdo va perfecto
User prompt
Cuando vuelve a aparecer el proyectil debe reiniciar los hiy que ha hecho
User prompt
Una vez golpea, debe caer con mayor gravedad y ya no puede volver a golpear la moto
User prompt
El rebote debe tener el 20% de la fuerza actual
User prompt
El hitbox de la moto debe estar solo en la parte superior medir 30% del alto total y ser un 15% más estrecho, los proyectiles que impactan deben rebotar
User prompt
Los proyectiles deben bajar energía si golpean en las parte superior de la moto, aprox 33% , si golpea en el 66% inferior el proyectil debe rebotar
User prompt
Hay que reducir el hitbox solo a la cabeza la moto, los que peguen de costado rebotan más abajo rebotan y caen
User prompt
Cuánto más fácil el nivel más alta la parábola de los proyectiles, ahora está al revés
User prompt
Compilation error[L815]: TypeError: Cannot read properties of undefined (reading 'score'
User prompt
Sigue el mismo error
User prompt
Puedes solucionar el error typeerror score [L815]
User prompt
Resuelve el error
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'score')' in or related to this line: 'var baseScore = currentDistance * diffMultiplier.score;' Line Number: 815
User prompt
Me gustaría implementar 5 niveles con dificultad progresiva, cada 1000 km, al pasar un nivel debe mostrar un asset y reproducir un sonido, la puntuación depende de los golpes que reciba y la dificultad de la la frecuencia y la altura de los objetos ↪💡 Consider importing and using the following plugins: @upit/tween.v1, @upit/storage.v1
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
No sirven ni queso ni lechuga
User prompt
Queso y lechuga deben ser arrojados a partes iguales con las mismas mecánicas que brocoli
User prompt
Agrega un nuevo tipo de proyectil llamado lechuga
User prompt
Truck debe estar siempre en el centro de la ruta
User prompt
La camioneta debe estar siempre en el centro de la ruta ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
El coche debe seguir el recorrido de la ruta ahora se está saliendo de la carretera ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Los segmentos de la carretera se crean alto y luego bajan al horizonte, deben generarse directamente del horizonte
User prompt
Puedes implementar el sistema de curvas de los arcades tipo hang on ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
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;
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;
// 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.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;
// Keep truck in center of road
self.x = roadCenterAtTruck;
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) {
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);
};
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 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 = storage.currentLevel || 1;
var maxLevel = 5;
var levelThresholdKm = 1000; // Each level requires 1000km
var lastLevelCheck = 0;
var hitsReceived = storage.hitsReceived || 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
var difficultyMultipliers = {
1: {
throwFreq: 1.0,
throwHeight: 1.0,
score: 1.0
},
2: {
throwFreq: 1.2,
throwHeight: 1.1,
score: 1.5
},
3: {
throwFreq: 1.4,
throwHeight: 1.2,
score: 2.0
},
4: {
throwFreq: 1.6,
throwHeight: 1.3,
score: 2.5
},
5: {
throwFreq: 1.8,
throwHeight: 1.4,
score: 3.0
}
};
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 = [];
// 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('Distancia: 0m', {
size: 80,
fill: 0x000000
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Level display text
var levelTxt = new Text2('Nivel: ' + currentLevel, {
size: 60,
fill: 0x000080
});
levelTxt.anchor.set(1, 0);
levelTxt.x = -20;
levelTxt.y = 0;
LK.gui.topRight.addChild(levelTxt);
function checkLevelProgression() {
var currentDistance = Math.floor(roadOffset / 10);
var targetKm = currentLevel * levelThresholdKm;
if (currentDistance >= targetKm && currentLevel < maxLevel) {
// Level completed
currentLevel++;
levelsCompleted++;
storage.currentLevel = currentLevel;
storage.levelsCompleted = levelsCompleted;
// Show level badge
showLevelBadge(currentLevel - 1); // Show badge for completed level
// Play level complete sound
var levelSound = LK.getSound('level_complete');
if (levelSound && levelSound.play) {
levelSound.play();
}
// Update level text
levelTxt.setText('Nivel: ' + currentLevel);
// Calculate and update score based on performance
updateLevelScore();
}
}
function showLevelBadge(completedLevel) {
if (levelBadge) {
LK.gui.center.removeChild(levelBadge);
}
var badgeAsset = 'level' + completedLevel + '_badge';
levelBadge = LK.getAsset(badgeAsset, {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -200,
scaleX: 0.1,
scaleY: 0.1
});
LK.gui.center.addChild(levelBadge);
levelBadgeTimer = levelBadgeDisplayTime;
// Animate badge appearance
tween(levelBadge, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(levelBadge, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeOut
});
}
});
}
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();
}
// 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;
};
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('Distancia de huida: ' + distance + 'm');
LK.setScore(distance);
}
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 && projectile.intersects(motorcycle)) {
// Calculate hit position relative to motorcycle
var motorcycleTop = motorcycle.y - motorcycle.height * motorcycle.scaleY * 0.5;
var motorcycleHeight = motorcycle.height * motorcycle.scaleY;
var hitPosition = (projectile.y - motorcycleTop) / motorcycleHeight;
// Clamp hit position to 0-1 range
hitPosition = Math.max(0, Math.min(1, hitPosition));
if (hitPosition <= 0.33) {
// Hit upper 33% - reduce energy but destroy projectile
var damageAmount = projectile.isHeavy ? 5 : 1; // Reduced damage (33% of original)
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();
}
projectile.destroy();
projectiles.splice(i, 1);
} else {
// Hit lower 66% - bounce the projectile
projectile.velocityY = -Math.abs(projectile.velocityY) * 0.7; // Bounce up with reduced velocity
projectile.velocityX *= 0.8; // Reduce horizontal velocity slightly
projectile.y = motorcycleTop - 10; // Move projectile above motorcycle to prevent multiple collisions
// Play bounce sound
var bounceSound = LK.getSound('hit');
if (bounceSound && bounceSound.play) {
bounceSound.play();
}
}
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);
powerItem.destroy();
powerItems.splice(i, 1);
continue;
}
if (powerItem.y >= extendedRoadY - 20 && powerItem.velocityY > 0) {
powerItem.destroy();
powerItems.splice(i, 1);
}
}
// 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();
}
// 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
}
}
}
}; ===================================================================
--- original.js
+++ change.js
@@ -985,18 +985,17 @@
projectiles.splice(i, 1);
continue;
}
if (gameStarted && projectile.intersects(motorcycle)) {
- // Define motorcycle head hitbox (top 30% of motorcycle)
- var motorcycleTop = motorcycle.y - 120; // Top of motorcycle head
- var motorcycleBottom = motorcycle.y + 40; // Bottom of head area (30% of total height)
- var motorcycleLeft = motorcycle.x - 60; // Left side of head
- var motorcycleRight = motorcycle.x + 60; // Right side of head
- // Check if projectile hits the head area
- var hitHead = projectile.x >= motorcycleLeft && projectile.x <= motorcycleRight && projectile.y >= motorcycleTop && projectile.y <= motorcycleBottom;
- if (hitHead) {
- // Direct head hit - take damage
- var damageAmount = projectile.isHeavy ? 15 : 3;
+ // Calculate hit position relative to motorcycle
+ var motorcycleTop = motorcycle.y - motorcycle.height * motorcycle.scaleY * 0.5;
+ var motorcycleHeight = motorcycle.height * motorcycle.scaleY;
+ var hitPosition = (projectile.y - motorcycleTop) / motorcycleHeight;
+ // Clamp hit position to 0-1 range
+ hitPosition = Math.max(0, Math.min(1, hitPosition));
+ if (hitPosition <= 0.33) {
+ // Hit upper 33% - reduce energy but destroy projectile
+ var damageAmount = projectile.isHeavy ? 5 : 1; // Reduced damage (33% of original)
motorcycle.takeDamage(damageAmount);
var impactSnd;
if (projectile.isHeavy) {
impactSnd = LK.getSound('piedra');
@@ -1013,21 +1012,20 @@
impactSnd.play();
}
projectile.destroy();
projectiles.splice(i, 1);
- continue;
} else {
- // Side hit - bounce the projectile down and away
- var bounceDirection = projectile.x < motorcycle.x ? -1 : 1; // Bounce away from motorcycle
- projectile.velocityX = bounceDirection * (2 + Math.random() * 3); // Random bounce speed
- projectile.velocityY = Math.abs(projectile.velocityY) + 2; // Make sure it falls down
- projectile.gravity = 0.4; // Increase gravity for faster fall
- // Play a softer bounce sound
+ // Hit lower 66% - bounce the projectile
+ projectile.velocityY = -Math.abs(projectile.velocityY) * 0.7; // Bounce up with reduced velocity
+ projectile.velocityX *= 0.8; // Reduce horizontal velocity slightly
+ projectile.y = motorcycleTop - 10; // Move projectile above motorcycle to prevent multiple collisions
+ // Play bounce sound
var bounceSound = LK.getSound('hit');
if (bounceSound && bounceSound.play) {
bounceSound.play();
}
}
+ continue;
}
if (projectile.y >= extendedRoadY - 20 && projectile.velocityY > 0) {
projectile.destroy();
projectiles.splice(i, 1);
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