/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Ball class
var Ball = Container.expand(function () {
var self = Container.call(this);
self.ballSprite = self.attachAsset('football', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = self.ballSprite.width;
self.height = self.ballSprite.height;
self.vx = 0;
self.vy = 0;
self.friction = 0.98;
self.maxSpeed = 38;
self.update = function () {
// Move ball
self.x += self.vx;
self.y += self.vy;
// Friction
self.vx *= self.friction;
self.vy *= self.friction;
// Clamp speed
var speed = Math.sqrt(self.vx * self.vx + self.vy * self.vy);
if (speed > self.maxSpeed) {
self.vx *= self.maxSpeed / speed;
self.vy *= self.maxSpeed / speed;
}
// Bounce off walls
if (self.x < self.width / 2) {
self.x = self.width / 2;
self.vx = -self.vx * 0.7;
}
if (self.x > 2048 - self.width / 2) {
self.x = 2048 - self.width / 2;
self.vx = -self.vx * 0.7;
}
if (self.y < self.height / 2) {
self.y = self.height / 2;
self.vy = -self.vy * 0.7;
}
if (self.y > 2732 - self.height / 2) {
self.y = 2732 - self.height / 2;
self.vy = -self.vy * 0.7;
}
};
return self;
});
// Car class (for both player and AI)
var Car = Container.expand(function () {
var self = Container.call(this);
// Attach car asset (default to player car)
self.carSprite = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = self.carSprite.width;
self.height = self.carSprite.height;
self.speed = 0;
self.maxSpeed = 32;
self.acceleration = 2.2;
self.friction = 0.95;
self.angle = 0; // radians
self.turnSpeed = 0.09;
self.boosting = false;
self.boostPower = 48;
self.boostCooldown = 0;
self.isAI = false;
// For AI
self.targetX = 0;
self.targetY = 0;
self.update = function () {
// AI logic
if (self.isAI) {
// Always try to push the ball into the player's (bottom/green) goal
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
// Target: aim for the ball, but bias target slightly toward the player's goal
var goalBias = 180;
var targetGoalX = goalPlayer.x;
var targetGoalY = goalPlayer.y;
// Calculate a point between the ball and the center of the player's goal
self.targetX = ball.x + (targetGoalX - ball.x) * 0.18;
self.targetY = ball.y + (targetGoalY - ball.y) * 0.18;
// Turn towards target
var desiredAngle = Math.atan2(self.targetY - self.y, self.targetX - self.x);
var angleDiff = desiredAngle - self.angle;
while (angleDiff > Math.PI) angleDiff -= Math.PI * 2;
while (angleDiff < -Math.PI) angleDiff += Math.PI * 2;
if (angleDiff > 0.1) self.angle += self.turnSpeed;else if (angleDiff < -0.1) self.angle -= self.turnSpeed;
// Accelerate if facing target
if (Math.abs(angleDiff) < 0.5) {
self.speed += self.acceleration * 0.7;
}
// AI boost if close to ball and facing it
if (dist < 400 && Math.abs(angleDiff) < 0.3 && self.boostCooldown <= 0) {
self.boosting = true;
}
}
// Boost logic
if (self.boosting && self.boostCooldown <= 0) {
self.speed = self.boostPower;
self.boostCooldown = 60; // 1 second cooldown
self.boosting = false;
}
if (self.boostCooldown > 0) self.boostCooldown--;
// Clamp speed
if (self.speed > self.maxSpeed) self.speed = self.maxSpeed;
if (self.speed < -self.maxSpeed / 2) self.speed = -self.maxSpeed / 2;
// Move car
self.x += Math.cos(self.angle) * self.speed;
self.y += Math.sin(self.angle) * self.speed;
// Friction
self.speed *= self.friction;
// Keep in bounds
if (self.x < self.width / 2) self.x = self.width / 2;
if (self.x > 2048 - self.width / 2) self.x = 2048 - self.width / 2;
if (self.y < self.height / 2) self.y = self.height / 2;
if (self.y > 2732 - self.height / 2) self.y = 2732 - self.height / 2;
// Rotate sprite
self.carSprite.rotation = self.angle;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game state
// Car asset (player)
// Car asset (AI)
// Ball asset
// Goal asset (player)
// Goal asset (AI)
var playerScore = 0;
var aiScore = 0;
var matchTime = 60; // seconds
var timeLeft = matchTime;
var gameActive = true;
// Add background image
var background = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 1,
scaleY: 1
});
game.addChild(background);
// Create goals
var goalPlayer = LK.getAsset('goal_player', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2732 - 80
});
var goalAI = LK.getAsset('goal_ai', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 80
});
game.addChild(goalPlayer);
game.addChild(goalAI);
// Create ball
var ball = new Ball();
game.addChild(ball);
// Create player car
var playerCar = new Car();
playerCar.x = 1024;
playerCar.y = 2732 - 400;
playerCar.angle = -Math.PI / 2;
game.addChild(playerCar);
// Create AI car
var aiCar = new Car();
aiCar.carSprite.destroy();
aiCar.carSprite = aiCar.attachAsset('car_ai', {
anchorX: 0.5,
anchorY: 0.5
});
aiCar.isAI = true;
aiCar.x = 1024;
aiCar.y = 400;
aiCar.angle = Math.PI / 2;
game.addChild(aiCar);
// Score display
var scoreTxt = new Text2('0 : 0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Timer display
var timerTxt = new Text2('60', {
size: 90,
fill: 0xFFFF00
});
timerTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timerTxt);
timerTxt.y = 120;
// Helper: Reset ball and cars to center
function resetPositions(scoredBy) {
ball.x = 1024;
ball.y = 1366;
ball.vx = 0;
ball.vy = 0;
playerCar.x = 1024;
playerCar.y = 2732 - 400;
playerCar.angle = -Math.PI / 2;
playerCar.speed = 0;
aiCar.x = 1024;
aiCar.y = 400;
aiCar.angle = Math.PI / 2;
aiCar.speed = 0;
// Small pause after goal
gameActive = false;
tween(ball, {
alpha: 0.2
}, {
duration: 200,
onFinish: function onFinish() {
tween(ball, {
alpha: 1
}, {
duration: 200,
onFinish: function onFinish() {
gameActive = true;
}
});
}
});
}
// Touch controls
var touchActive = false;
var touchStartX = 0,
touchStartY = 0;
var touchCurX = 0,
touchCurY = 0;
var boostActive = false;
// Touch: steer by dragging, boost by double tap or long press
game.down = function (x, y, obj) {
// Ignore if not active
if (!gameActive) return;
// Don't allow touch in top left 100x100
if (x < 100 && y < 100) return;
touchActive = true;
touchStartX = x;
touchStartY = y;
touchCurX = x;
touchCurY = y;
boostActive = false;
// Detect long press for boost
playerCar.boosting = false;
playerCar._boostTimeout = LK.setTimeout(function () {
boostActive = true;
playerCar.boosting = true;
}, 400);
};
game.move = function (x, y, obj) {
if (!gameActive) return;
if (!touchActive) return;
touchCurX = x;
touchCurY = y;
// Steer: angle towards drag direction
var dx = touchCurX - playerCar.x;
var dy = touchCurY - playerCar.y;
var desiredAngle = Math.atan2(dy, dx);
var angleDiff = desiredAngle - playerCar.angle;
while (angleDiff > Math.PI) angleDiff -= Math.PI * 2;
while (angleDiff < -Math.PI) angleDiff += Math.PI * 2;
if (angleDiff > 0.1) playerCar.angle += playerCar.turnSpeed;else if (angleDiff < -0.1) playerCar.angle -= playerCar.turnSpeed;
// Accelerate if facing drag direction
if (Math.abs(angleDiff) < 0.5) {
playerCar.speed += playerCar.acceleration;
}
// Clamp
if (playerCar.speed > playerCar.maxSpeed) playerCar.speed = playerCar.maxSpeed;
};
game.up = function (x, y, obj) {
touchActive = false;
if (playerCar._boostTimeout) {
LK.clearTimeout(playerCar._boostTimeout);
playerCar._boostTimeout = null;
}
if (boostActive) {
boostActive = false;
}
};
// Double tap for boost
var lastTap = 0;
game.down = function (origDown) {
return function (x, y, obj) {
var now = Date.now();
if (now - lastTap < 350) {
// Double tap: boost
playerCar.boosting = true;
}
lastTap = now;
origDown.call(game, x, y, obj);
};
}(game.down);
// Physics: Car-ball collision
function carBallCollision(car, ball) {
var dx = ball.x - car.x;
var dy = ball.y - car.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var minDist = (car.width / 2 + ball.width / 2) * 0.8;
if (dist < minDist) {
// Push ball away
var nx = dx / (dist || 1);
var ny = dy / (dist || 1);
var overlap = minDist - dist;
ball.x += nx * overlap / 2;
ball.y += ny * overlap / 2;
// Ball velocity: add car's speed
var impact = Math.max(8, car.speed * 1.2);
ball.vx += nx * impact;
ball.vy += ny * impact;
// Car bounces a bit
car.speed *= 0.7;
}
}
// Physics: Car-car collision
function carCarCollision(car1, car2) {
var dx = car2.x - car1.x;
var dy = car2.y - car1.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var minDist = (car1.width / 2 + car2.width / 2) * 0.9;
if (dist < minDist) {
var nx = dx / (dist || 1);
var ny = dy / (dist || 1);
var overlap = minDist - dist;
car1.x -= nx * overlap / 2;
car1.y -= ny * overlap / 2;
car2.x += nx * overlap / 2;
car2.y += ny * overlap / 2;
// Exchange some speed
var temp = car1.speed;
car1.speed = car2.speed * 0.7;
car2.speed = temp * 0.7;
}
}
// Goal detection
function checkGoal() {
// Player goal (top)
if (ball.y - ball.height / 2 < goalAI.y + goalAI.height / 2 && ball.x > goalAI.x - goalAI.width / 2 && ball.x < goalAI.x + goalAI.width / 2) {
playerScore++;
scoreTxt.setText(playerScore + " : " + aiScore);
LK.effects.flashScreen(0x00ff00, 500);
resetPositions('player');
}
// AI goal (bottom)
if (ball.y + ball.height / 2 > goalPlayer.y - goalPlayer.height / 2 && ball.x > goalPlayer.x - goalPlayer.width / 2 && ball.x < goalPlayer.x + goalPlayer.width / 2) {
aiScore++;
scoreTxt.setText(playerScore + " : " + aiScore);
LK.effects.flashScreen(0xff0000, 500);
resetPositions('ai');
}
}
// Timer
var timerInterval = LK.setInterval(function () {
if (!gameActive) return;
if (timeLeft > 0) {
timeLeft--;
timerTxt.setText(timeLeft + "");
if (timeLeft <= 10) timerTxt.setText(timeLeft + "!");
}
if (timeLeft <= 0) {
// End game
gameActive = false;
if (playerScore > aiScore) {
LK.showYouWin();
} else if (aiScore > playerScore) {
LK.showGameOver();
} else {
// Draw: treat as loss for now
LK.showGameOver();
}
}
}, 1000);
// Main update loop
game.update = function () {
if (!gameActive) return;
// Update cars and ball
playerCar.update();
aiCar.update();
ball.update();
// Collisions
carBallCollision(playerCar, ball);
carBallCollision(aiCar, ball);
carCarCollision(playerCar, aiCar);
// Goal check
checkGoal();
};
// Initial positions
resetPositions();
/* End of game code */ /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Ball class
var Ball = Container.expand(function () {
var self = Container.call(this);
self.ballSprite = self.attachAsset('football', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = self.ballSprite.width;
self.height = self.ballSprite.height;
self.vx = 0;
self.vy = 0;
self.friction = 0.98;
self.maxSpeed = 38;
self.update = function () {
// Move ball
self.x += self.vx;
self.y += self.vy;
// Friction
self.vx *= self.friction;
self.vy *= self.friction;
// Clamp speed
var speed = Math.sqrt(self.vx * self.vx + self.vy * self.vy);
if (speed > self.maxSpeed) {
self.vx *= self.maxSpeed / speed;
self.vy *= self.maxSpeed / speed;
}
// Bounce off walls
if (self.x < self.width / 2) {
self.x = self.width / 2;
self.vx = -self.vx * 0.7;
}
if (self.x > 2048 - self.width / 2) {
self.x = 2048 - self.width / 2;
self.vx = -self.vx * 0.7;
}
if (self.y < self.height / 2) {
self.y = self.height / 2;
self.vy = -self.vy * 0.7;
}
if (self.y > 2732 - self.height / 2) {
self.y = 2732 - self.height / 2;
self.vy = -self.vy * 0.7;
}
};
return self;
});
// Car class (for both player and AI)
var Car = Container.expand(function () {
var self = Container.call(this);
// Attach car asset (default to player car)
self.carSprite = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = self.carSprite.width;
self.height = self.carSprite.height;
self.speed = 0;
self.maxSpeed = 32;
self.acceleration = 2.2;
self.friction = 0.95;
self.angle = 0; // radians
self.turnSpeed = 0.09;
self.boosting = false;
self.boostPower = 48;
self.boostCooldown = 0;
self.isAI = false;
// For AI
self.targetX = 0;
self.targetY = 0;
self.update = function () {
// AI logic
if (self.isAI) {
// Always try to push the ball into the player's (bottom/green) goal
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
// Target: aim for the ball, but bias target slightly toward the player's goal
var goalBias = 180;
var targetGoalX = goalPlayer.x;
var targetGoalY = goalPlayer.y;
// Calculate a point between the ball and the center of the player's goal
self.targetX = ball.x + (targetGoalX - ball.x) * 0.18;
self.targetY = ball.y + (targetGoalY - ball.y) * 0.18;
// Turn towards target
var desiredAngle = Math.atan2(self.targetY - self.y, self.targetX - self.x);
var angleDiff = desiredAngle - self.angle;
while (angleDiff > Math.PI) angleDiff -= Math.PI * 2;
while (angleDiff < -Math.PI) angleDiff += Math.PI * 2;
if (angleDiff > 0.1) self.angle += self.turnSpeed;else if (angleDiff < -0.1) self.angle -= self.turnSpeed;
// Accelerate if facing target
if (Math.abs(angleDiff) < 0.5) {
self.speed += self.acceleration * 0.7;
}
// AI boost if close to ball and facing it
if (dist < 400 && Math.abs(angleDiff) < 0.3 && self.boostCooldown <= 0) {
self.boosting = true;
}
}
// Boost logic
if (self.boosting && self.boostCooldown <= 0) {
self.speed = self.boostPower;
self.boostCooldown = 60; // 1 second cooldown
self.boosting = false;
}
if (self.boostCooldown > 0) self.boostCooldown--;
// Clamp speed
if (self.speed > self.maxSpeed) self.speed = self.maxSpeed;
if (self.speed < -self.maxSpeed / 2) self.speed = -self.maxSpeed / 2;
// Move car
self.x += Math.cos(self.angle) * self.speed;
self.y += Math.sin(self.angle) * self.speed;
// Friction
self.speed *= self.friction;
// Keep in bounds
if (self.x < self.width / 2) self.x = self.width / 2;
if (self.x > 2048 - self.width / 2) self.x = 2048 - self.width / 2;
if (self.y < self.height / 2) self.y = self.height / 2;
if (self.y > 2732 - self.height / 2) self.y = 2732 - self.height / 2;
// Rotate sprite
self.carSprite.rotation = self.angle;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game state
// Car asset (player)
// Car asset (AI)
// Ball asset
// Goal asset (player)
// Goal asset (AI)
var playerScore = 0;
var aiScore = 0;
var matchTime = 60; // seconds
var timeLeft = matchTime;
var gameActive = true;
// Add background image
var background = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 1,
scaleY: 1
});
game.addChild(background);
// Create goals
var goalPlayer = LK.getAsset('goal_player', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2732 - 80
});
var goalAI = LK.getAsset('goal_ai', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 80
});
game.addChild(goalPlayer);
game.addChild(goalAI);
// Create ball
var ball = new Ball();
game.addChild(ball);
// Create player car
var playerCar = new Car();
playerCar.x = 1024;
playerCar.y = 2732 - 400;
playerCar.angle = -Math.PI / 2;
game.addChild(playerCar);
// Create AI car
var aiCar = new Car();
aiCar.carSprite.destroy();
aiCar.carSprite = aiCar.attachAsset('car_ai', {
anchorX: 0.5,
anchorY: 0.5
});
aiCar.isAI = true;
aiCar.x = 1024;
aiCar.y = 400;
aiCar.angle = Math.PI / 2;
game.addChild(aiCar);
// Score display
var scoreTxt = new Text2('0 : 0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Timer display
var timerTxt = new Text2('60', {
size: 90,
fill: 0xFFFF00
});
timerTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timerTxt);
timerTxt.y = 120;
// Helper: Reset ball and cars to center
function resetPositions(scoredBy) {
ball.x = 1024;
ball.y = 1366;
ball.vx = 0;
ball.vy = 0;
playerCar.x = 1024;
playerCar.y = 2732 - 400;
playerCar.angle = -Math.PI / 2;
playerCar.speed = 0;
aiCar.x = 1024;
aiCar.y = 400;
aiCar.angle = Math.PI / 2;
aiCar.speed = 0;
// Small pause after goal
gameActive = false;
tween(ball, {
alpha: 0.2
}, {
duration: 200,
onFinish: function onFinish() {
tween(ball, {
alpha: 1
}, {
duration: 200,
onFinish: function onFinish() {
gameActive = true;
}
});
}
});
}
// Touch controls
var touchActive = false;
var touchStartX = 0,
touchStartY = 0;
var touchCurX = 0,
touchCurY = 0;
var boostActive = false;
// Touch: steer by dragging, boost by double tap or long press
game.down = function (x, y, obj) {
// Ignore if not active
if (!gameActive) return;
// Don't allow touch in top left 100x100
if (x < 100 && y < 100) return;
touchActive = true;
touchStartX = x;
touchStartY = y;
touchCurX = x;
touchCurY = y;
boostActive = false;
// Detect long press for boost
playerCar.boosting = false;
playerCar._boostTimeout = LK.setTimeout(function () {
boostActive = true;
playerCar.boosting = true;
}, 400);
};
game.move = function (x, y, obj) {
if (!gameActive) return;
if (!touchActive) return;
touchCurX = x;
touchCurY = y;
// Steer: angle towards drag direction
var dx = touchCurX - playerCar.x;
var dy = touchCurY - playerCar.y;
var desiredAngle = Math.atan2(dy, dx);
var angleDiff = desiredAngle - playerCar.angle;
while (angleDiff > Math.PI) angleDiff -= Math.PI * 2;
while (angleDiff < -Math.PI) angleDiff += Math.PI * 2;
if (angleDiff > 0.1) playerCar.angle += playerCar.turnSpeed;else if (angleDiff < -0.1) playerCar.angle -= playerCar.turnSpeed;
// Accelerate if facing drag direction
if (Math.abs(angleDiff) < 0.5) {
playerCar.speed += playerCar.acceleration;
}
// Clamp
if (playerCar.speed > playerCar.maxSpeed) playerCar.speed = playerCar.maxSpeed;
};
game.up = function (x, y, obj) {
touchActive = false;
if (playerCar._boostTimeout) {
LK.clearTimeout(playerCar._boostTimeout);
playerCar._boostTimeout = null;
}
if (boostActive) {
boostActive = false;
}
};
// Double tap for boost
var lastTap = 0;
game.down = function (origDown) {
return function (x, y, obj) {
var now = Date.now();
if (now - lastTap < 350) {
// Double tap: boost
playerCar.boosting = true;
}
lastTap = now;
origDown.call(game, x, y, obj);
};
}(game.down);
// Physics: Car-ball collision
function carBallCollision(car, ball) {
var dx = ball.x - car.x;
var dy = ball.y - car.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var minDist = (car.width / 2 + ball.width / 2) * 0.8;
if (dist < minDist) {
// Push ball away
var nx = dx / (dist || 1);
var ny = dy / (dist || 1);
var overlap = minDist - dist;
ball.x += nx * overlap / 2;
ball.y += ny * overlap / 2;
// Ball velocity: add car's speed
var impact = Math.max(8, car.speed * 1.2);
ball.vx += nx * impact;
ball.vy += ny * impact;
// Car bounces a bit
car.speed *= 0.7;
}
}
// Physics: Car-car collision
function carCarCollision(car1, car2) {
var dx = car2.x - car1.x;
var dy = car2.y - car1.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var minDist = (car1.width / 2 + car2.width / 2) * 0.9;
if (dist < minDist) {
var nx = dx / (dist || 1);
var ny = dy / (dist || 1);
var overlap = minDist - dist;
car1.x -= nx * overlap / 2;
car1.y -= ny * overlap / 2;
car2.x += nx * overlap / 2;
car2.y += ny * overlap / 2;
// Exchange some speed
var temp = car1.speed;
car1.speed = car2.speed * 0.7;
car2.speed = temp * 0.7;
}
}
// Goal detection
function checkGoal() {
// Player goal (top)
if (ball.y - ball.height / 2 < goalAI.y + goalAI.height / 2 && ball.x > goalAI.x - goalAI.width / 2 && ball.x < goalAI.x + goalAI.width / 2) {
playerScore++;
scoreTxt.setText(playerScore + " : " + aiScore);
LK.effects.flashScreen(0x00ff00, 500);
resetPositions('player');
}
// AI goal (bottom)
if (ball.y + ball.height / 2 > goalPlayer.y - goalPlayer.height / 2 && ball.x > goalPlayer.x - goalPlayer.width / 2 && ball.x < goalPlayer.x + goalPlayer.width / 2) {
aiScore++;
scoreTxt.setText(playerScore + " : " + aiScore);
LK.effects.flashScreen(0xff0000, 500);
resetPositions('ai');
}
}
// Timer
var timerInterval = LK.setInterval(function () {
if (!gameActive) return;
if (timeLeft > 0) {
timeLeft--;
timerTxt.setText(timeLeft + "");
if (timeLeft <= 10) timerTxt.setText(timeLeft + "!");
}
if (timeLeft <= 0) {
// End game
gameActive = false;
if (playerScore > aiScore) {
LK.showYouWin();
} else if (aiScore > playerScore) {
LK.showGameOver();
} else {
// Draw: treat as loss for now
LK.showGameOver();
}
}
}, 1000);
// Main update loop
game.update = function () {
if (!gameActive) return;
// Update cars and ball
playerCar.update();
aiCar.update();
ball.update();
// Collisions
carBallCollision(playerCar, ball);
carBallCollision(aiCar, ball);
carCarCollision(playerCar, aiCar);
// Goal check
checkGoal();
};
// Initial positions
resetPositions();
/* End of game code */