/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { playerScore: 0, computerScore: 0, currentRound: 1, totalRounds: 5, difficulty: 1 }); /**** * Classes ****/ var Ball = Container.expand(function () { var self = Container.call(this); var ballGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0; self.direction = { x: 0, y: 0 }; self.isMoving = false; self.curveFactor = 0; self.reset = function () { self.x = 1024; // Center of field horizontally self.y = 1700; // Moved ball closer to the goal self.isMoving = false; self.speed = 0; self.direction = { x: 0, y: 0 }; self.curveFactor = 0; self.scaleX = 1; self.scaleY = 1; }; self.shoot = function (targetX, targetY, power, curve) { if (self.isMoving) return; // Calculate direction vector self.direction.x = targetX - self.x; self.direction.y = targetY - self.y; // Normalize direction vector var length = Math.sqrt(self.direction.x * self.direction.x + self.direction.y * self.direction.y); self.direction.x /= length; self.direction.y /= length; // Set speed based on power (0-100) self.speed = 5 + power * 15 / 100; // Set curve factor (-1 to 1) self.curveFactor = curve; self.isMoving = true; // Play kick sound LK.getSound('kick').play(); }; self.update = function () { if (!self.isMoving) return; // Apply curve to direction (simulating curved shot) if (self.curveFactor !== 0) { var perpX = -self.direction.y; var perpY = self.direction.x; self.direction.x += perpX * self.curveFactor * 0.01; self.direction.y += perpY * self.curveFactor * 0.01; // Renormalize direction after applying curve var length = Math.sqrt(self.direction.x * self.direction.x + self.direction.y * self.direction.y); self.direction.x /= length; self.direction.y /= length; } self.x += self.direction.x * self.speed; self.y += self.direction.y * self.speed; // Simulate perspective by scaling the ball as it moves away var scaleFactor = 1 - (2732 - self.y) / 2732 * 0.5; self.scaleX = scaleFactor; self.scaleY = scaleFactor; // Apply gravity effect to slow down the ball self.speed *= 0.99; // Stop the ball if it's moving too slowly if (self.speed < 0.5) { self.isMoving = false; } }; self.reset(); return self; }); var GameState = Container.expand(function () { var self = Container.call(this); self.STATES = { AIMING: 'aiming', POWER: 'power', SHOOTING: 'shooting', SAVING: 'saving', RESULT: 'result', GAME_OVER: 'gameOver' }; self.currentState = self.STATES.AIMING; self.playerTurn = true; self.setState = function (newState) { self.currentState = newState; }; self.nextState = function () { switch (self.currentState) { case self.STATES.AIMING: self.setState(self.STATES.POWER); break; case self.STATES.POWER: self.setState(self.STATES.SHOOTING); break; case self.STATES.SHOOTING: self.setState(self.STATES.RESULT); break; case self.STATES.SAVING: self.setState(self.STATES.RESULT); break; case self.STATES.RESULT: if (storage.currentRound >= storage.totalRounds) { self.setState(self.STATES.GAME_OVER); } else { self.switchTurn(); self.setState(self.STATES.AIMING); } break; } }; self.switchTurn = function () { self.playerTurn = !self.playerTurn; // Increment round when both players had their turn if (self.playerTurn) { storage.currentRound++; } }; return self; }); var Goal = Container.expand(function () { var self = Container.call(this); var goalGraphics = self.attachAsset('goal', { anchorX: 0.5, anchorY: 0.5 }); self.width = 800; self.height = 300; self.reset = function () { self.x = 1024; // Center of field horizontally self.y = 500; // Top of field }; self.isInGoal = function (x, y) { return x >= self.x - self.width / 2 && x <= self.x + self.width / 2 && y >= self.y - self.height / 2 && y <= self.y + self.height / 2; }; self.reset(); return self; }); var Goalkeeper = Container.expand(function () { var self = Container.call(this); var goalkeeperGraphics = self.attachAsset('goalkeeper', { anchorX: 0.5, anchorY: 0.5 }); self.targetX = 0; self.targetY = 0; self.speed = 15; self.diving = false; self.diveDirection = 0; // -1 left, 0 center, 1 right self.difficulty = 1; self.reset = function () { self.x = 1024; // Center of goal horizontally self.y = 650; // Goal line self.diving = false; self.diveDirection = 0; goalkeeperGraphics.rotation = 0; }; self.dive = function (direction) { if (self.diving) return; self.diving = true; self.diveDirection = direction; // Rotate goalkeeper based on dive direction if (direction < 0) { goalkeeperGraphics.rotation = -Math.PI / 4; // 45 degrees left } else if (direction > 0) { goalkeeperGraphics.rotation = Math.PI / 4; // 45 degrees right } }; self.computerMove = function (ballX, ballY, difficulty) { if (self.diving) return; var predictAccuracy = Math.min(0.5 + difficulty * 0.1, 0.9); // Higher difficulty = better prediction var randomFactor = Math.random(); if (randomFactor < predictAccuracy) { // AI predicts correctly where the ball will go self.targetX = ballX; } else { // AI makes a random guess self.targetX = 1024 + (Math.random() * 600 - 300); } // Determine dive direction var diveDirection = 0; if (self.targetX < self.x - 100) diveDirection = -1;else if (self.targetX > self.x + 100) diveDirection = 1; // Dive after a short reaction time based on difficulty var reactionTime = Math.max(300 - difficulty * 30, 100); LK.setTimeout(function () { self.dive(diveDirection); }, reactionTime); }; self.update = function () { if (!self.diving) return; // Move goalkeeper during dive self.x += self.speed * self.diveDirection; // Limit goalkeeper movement within goal area if (self.x < 700) self.x = 700; if (self.x > 1348) self.x = 1348; }; self.reset(); return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.reset = function () { self.x = 1024; // Center of field horizontally self.y = 1800; // Moved player closer to the goal }; self.celebrate = function () { // Make player jump in celebration tween(self, { y: self.y - 100 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { y: 1800 }, { duration: 400, easing: tween.easeIn }); } }); }; self.disappointment = function () { // Make player slump down in disappointment tween(self, { scaleY: 0.8 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleY: 1 }, { duration: 500, easing: tween.easeIn }); } }); }; self.reset(); return self; }); var PowerMeter = Container.expand(function () { var self = Container.call(this); var background = self.attachAsset('power_meter', { anchorX: 0.5, anchorY: 0.5 }); var indicator = self.attachAsset('power_indicator', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 140 // Start at bottom of power meter }); self.power = 0; self.increasing = true; self.active = false; self.startMeter = function () { self.power = 0; self.increasing = true; self.active = true; indicator.y = 140; // Reset to bottom }; self.stopMeter = function () { self.active = false; return self.power; }; self.update = function () { if (!self.active) return; if (self.increasing) { self.power += 2; if (self.power >= 100) { self.power = 100; self.increasing = false; } } else { self.power -= 2; if (self.power <= 0) { self.power = 0; self.increasing = true; } } // Update indicator position indicator.y = 140 - self.power * 280 / 100; }; self.reset = function () { self.x = 200; self.y = 1366; // Center vertically self.power = 0; self.active = false; indicator.y = 140; // Reset to bottom }; self.reset(); return self; }); var ScoreDisplay = Container.expand(function () { var self = Container.call(this); // Score text self.scoreText = new Text2('0 - 0', { size: 100, fill: 0xFFFFFF }); self.scoreText.anchor.set(0.5, 0); self.addChild(self.scoreText); // Round text self.roundText = new Text2('Round 1/5', { size: 60, fill: 0xFFFFFF }); self.roundText.anchor.set(0.5, 0); self.roundText.y = 120; self.addChild(self.roundText); self.updateScore = function (playerScore, computerScore) { self.scoreText.setText(playerScore + ' - ' + computerScore); }; self.updateRound = function (currentRound, totalRounds) { self.roundText.setText('Round ' + currentRound + '/' + totalRounds); }; return self; }); var Spectator = Container.expand(function () { var self = Container.call(this); // Create a simple spectator shape var colors = [0x3366CC, 0xFF6633, 0xFFFF33, 0x33CC33, 0xFF33CC, 0x33CCFF]; var color = colors[Math.floor(Math.random() * colors.length)]; var spectatorGraphics = self.attachAsset('character', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5 + Math.random() * 0.3, scaleY: 0.5 + Math.random() * 0.3, tint: color }); // Add random animation for cheering self.cheer = function () { if (Math.random() > 0.7) { tween(self, { y: self.y - 20 }, { duration: 200 + Math.random() * 300, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { y: self.y + 20 }, { duration: 200 + Math.random() * 300, easing: tween.easeIn }); } }); } }; // Update method for random movements self.update = function () { if (Math.random() < 0.01) { self.cheer(); } }; return self; }); var Target = Container.expand(function () { var self = Container.call(this); var targetGraphics = self.attachAsset('target', { anchorX: 0.5, anchorY: 0.5 }); self.visible = false; self.show = function (x, y) { self.x = x; self.y = y; self.visible = true; // Pulse animation self.scale.set(0.7, 0.7); tween(self.scale, { x: 1, y: 1 }, { duration: 300, easing: tween.easeOut }); }; self.hide = function () { self.visible = false; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x33AA33 }); /**** * Game Code ****/ // Play background crowd noise LK.playMusic('background_crowd', { volume: 0.3 }); // Create background field var background = LK.getAsset('bg_field', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366 }); game.addChild(background); // Create game objects var gameState = new GameState(); var ball = new Ball(); var goalkeeper = new Goalkeeper(); var goal = new Goal(); var player = new Player(); var powerMeter = new PowerMeter(); var target = new Target(); // Create spectator crowds on both sides var leftCrowd = new Container(); var rightCrowd = new Container(); game.addChild(leftCrowd); game.addChild(rightCrowd); // Generate spectators for left side for (var i = 0; i < 40; i++) { var spectator = new Spectator(); spectator.x = 100 + Math.random() * 300; spectator.y = 200 + Math.random() * 2000; leftCrowd.addChild(spectator); } // Generate spectators for right side for (var i = 0; i < 40; i++) { var spectator = new Spectator(); spectator.x = 1624 + Math.random() * 300; spectator.y = 200 + Math.random() * 2000; rightCrowd.addChild(spectator); } // Add objects to game game.addChild(goal); game.addChild(goalkeeper); game.addChild(player); game.addChild(ball); game.addChild(powerMeter); game.addChild(target); // Create UI score display var scoreDisplay = new ScoreDisplay(); LK.gui.top.addChild(scoreDisplay); scoreDisplay.updateScore(storage.playerScore, storage.computerScore); scoreDisplay.updateRound(storage.currentRound, storage.totalRounds); // Game status text var statusText = new Text2('Tap where you want to aim', { size: 60, fill: 0xFFFFFF }); statusText.anchor.set(0.5, 0); LK.gui.center.addChild(statusText); statusText.y = -200; // Instructions text var instructionText = new Text2('', { size: 50, fill: 0xFFFF00 }); instructionText.anchor.set(0.5, 0); LK.gui.center.addChild(instructionText); instructionText.y = -120; // Reset the game and prepare for a new round function resetRound() { ball.reset(); goalkeeper.reset(); player.reset(); powerMeter.reset(); target.hide(); if (gameState.playerTurn) { // Player is shooter statusText.setText('Tap where you want to aim'); instructionText.setText('Choose a spot inside the goal'); gameState.setState(gameState.STATES.AIMING); } else { // Player is goalkeeper statusText.setText('Get ready to save!'); instructionText.setText('Swipe in the direction you want to dive'); gameState.setState(gameState.STATES.SAVING); // Computer shoots after a delay LK.setTimeout(function () { if (gameState.currentState === gameState.STATES.SAVING) { computerShoot(); } }, 2000); } scoreDisplay.updateScore(storage.playerScore, storage.computerScore); scoreDisplay.updateRound(storage.currentRound, storage.totalRounds); } // Computer AI shoots the ball function computerShoot() { // Computer chooses a random target inside the goal var targetX = goal.x + (Math.random() * 600 - 300); var targetY = goal.y + (Math.random() * 200 - 100); // Calculate a random power between var power = 50 + Math.random() * 40; // Calculate a random curve factor var curve = Math.random() * 0.6 - 0.3; // Show target briefly target.show(targetX, targetY); // Shoot the ball ball.shoot(targetX, targetY, power, curve); statusText.setText('Try to save it!'); instructionText.setText('Swipe left or right to dive'); } // Check if the ball is saved by the goalkeeper function checkSave() { // Calculate distance between ball and goalkeeper var dx = ball.x - goalkeeper.x; var dy = ball.y - goalkeeper.y; var distance = Math.sqrt(dx * dx + dy * dy); // Check if goalkeeper saved the ball (within his reach) var saved = distance < 150; if (saved) { // Play save sound LK.getSound('save').play(); // Flash effect to show the save LK.effects.flashObject(goalkeeper, 0xFFFFFF, 500); statusText.setText('SAVED!'); if (gameState.playerTurn) { // Computer saved player's shot instructionText.setText('The goalkeeper saved your shot!'); } else { // Player saved computer's shot instructionText.setText('Great save!'); storage.playerScore++; } } else if (goal.isInGoal(ball.x, ball.y)) { // Ball went in the goal - it's a score! LK.getSound('goal').play(); statusText.setText('GOAL!'); if (gameState.playerTurn) { // Player scored instructionText.setText('You scored!'); storage.playerScore++; player.celebrate(); // Make crowd cheer for (var i = 0; i < leftCrowd.children.length; i++) { if (leftCrowd.children[i].cheer) { leftCrowd.children[i].cheer(); } } for (var i = 0; i < rightCrowd.children.length; i++) { if (rightCrowd.children[i].cheer) { rightCrowd.children[i].cheer(); } } } else { // Computer scored instructionText.setText('They scored past you!'); storage.computerScore++; // Make crowd cheer for (var i = 0; i < leftCrowd.children.length; i++) { if (leftCrowd.children[i].cheer) { leftCrowd.children[i].cheer(); } } for (var i = 0; i < rightCrowd.children.length; i++) { if (rightCrowd.children[i].cheer) { rightCrowd.children[i].cheer(); } } } } else { // Ball missed the goal LK.getSound('miss').play(); statusText.setText('MISSED!'); if (gameState.playerTurn) { // Player missed instructionText.setText('Your shot went wide!'); player.disappointment(); } else { // Computer missed instructionText.setText('Their shot went wide!'); } } // Update score display scoreDisplay.updateScore(storage.playerScore, storage.computerScore); // Move to next state after a delay LK.setTimeout(function () { gameState.nextState(); if (gameState.currentState === gameState.STATES.GAME_OVER) { endGame(); } else { resetRound(); } }, 2000); } // End the game and show final result function endGame() { var resultText = ''; if (storage.playerScore > storage.computerScore) { resultText = 'You Win! ' + storage.playerScore + ' - ' + storage.computerScore; LK.showYouWin(); } else if (storage.playerScore < storage.computerScore) { resultText = 'You Lose! ' + storage.playerScore + ' - ' + storage.computerScore; LK.showGameOver(); } else { resultText = 'It\'s a Draw! ' + storage.playerScore + ' - ' + storage.computerScore; LK.showYouWin(); } statusText.setText(resultText); instructionText.setText('Game Over'); // Reset game for next round storage.playerScore = 0; storage.computerScore = 0; storage.currentRound = 1; } // Game click/tap handler game.down = function (x, y, obj) { if (gameState.currentState === gameState.STATES.AIMING && gameState.playerTurn) { // Only allow aiming inside the goal if (goal.isInGoal(x, y)) { target.show(x, y); statusText.setText('Set your power'); instructionText.setText('Tap to stop the power meter'); gameState.nextState(); powerMeter.startMeter(); } } else if (gameState.currentState === gameState.STATES.POWER && gameState.playerTurn) { var power = powerMeter.stopMeter(); statusText.setText('Shooting!'); instructionText.setText(''); // Shoot the ball with random curve var curve = Math.random() * 0.4 - 0.2; ball.shoot(target.x, target.y, power, curve); // Computer goalkeeper tries to save goalkeeper.computerMove(target.x, target.y, storage.difficulty); gameState.nextState(); } else if (gameState.currentState === gameState.STATES.SAVING && !gameState.playerTurn) { // Record start of swipe for goalkeeper dive game.swipeStartX = x; game.swipeStartY = y; } }; // Game drag release handler game.up = function (x, y, obj) { if (gameState.currentState === gameState.STATES.SAVING && !gameState.playerTurn && game.swipeStartX) { // Calculate swipe direction var swipeDx = x - game.swipeStartX; var swipeThreshold = 50; var diveDirection = 0; if (swipeDx < -swipeThreshold) diveDirection = -1;else if (swipeDx > swipeThreshold) diveDirection = 1; // Make goalkeeper dive goalkeeper.dive(diveDirection); // Reset swipe tracking game.swipeStartX = null; } }; // Main game update loop game.update = function () { // Update game objects ball.update(); goalkeeper.update(); powerMeter.update(); // Update spectators for (var i = 0; i < leftCrowd.children.length; i++) { if (leftCrowd.children[i].update) { leftCrowd.children[i].update(); } } for (var i = 0; i < rightCrowd.children.length; i++) { if (rightCrowd.children[i].update) { rightCrowd.children[i].update(); } } // Handle state-specific logic if (gameState.currentState === gameState.STATES.SHOOTING || gameState.currentState === gameState.STATES.SAVING) { // Check if ball has stopped moving if (ball.isMoving === false && ball.y < 1000) { gameState.setState(gameState.STATES.RESULT); checkSave(); } // Check if ball has gone far off screen if (ball.y < -200 || ball.x < -200 || ball.x > 2248) { ball.isMoving = false; gameState.setState(gameState.STATES.RESULT); checkSave(); } } }; // Initialize the game with first round resetRound();
===================================================================
--- original.js
+++ change.js
@@ -27,9 +27,9 @@
self.isMoving = false;
self.curveFactor = 0;
self.reset = function () {
self.x = 1024; // Center of field horizontally
- self.y = 1900; // Moved ball even closer to the goal
+ self.y = 1700; // Moved ball closer to the goal
self.isMoving = false;
self.speed = 0;
self.direction = {
x: 0,
@@ -68,9 +68,8 @@
var length = Math.sqrt(self.direction.x * self.direction.x + self.direction.y * self.direction.y);
self.direction.x /= length;
self.direction.y /= length;
}
- // Move ball
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
// Simulate perspective by scaling the ball as it moves away
var scaleFactor = 1 - (2732 - self.y) / 2732 * 0.5;
@@ -220,9 +219,9 @@
anchorY: 0.5
});
self.reset = function () {
self.x = 1024; // Center of field horizontally
- self.y = 2000; // Moved player even closer to the goal
+ self.y = 1800; // Moved player closer to the goal
};
self.celebrate = function () {
// Make player jump in celebration
tween(self, {
@@ -231,9 +230,9 @@
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
- y: 2000
+ y: 1800
}, {
duration: 400,
easing: tween.easeIn
});
@@ -337,8 +336,47 @@
self.roundText.setText('Round ' + currentRound + '/' + totalRounds);
};
return self;
});
+var Spectator = Container.expand(function () {
+ var self = Container.call(this);
+ // Create a simple spectator shape
+ var colors = [0x3366CC, 0xFF6633, 0xFFFF33, 0x33CC33, 0xFF33CC, 0x33CCFF];
+ var color = colors[Math.floor(Math.random() * colors.length)];
+ var spectatorGraphics = self.attachAsset('character', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 0.5 + Math.random() * 0.3,
+ scaleY: 0.5 + Math.random() * 0.3,
+ tint: color
+ });
+ // Add random animation for cheering
+ self.cheer = function () {
+ if (Math.random() > 0.7) {
+ tween(self, {
+ y: self.y - 20
+ }, {
+ duration: 200 + Math.random() * 300,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ tween(self, {
+ y: self.y + 20
+ }, {
+ duration: 200 + Math.random() * 300,
+ easing: tween.easeIn
+ });
+ }
+ });
+ }
+ };
+ // Update method for random movements
+ self.update = function () {
+ if (Math.random() < 0.01) {
+ self.cheer();
+ }
+ };
+ return self;
+});
var Target = Container.expand(function () {
var self = Container.call(this);
var targetGraphics = self.attachAsset('target', {
anchorX: 0.5,
@@ -394,8 +432,27 @@
var goal = new Goal();
var player = new Player();
var powerMeter = new PowerMeter();
var target = new Target();
+// Create spectator crowds on both sides
+var leftCrowd = new Container();
+var rightCrowd = new Container();
+game.addChild(leftCrowd);
+game.addChild(rightCrowd);
+// Generate spectators for left side
+for (var i = 0; i < 40; i++) {
+ var spectator = new Spectator();
+ spectator.x = 100 + Math.random() * 300;
+ spectator.y = 200 + Math.random() * 2000;
+ leftCrowd.addChild(spectator);
+}
+// Generate spectators for right side
+for (var i = 0; i < 40; i++) {
+ var spectator = new Spectator();
+ spectator.x = 1624 + Math.random() * 300;
+ spectator.y = 200 + Math.random() * 2000;
+ rightCrowd.addChild(spectator);
+}
// Add objects to game
game.addChild(goal);
game.addChild(goalkeeper);
game.addChild(player);
@@ -496,12 +553,34 @@
// Player scored
instructionText.setText('You scored!');
storage.playerScore++;
player.celebrate();
+ // Make crowd cheer
+ for (var i = 0; i < leftCrowd.children.length; i++) {
+ if (leftCrowd.children[i].cheer) {
+ leftCrowd.children[i].cheer();
+ }
+ }
+ for (var i = 0; i < rightCrowd.children.length; i++) {
+ if (rightCrowd.children[i].cheer) {
+ rightCrowd.children[i].cheer();
+ }
+ }
} else {
// Computer scored
instructionText.setText('They scored past you!');
storage.computerScore++;
+ // Make crowd cheer
+ for (var i = 0; i < leftCrowd.children.length; i++) {
+ if (leftCrowd.children[i].cheer) {
+ leftCrowd.children[i].cheer();
+ }
+ }
+ for (var i = 0; i < rightCrowd.children.length; i++) {
+ if (rightCrowd.children[i].cheer) {
+ rightCrowd.children[i].cheer();
+ }
+ }
}
} else {
// Ball missed the goal
LK.getSound('miss').play();
@@ -593,8 +672,19 @@
// Update game objects
ball.update();
goalkeeper.update();
powerMeter.update();
+ // Update spectators
+ for (var i = 0; i < leftCrowd.children.length; i++) {
+ if (leftCrowd.children[i].update) {
+ leftCrowd.children[i].update();
+ }
+ }
+ for (var i = 0; i < rightCrowd.children.length; i++) {
+ if (rightCrowd.children[i].update) {
+ rightCrowd.children[i].update();
+ }
+ }
// Handle state-specific logic
if (gameState.currentState === gameState.STATES.SHOOTING || gameState.currentState === gameState.STATES.SAVING) {
// Check if ball has stopped moving
if (ball.isMoving === false && ball.y < 1000) {
soccer ball 2d ingame asset high contrast no shadows. In-Game asset. 2d. High contrast. No shadows
soccer goal 2d ingame asset high contrast no shadows. In-Game asset. 2d. High contrast. No shadows
male soccer goalkeeper with hands up and not holding ball 2d ingame asset high contrast no shadows. In-Game asset. 2d. High contrast. No shadows
target logo 2d ingame asset high contrast no shadows. In-Game asset. 2d. High contrast. No shadows
soccer field 2d ingame asset high contrast no shadows. In-Game asset. 2d. High contrast. No shadows
male soccer striker not holding ball 2d ingame asset high contrast no shadows. In-Game asset. 2d. High contrast. No shadows
spectator character 2d in game asset high contrast no shadows. In-Game asset. 2d. High contrast. No shadows