User prompt
delever package on the right house when player collide with correct house
User prompt
add an arrow indicator to the house where the player should be delivering the package, and also for the package that has to pickup ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
sorry, car front side is left side, switch sprite orientation ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
cars when moving always turn the sprite to that direction, the front side of the sprite is right side ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
prevent house generation to overlap, cars, dogs and player should collide with houses
User prompt
when you click in the screen, player character should follow the mouse instead of teleporting to mouse
User prompt
dog behaviour follows player when get close to him
Code edit (1 edits merged)
Please save this source code
User prompt
Delivery Dash
Initial prompt
Create a simple yet fun 2D pixel art game titled "Delivery Dash". Genre: Action / Arcade / Time Management View: Top-down (like old-school Zelda or GTA 1 style) Main Gameplay Loop: The player controls a delivery person riding a bike or walking around a city map. The objective is to deliver packages to correct houses before time runs out. Houses are randomly placed, and the player must avoid obstacles like cars, dogs, or rival delivery agents. The player earns points based on speed, accuracy, and package condition. Controls: Arrow keys or WASD to move Spacebar or E to deliver a package when standing in front of the right house Optional: Shift for dash (limited stamina) Features: Timer countdown per level Increasing difficulty with new obstacles Power-ups like time extenders, speed boosts, or package protectors Simple scoring system 2-4 progressively harder levels with different city layouts Art Style: Pixel art, colorful and cartoony Sound: Add upbeat chiptune background music and delivery sound effects Make it playable in-browser. Keep code modular and readable in case I want to expand later.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Car = Container.expand(function () { var self = Container.call(this); var carGraphics = self.attachAsset('car', { anchorX: 0.5, anchorY: 0.5 }); self.speedX = (Math.random() - 0.5) * 4; self.speedY = (Math.random() - 0.5) * 4; self.update = function () { self.x += self.speedX; self.y += self.speedY; // Bounce off edges if (self.x < 40 || self.x > 2008) { self.speedX *= -1; } if (self.y < 40 || self.y > 2692) { self.speedY *= -1; } // Keep in bounds self.x = Math.max(40, Math.min(2008, self.x)); self.y = Math.max(40, Math.min(2692, self.y)); }; return self; }); var Dog = Container.expand(function () { var self = Container.call(this); var dogGraphics = self.attachAsset('dog', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; self.direction = Math.random() * Math.PI * 2; self.update = function () { // Simple AI - change direction occasionally if (Math.random() < 0.02) { self.direction = Math.random() * Math.PI * 2; } self.x += Math.cos(self.direction) * self.speed; self.y += Math.sin(self.direction) * self.speed; // Bounce off edges if (self.x < 15 || self.x > 2033) { self.direction = Math.PI - self.direction; } if (self.y < 15 || self.y > 2717) { self.direction = -self.direction; } // Keep in bounds self.x = Math.max(15, Math.min(2033, self.x)); self.y = Math.max(15, Math.min(2717, self.y)); }; return self; }); var House = Container.expand(function (houseId) { var self = Container.call(this); var houseGraphics = self.attachAsset('house', { anchorX: 0.5, anchorY: 0.5 }); self.houseId = houseId; self.hasDelivery = false; self.down = function (x, y, obj) { if (player.hasPackage && self.distanceTo(player) < 80) { if (player.targetHouse === self.houseId) { // Correct delivery LK.setScore(LK.getScore() + 100); player.hasPackage = false; player.targetHouse = null; self.hasDelivery = true; packagesDelivered++; // Visual feedback tween(self, { scaleX: 1.3, scaleY: 1.3 }, { duration: 200, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); LK.getSound('delivery').play(); scoreText.setText(LK.getScore()); // Check win condition if (packagesDelivered >= totalPackages) { if (currentLevel < 4) { currentLevel++; startLevel(); } else { LK.showYouWin(); } } } else { // Wrong delivery LK.setScore(Math.max(0, LK.getScore() - 50)); scoreText.setText(LK.getScore()); LK.effects.flashObject(self, 0xff0000, 500); } } }; self.distanceTo = function (other) { var dx = self.x - other.x; var dy = self.y - other.y; return Math.sqrt(dx * dx + dy * dy); }; return self; }); var Package = Container.expand(function () { var self = Container.call(this); var packageGraphics = self.attachAsset('package', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 5; self.hasPackage = false; self.targetHouse = null; return self; }); var PowerUp = Container.expand(function (type) { var self = Container.call(this); var powerupGraphics = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); self.type = type; // 'time', 'speed', 'shield' self.collected = false; // Set different colors for different types if (type === 'time') { powerupGraphics.tint = 0x00FF00; } else if (type === 'speed') { powerupGraphics.tint = 0x0080FF; } else if (type === 'shield') { powerupGraphics.tint = 0xFFFF00; } return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x228B22 }); /**** * Game Code ****/ // Game variables var player; var packages = []; var houses = []; var cars = []; var dogs = []; var powerups = []; var currentLevel = 1; var totalPackages = 3; var packagesDelivered = 0; var gameTime = 60000; // 60 seconds var timeRemaining = gameTime; var dragNode = null; var lastCollisionCheck = {}; // UI elements var scoreText = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); var timeText = new Text2('Time: 60', { size: 60, fill: 0xFFFFFF }); timeText.anchor.set(1, 0); LK.gui.topRight.addChild(timeText); var levelText = new Text2('Level 1', { size: 60, fill: 0xFFFFFF }); levelText.anchor.set(0, 0); LK.gui.topLeft.addChild(levelText); // Create road background for (var i = 0; i < 21; i++) { for (var j = 0; j < 28; j++) { var road = LK.getAsset('road', { x: i * 100, y: j * 100, anchorX: 0, anchorY: 0 }); game.addChild(road); } } function startLevel() { // Clear existing game objects for (var i = packages.length - 1; i >= 0; i--) { packages[i].destroy(); } for (var i = houses.length - 1; i >= 0; i--) { houses[i].destroy(); } for (var i = cars.length - 1; i >= 0; i--) { cars[i].destroy(); } for (var i = dogs.length - 1; i >= 0; i--) { dogs[i].destroy(); } for (var i = powerups.length - 1; i >= 0; i--) { powerups[i].destroy(); } packages = []; houses = []; cars = []; dogs = []; powerups = []; packagesDelivered = 0; // Reset time timeRemaining = gameTime; // Update UI levelText.setText('Level ' + currentLevel); // Create player if not exists if (!player) { player = game.addChild(new Player()); player.x = 1024; player.y = 2366; } player.hasPackage = false; player.targetHouse = null; // Create packages and houses totalPackages = 2 + currentLevel; for (var i = 0; i < totalPackages; i++) { // Create package var pkg = new Package(); pkg.x = 200 + Math.random() * 1600; pkg.y = 200 + Math.random() * 1200; packages.push(pkg); game.addChild(pkg); // Create corresponding house var house = new House(i); house.x = 200 + Math.random() * 1600; house.y = 1500 + Math.random() * 1000; houses.push(house); game.addChild(house); } // Create cars (more with each level) var numCars = currentLevel + 1; for (var i = 0; i < numCars; i++) { var car = new Car(); car.x = Math.random() * 2048; car.y = Math.random() * 2732; cars.push(car); game.addChild(car); } // Create dogs var numDogs = Math.floor(currentLevel / 2) + 1; for (var i = 0; i < numDogs; i++) { var dog = new Dog(); dog.x = Math.random() * 2048; dog.y = Math.random() * 2732; dogs.push(dog); game.addChild(dog); } // Create power-ups var powerupTypes = ['time', 'speed', 'shield']; for (var i = 0; i < 2; i++) { var powerup = new PowerUp(powerupTypes[Math.floor(Math.random() * powerupTypes.length)]); powerup.x = Math.random() * 2048; powerup.y = Math.random() * 2732; powerups.push(powerup); game.addChild(powerup); } } function handleMove(x, y, obj) { if (dragNode) { dragNode.x = x; dragNode.y = y; // Keep player in bounds dragNode.x = Math.max(30, Math.min(2018, dragNode.x)); dragNode.y = Math.max(30, Math.min(2702, dragNode.y)); } } game.move = handleMove; game.down = function (x, y, obj) { // Check if clicking on package to pick up for (var i = 0; i < packages.length; i++) { var pkg = packages[i]; if (!pkg.collected && pkg.intersects && player.intersects(pkg)) { if (!player.hasPackage) { pkg.collected = true; pkg.destroy(); packages.splice(i, 1); player.hasPackage = true; player.targetHouse = packages.length; // Simple targeting system LK.getSound('pickup').play(); return; } } } dragNode = player; handleMove(x, y, obj); }; game.up = function (x, y, obj) { dragNode = null; }; game.update = function () { // Update time timeRemaining -= 16.67; // Approximate 60fps var seconds = Math.ceil(timeRemaining / 1000); timeText.setText('Time: ' + Math.max(0, seconds)); // Check time up if (timeRemaining <= 0) { LK.showGameOver(); return; } // Check package pickup for (var i = packages.length - 1; i >= 0; i--) { var pkg = packages[i]; if (!pkg.collected && player.intersects(pkg)) { if (!player.hasPackage) { pkg.collected = true; pkg.destroy(); packages.splice(i, 1); player.hasPackage = true; player.targetHouse = i; LK.getSound('pickup').play(); } } } // Check powerup collection for (var i = powerups.length - 1; i >= 0; i--) { var powerup = powerups[i]; if (!powerup.collected && player.intersects(powerup)) { powerup.collected = true; powerup.destroy(); powerups.splice(i, 1); if (powerup.type === 'time') { timeRemaining += 10000; // Add 10 seconds } else if (powerup.type === 'speed') { player.speed = 8; LK.setTimeout(function () { player.speed = 5; }, 5000); } LK.getSound('powerup').play(); LK.setScore(LK.getScore() + 25); scoreText.setText(LK.getScore()); } } // Check collisions with obstacles for (var i = 0; i < cars.length; i++) { var car = cars[i]; var carKey = 'car_' + i; var currentCollision = player.intersects(car); if (!lastCollisionCheck[carKey] && currentCollision) { // Just started colliding LK.setScore(Math.max(0, LK.getScore() - 25)); scoreText.setText(LK.getScore()); LK.effects.flashObject(player, 0xff0000, 500); } lastCollisionCheck[carKey] = currentCollision; } for (var i = 0; i < dogs.length; i++) { var dog = dogs[i]; var dogKey = 'dog_' + i; var currentCollision = player.intersects(dog); if (!lastCollisionCheck[dogKey] && currentCollision) { // Just started colliding LK.setScore(Math.max(0, LK.getScore() - 15)); scoreText.setText(LK.getScore()); LK.effects.flashObject(player, 0xff0000, 300); } lastCollisionCheck[dogKey] = currentCollision; } }; // Start the first level startLevel(); // Play background music LK.playMusic('bgmusic');
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,397 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+var Car = Container.expand(function () {
+ var self = Container.call(this);
+ var carGraphics = self.attachAsset('car', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speedX = (Math.random() - 0.5) * 4;
+ self.speedY = (Math.random() - 0.5) * 4;
+ self.update = function () {
+ self.x += self.speedX;
+ self.y += self.speedY;
+ // Bounce off edges
+ if (self.x < 40 || self.x > 2008) {
+ self.speedX *= -1;
+ }
+ if (self.y < 40 || self.y > 2692) {
+ self.speedY *= -1;
+ }
+ // Keep in bounds
+ self.x = Math.max(40, Math.min(2008, self.x));
+ self.y = Math.max(40, Math.min(2692, self.y));
+ };
+ return self;
+});
+var Dog = Container.expand(function () {
+ var self = Container.call(this);
+ var dogGraphics = self.attachAsset('dog', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = 2;
+ self.direction = Math.random() * Math.PI * 2;
+ self.update = function () {
+ // Simple AI - change direction occasionally
+ if (Math.random() < 0.02) {
+ self.direction = Math.random() * Math.PI * 2;
+ }
+ self.x += Math.cos(self.direction) * self.speed;
+ self.y += Math.sin(self.direction) * self.speed;
+ // Bounce off edges
+ if (self.x < 15 || self.x > 2033) {
+ self.direction = Math.PI - self.direction;
+ }
+ if (self.y < 15 || self.y > 2717) {
+ self.direction = -self.direction;
+ }
+ // Keep in bounds
+ self.x = Math.max(15, Math.min(2033, self.x));
+ self.y = Math.max(15, Math.min(2717, self.y));
+ };
+ return self;
+});
+var House = Container.expand(function (houseId) {
+ var self = Container.call(this);
+ var houseGraphics = self.attachAsset('house', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.houseId = houseId;
+ self.hasDelivery = false;
+ self.down = function (x, y, obj) {
+ if (player.hasPackage && self.distanceTo(player) < 80) {
+ if (player.targetHouse === self.houseId) {
+ // Correct delivery
+ LK.setScore(LK.getScore() + 100);
+ player.hasPackage = false;
+ player.targetHouse = null;
+ self.hasDelivery = true;
+ packagesDelivered++;
+ // Visual feedback
+ tween(self, {
+ scaleX: 1.3,
+ scaleY: 1.3
+ }, {
+ duration: 200,
+ onFinish: function onFinish() {
+ tween(self, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 200
+ });
+ }
+ });
+ LK.getSound('delivery').play();
+ scoreText.setText(LK.getScore());
+ // Check win condition
+ if (packagesDelivered >= totalPackages) {
+ if (currentLevel < 4) {
+ currentLevel++;
+ startLevel();
+ } else {
+ LK.showYouWin();
+ }
+ }
+ } else {
+ // Wrong delivery
+ LK.setScore(Math.max(0, LK.getScore() - 50));
+ scoreText.setText(LK.getScore());
+ LK.effects.flashObject(self, 0xff0000, 500);
+ }
+ }
+ };
+ self.distanceTo = function (other) {
+ var dx = self.x - other.x;
+ var dy = self.y - other.y;
+ return Math.sqrt(dx * dx + dy * dy);
+ };
+ return self;
+});
+var Package = Container.expand(function () {
+ var self = Container.call(this);
+ var packageGraphics = self.attachAsset('package', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.collected = false;
+ return self;
+});
+var Player = Container.expand(function () {
+ var self = Container.call(this);
+ var playerGraphics = self.attachAsset('player', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = 5;
+ self.hasPackage = false;
+ self.targetHouse = null;
+ return self;
+});
+var PowerUp = Container.expand(function (type) {
+ var self = Container.call(this);
+ var powerupGraphics = self.attachAsset('powerup', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.type = type; // 'time', 'speed', 'shield'
+ self.collected = false;
+ // Set different colors for different types
+ if (type === 'time') {
+ powerupGraphics.tint = 0x00FF00;
+ } else if (type === 'speed') {
+ powerupGraphics.tint = 0x0080FF;
+ } else if (type === 'shield') {
+ powerupGraphics.tint = 0xFFFF00;
+ }
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x228B22
+});
+
+/****
+* Game Code
+****/
+// Game variables
+var player;
+var packages = [];
+var houses = [];
+var cars = [];
+var dogs = [];
+var powerups = [];
+var currentLevel = 1;
+var totalPackages = 3;
+var packagesDelivered = 0;
+var gameTime = 60000; // 60 seconds
+var timeRemaining = gameTime;
+var dragNode = null;
+var lastCollisionCheck = {};
+// UI elements
+var scoreText = new Text2('Score: 0', {
+ size: 60,
+ fill: 0xFFFFFF
+});
+scoreText.anchor.set(0.5, 0);
+LK.gui.top.addChild(scoreText);
+var timeText = new Text2('Time: 60', {
+ size: 60,
+ fill: 0xFFFFFF
+});
+timeText.anchor.set(1, 0);
+LK.gui.topRight.addChild(timeText);
+var levelText = new Text2('Level 1', {
+ size: 60,
+ fill: 0xFFFFFF
+});
+levelText.anchor.set(0, 0);
+LK.gui.topLeft.addChild(levelText);
+// Create road background
+for (var i = 0; i < 21; i++) {
+ for (var j = 0; j < 28; j++) {
+ var road = LK.getAsset('road', {
+ x: i * 100,
+ y: j * 100,
+ anchorX: 0,
+ anchorY: 0
+ });
+ game.addChild(road);
+ }
+}
+function startLevel() {
+ // Clear existing game objects
+ for (var i = packages.length - 1; i >= 0; i--) {
+ packages[i].destroy();
+ }
+ for (var i = houses.length - 1; i >= 0; i--) {
+ houses[i].destroy();
+ }
+ for (var i = cars.length - 1; i >= 0; i--) {
+ cars[i].destroy();
+ }
+ for (var i = dogs.length - 1; i >= 0; i--) {
+ dogs[i].destroy();
+ }
+ for (var i = powerups.length - 1; i >= 0; i--) {
+ powerups[i].destroy();
+ }
+ packages = [];
+ houses = [];
+ cars = [];
+ dogs = [];
+ powerups = [];
+ packagesDelivered = 0;
+ // Reset time
+ timeRemaining = gameTime;
+ // Update UI
+ levelText.setText('Level ' + currentLevel);
+ // Create player if not exists
+ if (!player) {
+ player = game.addChild(new Player());
+ player.x = 1024;
+ player.y = 2366;
+ }
+ player.hasPackage = false;
+ player.targetHouse = null;
+ // Create packages and houses
+ totalPackages = 2 + currentLevel;
+ for (var i = 0; i < totalPackages; i++) {
+ // Create package
+ var pkg = new Package();
+ pkg.x = 200 + Math.random() * 1600;
+ pkg.y = 200 + Math.random() * 1200;
+ packages.push(pkg);
+ game.addChild(pkg);
+ // Create corresponding house
+ var house = new House(i);
+ house.x = 200 + Math.random() * 1600;
+ house.y = 1500 + Math.random() * 1000;
+ houses.push(house);
+ game.addChild(house);
+ }
+ // Create cars (more with each level)
+ var numCars = currentLevel + 1;
+ for (var i = 0; i < numCars; i++) {
+ var car = new Car();
+ car.x = Math.random() * 2048;
+ car.y = Math.random() * 2732;
+ cars.push(car);
+ game.addChild(car);
+ }
+ // Create dogs
+ var numDogs = Math.floor(currentLevel / 2) + 1;
+ for (var i = 0; i < numDogs; i++) {
+ var dog = new Dog();
+ dog.x = Math.random() * 2048;
+ dog.y = Math.random() * 2732;
+ dogs.push(dog);
+ game.addChild(dog);
+ }
+ // Create power-ups
+ var powerupTypes = ['time', 'speed', 'shield'];
+ for (var i = 0; i < 2; i++) {
+ var powerup = new PowerUp(powerupTypes[Math.floor(Math.random() * powerupTypes.length)]);
+ powerup.x = Math.random() * 2048;
+ powerup.y = Math.random() * 2732;
+ powerups.push(powerup);
+ game.addChild(powerup);
+ }
+}
+function handleMove(x, y, obj) {
+ if (dragNode) {
+ dragNode.x = x;
+ dragNode.y = y;
+ // Keep player in bounds
+ dragNode.x = Math.max(30, Math.min(2018, dragNode.x));
+ dragNode.y = Math.max(30, Math.min(2702, dragNode.y));
+ }
+}
+game.move = handleMove;
+game.down = function (x, y, obj) {
+ // Check if clicking on package to pick up
+ for (var i = 0; i < packages.length; i++) {
+ var pkg = packages[i];
+ if (!pkg.collected && pkg.intersects && player.intersects(pkg)) {
+ if (!player.hasPackage) {
+ pkg.collected = true;
+ pkg.destroy();
+ packages.splice(i, 1);
+ player.hasPackage = true;
+ player.targetHouse = packages.length; // Simple targeting system
+ LK.getSound('pickup').play();
+ return;
+ }
+ }
+ }
+ dragNode = player;
+ handleMove(x, y, obj);
+};
+game.up = function (x, y, obj) {
+ dragNode = null;
+};
+game.update = function () {
+ // Update time
+ timeRemaining -= 16.67; // Approximate 60fps
+ var seconds = Math.ceil(timeRemaining / 1000);
+ timeText.setText('Time: ' + Math.max(0, seconds));
+ // Check time up
+ if (timeRemaining <= 0) {
+ LK.showGameOver();
+ return;
+ }
+ // Check package pickup
+ for (var i = packages.length - 1; i >= 0; i--) {
+ var pkg = packages[i];
+ if (!pkg.collected && player.intersects(pkg)) {
+ if (!player.hasPackage) {
+ pkg.collected = true;
+ pkg.destroy();
+ packages.splice(i, 1);
+ player.hasPackage = true;
+ player.targetHouse = i;
+ LK.getSound('pickup').play();
+ }
+ }
+ }
+ // Check powerup collection
+ for (var i = powerups.length - 1; i >= 0; i--) {
+ var powerup = powerups[i];
+ if (!powerup.collected && player.intersects(powerup)) {
+ powerup.collected = true;
+ powerup.destroy();
+ powerups.splice(i, 1);
+ if (powerup.type === 'time') {
+ timeRemaining += 10000; // Add 10 seconds
+ } else if (powerup.type === 'speed') {
+ player.speed = 8;
+ LK.setTimeout(function () {
+ player.speed = 5;
+ }, 5000);
+ }
+ LK.getSound('powerup').play();
+ LK.setScore(LK.getScore() + 25);
+ scoreText.setText(LK.getScore());
+ }
+ }
+ // Check collisions with obstacles
+ for (var i = 0; i < cars.length; i++) {
+ var car = cars[i];
+ var carKey = 'car_' + i;
+ var currentCollision = player.intersects(car);
+ if (!lastCollisionCheck[carKey] && currentCollision) {
+ // Just started colliding
+ LK.setScore(Math.max(0, LK.getScore() - 25));
+ scoreText.setText(LK.getScore());
+ LK.effects.flashObject(player, 0xff0000, 500);
+ }
+ lastCollisionCheck[carKey] = currentCollision;
+ }
+ for (var i = 0; i < dogs.length; i++) {
+ var dog = dogs[i];
+ var dogKey = 'dog_' + i;
+ var currentCollision = player.intersects(dog);
+ if (!lastCollisionCheck[dogKey] && currentCollision) {
+ // Just started colliding
+ LK.setScore(Math.max(0, LK.getScore() - 15));
+ scoreText.setText(LK.getScore());
+ LK.effects.flashObject(player, 0xff0000, 300);
+ }
+ lastCollisionCheck[dogKey] = currentCollision;
+ }
+};
+// Start the first level
+startLevel();
+// Play background music
+LK.playMusic('bgmusic');
\ No newline at end of file
shield icon inside of a blue energy bubble. In-Game asset. 2d. High contrast. No shadows
1+ up heart icon. In-Game asset. 2d. High contrast. No shadows
fast foward icon. In-Game asset. 2d. High contrast. No shadows
+10 inside of a clock icon. In-Game asset. 2d. High contrast. No shadows
Street block, garden with concrete walkway on the border. In-Game asset. 2d. High contrast. No shadows