initial commit

This commit is contained in:
Jakub Žitník 2025-02-17 22:16:35 +01:00
commit f0f1047078
Signed by: jzitnik
GPG Key ID: C577A802A6AF4EF3
26 changed files with 801 additions and 0 deletions

38
.gitignore vendored Normal file
View File

@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

3
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

7
.idea/encodings.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

14
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_22" default="true" project-jdk-name="22" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

33
pom.xml Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cz.jzitnik</groupId>
<artifactId>twodcraft</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>22</maven.compiler.source>
<maven.compiler.target>22</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline-reader</artifactId>
<version>3.20.0</version> <!-- or another stable version -->
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,73 @@
package cz.jzitnik;
import cz.jzitnik.game.Game;
import cz.jzitnik.game.SpriteLoader;
import cz.jzitnik.tui.ScreenRenderer;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
try {
// Set up terminal with JLine
Terminal terminal = TerminalBuilder.terminal();
terminal.enterRawMode(); // Switch to raw mode to capture keys immediately
var spriteList = SpriteLoader.load();
var screenRenderer = new ScreenRenderer(spriteList);
var game = new Game();
final boolean[] isRunning = {true};
Thread inputThread = new Thread(() -> {
try {
while (isRunning[0]) {
int key = terminal.reader().read();
switch (key) {
case 'a':
game.movePlayerLeft(screenRenderer);
screenRenderer.render(game.getWorld());
break;
case 'd':
game.movePlayerRight(screenRenderer);
screenRenderer.render(game.getWorld());
break;
case ' ':
game.movePlayerUp(screenRenderer);
screenRenderer.render(game.getWorld());
break;
case 'm':
System.out.println("Mine pressed");
break;
case 'q':
System.out.println("Exiting game...");
isRunning[0] = false;
break;
default:
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
});
inputThread.start();
// Game loop (rendering the game)
while (isRunning[0]) {
screenRenderer.render(game.getWorld());
try {
Thread.sleep(1000); // Control game loop speed
} catch (InterruptedException e) {
e.printStackTrace();
}
}
terminal.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,18 @@
package cz.jzitnik.game;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public class Block {
private String blockId;
private SpriteLoader.SPRITES sprite;
private boolean ghost;
public Block(String blockId, SpriteLoader.SPRITES sprite) {
this.blockId = blockId;
this.sprite = sprite;
this.ghost = false;
}
}

View File

@ -0,0 +1,150 @@
package cz.jzitnik.game;
import cz.jzitnik.tui.ScreenRenderer;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
@Getter
public class Game {
private List<Block>[][] world = new ArrayList[50][50];
private Block player;
public Game() {
for (int i = 0; i < 50; i++) {
for (int j = 0; j < 50; j++) {
world[i][j] = new ArrayList<>();
}
}
Block steveBlock = new Block("steve", SpriteLoader.SPRITES.STEVE);
world[10][2].add(new Block("grass", SpriteLoader.SPRITES.GRASS));
player = steveBlock;
for (int i = 0; i < 50; i++) {
world[11][i].add(new Block("grass", SpriteLoader.SPRITES.GRASS));
world[12][i].add(new Block("dirt", SpriteLoader.SPRITES.DIRT));
world[13][i].add(new Block("dirt", SpriteLoader.SPRITES.DIRT));
world[14][i].add(new Block("dirt", SpriteLoader.SPRITES.DIRT));
for (int j = 15; j < 49; j++) {
world[j][i].add(new Block("stone", SpriteLoader.SPRITES.STONE));
}
world[49][i].add(new Block("bedrock", SpriteLoader.SPRITES.BEDROCK));
}
for (int i = 0; i < world.length; i++) {
for (int j = 0; j < world[i].length; j++) {
if (world[i][j].isEmpty()) {
world[i][j].add(new Block("air", SpriteLoader.SPRITES.AIR, true)); // Fill with air
}
}
}
world[10][0].add(steveBlock);
}
private int[] getPlayerCords() {
for (int i = 0; i < world.length; i++) {
for (int j = 0; j < world[i].length; j++) {
for (Block block : world[i][j]) {
if (block.getBlockId().equals("steve")) {
return new int[]{j, i};
}
}
}
}
return null;
}
public void movePlayerRight(ScreenRenderer screenRenderer) {
int[] cords = getPlayerCords();
if (world[cords[1]][cords[0] + 1].stream().anyMatch(block -> !block.isGhost())) {
return;
}
world[cords[1]][cords[0] + 1].add(player);
world[cords[1]][cords[0]].remove(player);
new Thread(() -> {
while (true) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int[] cords2 = getPlayerCords();
if (world[cords2[1] + 1][cords2[0]].stream().anyMatch(Block::isGhost)) {
world[cords2[1] + 1][cords2[0]].add(player);
world[cords2[1]][cords2[0]].remove(player);
screenRenderer.render(world);
} else {
break;
}
}
}).start();
}
public void movePlayerLeft(ScreenRenderer screenRenderer) {
int[] cords = getPlayerCords();
if (world[cords[1]][cords[0] - 1].stream().anyMatch(block -> !block.isGhost())) {
return;
}
world[cords[1]][cords[0] - 1].add(player);
world[cords[1]][cords[0]].remove(player);
new Thread(() -> {
while (true) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int[] cords2 = getPlayerCords();
if (world[cords2[1] + 1][cords2[0]].stream().anyMatch(Block::isGhost)) {
world[cords2[1] + 1][cords2[0]].add(player);
world[cords2[1]][cords2[0]].remove(player);
screenRenderer.render(world);
} else {
break;
}
}
}).start();
}
public void movePlayerUp(ScreenRenderer screenRenderer) {
int[] cords = getPlayerCords();
if (world[cords[1] - 1][cords[0]].stream().anyMatch(block -> !block.isGhost()) || world[cords[1] + 1][cords[0]].stream().anyMatch(Block::isGhost)) {
return;
}
world[cords[1] - 1][cords[0]].add(player);
world[cords[1]][cords[0]].remove(player);
new Thread(() -> {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int[] cords2 = getPlayerCords();
if (world[cords2[1] + 1][cords2[0]].stream().anyMatch(Block::isGhost)) {
world[cords2[1] + 1][cords2[0]].add(player);
world[cords2[1]][cords2[0]].remove(player);
screenRenderer.render(world);
}
}).start();
}
}

View File

@ -0,0 +1,33 @@
package cz.jzitnik.game;
import cz.jzitnik.game.sprites.*;
import cz.jzitnik.tui.Sprite;
import cz.jzitnik.tui.SpriteList;
import java.util.HashMap;
public class SpriteLoader {
public enum SPRITES {
AIR,
DIRT,
GRASS,
STEVE,
STONE,
BEDROCK
}
public static final HashMap<SPRITES, Sprite> SPRITES_MAP = new HashMap<>();
static {
SPRITES_MAP.put(SPRITES.AIR, new Air());
SPRITES_MAP.put(SPRITES.DIRT, new Dirt());
SPRITES_MAP.put(SPRITES.GRASS, new Grass());
SPRITES_MAP.put(SPRITES.STONE, new Stone());
SPRITES_MAP.put(SPRITES.STEVE, new Steve());
SPRITES_MAP.put(SPRITES.BEDROCK, new Bedrock());
}
public static SpriteList<SPRITES> load() {
return new SpriteList<>(SPRITES.class, SPRITES_MAP);
}
}

View File

@ -0,0 +1,20 @@
package cz.jzitnik.game.sprites;
import cz.jzitnik.tui.Sprite;
public class Air extends Sprite {
public String getSprite() {
StringBuilder sprite = new StringBuilder();
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 50; j++) {
sprite.append("\033[0m ");
}
sprite.append("\n");
}
return sprite.toString();
}
public String getSprite(Enum e) {
throw new RuntimeException("Imposible state");
}
}

View File

@ -0,0 +1,14 @@
package cz.jzitnik.game.sprites;
import cz.jzitnik.tui.ResourceLoader;
import cz.jzitnik.tui.Sprite;
public class Bedrock extends Sprite {
public String getSprite() {
return ResourceLoader.loadResource("bedrock.ans");
}
public String getSprite(Enum key) {
throw new RuntimeException("Imposible state");
}
}

View File

@ -0,0 +1,14 @@
package cz.jzitnik.game.sprites;
import cz.jzitnik.tui.ResourceLoader;
import cz.jzitnik.tui.Sprite;
public class Dirt extends Sprite {
public String getSprite() {
return ResourceLoader.loadResource("dirt.ans");
}
public String getSprite(Enum key) {
throw new RuntimeException("Imposible state");
}
}

View File

@ -0,0 +1,14 @@
package cz.jzitnik.game.sprites;
import cz.jzitnik.tui.ResourceLoader;
import cz.jzitnik.tui.Sprite;
public class Grass extends Sprite {
public String getSprite() {
return ResourceLoader.loadResource("grass.ans");
}
public String getSprite(Enum key) {
throw new RuntimeException("Imposible state");
}
}

View File

@ -0,0 +1,23 @@
package cz.jzitnik.game.sprites;
import cz.jzitnik.tui.ResourceLoader;
import cz.jzitnik.tui.Sprite;
import java.util.Arrays;
import java.util.stream.Collectors;
public class Steve extends Sprite {
private String fix(String x) {
var arr = x.replaceAll("\033\\[38;5;1;48;5;16m", "\033[0m").split("\n");
arr = Arrays.copyOf(arr, arr.length - 1); // Remove the last line
return ("\033[0m ".repeat(50) + "\n").repeat(3) + Arrays.stream(arr).map(y -> "\033[0m ".repeat(12) + y + " " + "\033[0m ".repeat(12) + "\n").collect(Collectors.joining());
}
public String getSprite() {
return fix(ResourceLoader.loadResource("steve.ans"));
}
public String getSprite(Enum e) {
throw new RuntimeException("Imposible state");
}
}

View File

@ -0,0 +1,14 @@
package cz.jzitnik.game.sprites;
import cz.jzitnik.tui.ResourceLoader;
import cz.jzitnik.tui.Sprite;
public class Stone extends Sprite {
public String getSprite() {
return ResourceLoader.loadResource("stone.ans");
}
public String getSprite(Enum key) {
throw new RuntimeException("Imposible state");
}
}

View File

@ -0,0 +1,22 @@
package cz.jzitnik.tui;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
public class ResourceLoader {
public static String loadResource(String fileName) {
try (InputStream inputStream = ResourceLoader.class.getClassLoader().getResourceAsStream("textures/"+fileName)) {
if (inputStream == null) {
// If the file is not found, return null
return null;
}
byte[] bytes = inputStream.readAllBytes();
return new String(bytes, StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,72 @@
package cz.jzitnik.tui;
import cz.jzitnik.game.Block;
import lombok.AllArgsConstructor;
import java.util.List;
@AllArgsConstructor
public class ScreenRenderer {
private SpriteList spriteList;
private int[] getPlayerCords(List<Block>[][] world) {
for (int i = 0; i < world.length; i++) {
for (int j = 0; j < world[i].length; j++) {
if (world[i][j].stream().anyMatch(x -> x.getBlockId().equals("steve"))) {
return new int[]{j, i};
}
}
}
return null;
}
public void render(List<Block>[][] world) {
StringBuilder main = new StringBuilder();
main.append("\033[H\033[2J");
int[] cords = getPlayerCords(world);
if (cords == null) return;
int playerX = cords[0];
int playerY = cords[1];
int viewXRadius = 5;
int viewUpRadius = 4;
int viewDownRadius = 3;
// Calculate visible area boundaries
int startX = Math.max(0, playerX - viewXRadius);
int endX = Math.min(world[0].length, playerX + viewXRadius + 1);
int startY = Math.max(0, playerY - viewUpRadius);
int endY = Math.min(world.length, playerY + viewDownRadius + 1);
StringBuilder[] lines = new StringBuilder[(endY - startY) * 25 + 1];
for (int i = 0; i < lines.length; i++) {
lines[i] = new StringBuilder();
}
int counter = 0;
for (int y = startY; y < endY; y++) {
for (int x = startX; x < endX; x++) {
List<Block> blocks = world[y][x];
String sprite = SpriteCombiner.combineSprites(blocks.stream()
.map(block -> spriteList.getSprite(block.getSprite()).getSprite())
.toArray(String[]::new));
String[] spriteLines = sprite.split("\n");
for (int i = 0; i < spriteLines.length; i++) {
lines[counter * 25 + i].append(spriteLines[i]);
}
}
counter++;
}
for (StringBuilder line : lines) {
main.append(line.toString());
main.append("\n");
}
System.out.println(main);
}
}

View File

@ -0,0 +1,6 @@
package cz.jzitnik.tui;
public abstract class Sprite<E extends Enum<E>> {
public abstract String getSprite();
public abstract String getSprite(E key);
}

View File

@ -0,0 +1,67 @@
package cz.jzitnik.tui;
public class SpriteCombiner {
public static String combineSprites(String[] sprites) {
if (sprites == null || sprites.length == 0) {
return "";
}
String combinedSprite = sprites[0];
for (int i = 1; i < sprites.length; i++) {
combinedSprite = combineTwoSprites(combinedSprite, sprites[i]);
}
return combinedSprite;
}
private static String combineTwoSprites(String sprite1, String sprite2) {
String[] rows1 = sprite1.split("\n");
String[] rows2 = sprite2.split("\n");
StringBuilder combinedSprite = new StringBuilder();
for (int i = 0; i < 25; i++) {
String row1 = rows1[i];
String row2 = rows2[i];
StringBuilder combinedRow = new StringBuilder();
int cursor1 = 0;
int cursor2 = 0;
while (cursor2 < row2.length()) {
String color1 = extractColorCode(row1, cursor1);
char pixel1 = row1.charAt(cursor1 + color1.length());
String color2 = extractColorCode(row2, cursor2);
char pixel2 = row2.charAt(cursor2 + color2.length());
if (color2.equals("\033[0m") && pixel2 == ' ') {
combinedRow.append(color1).append(pixel1);
} else {
combinedRow.append(color2).append(pixel2);
}
cursor1 += color1.length() + 1;
cursor2 += color2.length() + 1;
}
combinedSprite.append(combinedRow).append("\n");
}
return combinedSprite.toString();
}
private static String extractColorCode(String row, int index) {
StringBuilder colorCode = new StringBuilder();
if (row.charAt(index) == '\033') {
while (index < row.length() && row.charAt(index) != 'm') {
colorCode.append(row.charAt(index));
index++;
}
colorCode.append('m');
}
return colorCode.toString();
}
}

View File

@ -0,0 +1,33 @@
package cz.jzitnik.tui;
import java.util.EnumMap;
import java.util.HashMap;
public class SpriteList<E extends Enum<E>> {
private final EnumMap<E, Sprite> sprites;
// Constructor that takes an Enum class and a HashMap
public SpriteList(Class<E> enumClass, HashMap<E, Sprite> initialMap) {
sprites = new EnumMap<>(enumClass);
// Initialize with values from the provided HashMap
for (E key : enumClass.getEnumConstants()) {
if (!initialMap.containsKey(key)) {
throw new RuntimeException("TODO: Missing sprite");
}
Sprite value = initialMap.get(key);
sprites.put(key, value);
}
}
public Sprite getSprite(E key) {
return sprites.get(key);
}
public void setSprite(E key, Sprite value) {
if (!sprites.containsKey(key)) {
throw new IllegalArgumentException("Invalid key: " + key);
}
sprites.put(key, value);
}
}

View File

@ -0,0 +1,26 @@
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓


View File

@ -0,0 +1,26 @@
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒▒▒▒▒
▒▒▒▒▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▓▓▓▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒▒▒▒▒▒▒▒


View File

@ -0,0 +1,26 @@
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒░▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▓▒▒▒▒▒▒░░░▒▒▓▒▒░▒▓▓▒▒▒▒░▒▒▒▒▒▒▒░░▒▒▒▒▒▒▒░▒▒▒▒▒▒
▒▒▒▒▒▒▓▒▒▒▒▒▒░▒▒▒▒░▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▓▓▓▓▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▓▓▓▓▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▓▒▒▒▓▓▒▒░▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▓▓▓░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▓▒▒▒▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒░▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒▒
▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒░▒░░░▒▒
▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒░░░▒▒▒░░░▒▒▒▒▒▒▒░░░░░░▓▓▓▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒▒▓▒▒
▒▒▒▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒


View File

@ -0,0 +1,23 @@
                         
        ▒▒▒▒▒▒▒▒▒▒       
       ▒ ▓▒▓▒▒▓▒▓ ▒      
       ▒▒▒▒▒▒▒▒▒▒▒▒      
       ▒▒▒ ░░░░ ▒▒▒      
          ░▒▒▒▒▒         
           ▓▒▒▒          
                         
 ▒▓▓▒▒▓            ▒▒▒▒▒▒
 ▒▓▓▒▒▓            ▒▒▒▒▒▒
 ▒▒▒▒▒▓            ▒▒▒▒▒▒
 ▒▒▒▒▓▓            ▒▒▒▒▒▒
 ▒▒▒▒▒▓▒░░░░░░░░   ▒▒▒▒▒▒
 ▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▒░ ▒▒▒▒▓▒
       ▒▒▒▒▒▒▒▒▒▒▒▒      
       ▒▒▒▒▒▒▒▒▒▒▒▒      
       ▒▒▒▒▒▒▒▒▒▒▒▒      
       ▒▒▒▒▒▒▒▒▒▒▒▒      
       ▒▒▒▒▒▒▒▒▒▒▒▒      
       ▒▒▒▒▒▒▒▒▒▒▒▒      
       ▒▒▒▒▒▒▒▒▒▒▒▒      
       ▓▓▓▓▓▓▓▓▓▓▓▓      


View File

@ -0,0 +1,26 @@
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓