From 5e1d1dde52118b1e4f34865d4e3a55233d736942 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Fri, 12 Oct 2018 23:23:16 +0200 Subject: [PATCH] New extra features --- .../{BlockType.java => BlockColor.java} | 5 +- .../warppi/extra/tetris/TetrisGame.java | 72 ++++++++-- .../warppi/extra/tetris/TetrisScreen.java | 17 ++- .../warppi/extra/tetris/Tetromino.java | 126 ++++++++++++++++++ .../warppi/extra/tetris/TetrominoICyan.java | 48 +++++++ .../warppi/extra/tetris/TetrominoJBlue.java | 44 ++++++ .../warppi/extra/tetris/TetrominoLOrange.java | 44 ++++++ .../warppi/extra/tetris/TetrominoOYellow.java | 19 +++ .../warppi/extra/tetris/TetrominoSGreen.java | 44 ++++++ .../warppi/extra/tetris/TetrominoTPurple.java | 44 ++++++ .../warppi/extra/tetris/TetrominoType.java | 11 ++ .../warppi/extra/tetris/TetrominoZRed.java | 44 ++++++ core/src/main/resources/tetrisskin.png | Bin 1722 -> 2289 bytes core/src/main/resources/tetrisskin.xcf | Bin 1494 -> 1600 bytes 14 files changed, 499 insertions(+), 19 deletions(-) rename core/src/main/java/it/cavallium/warppi/extra/tetris/{BlockType.java => BlockColor.java} (67%) create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoICyan.java create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoJBlue.java create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoLOrange.java create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoOYellow.java create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoSGreen.java create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoType.java create mode 100644 core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoZRed.java diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/BlockType.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/BlockColor.java similarity index 67% rename from core/src/main/java/it/cavallium/warppi/extra/tetris/BlockType.java rename to core/src/main/java/it/cavallium/warppi/extra/tetris/BlockColor.java index ce042e1d..903667cc 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/BlockType.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/BlockColor.java @@ -1,10 +1,11 @@ package it.cavallium.warppi.extra.tetris; -public enum BlockType { +public enum BlockColor { RED, GREEN, BLUE, YELLOW, ORANGE, - VIOLET + PURPLE, + CYAN } diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java index c230c1c0..410f718c 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisGame.java @@ -1,14 +1,20 @@ package it.cavallium.warppi.extra.tetris; +import java.util.Arrays; + public class TetrisGame { public static final int WIDTH = 10, HEIGHT = 22; - private BlockType[] grid; - private BlockType[] hovergrid; - private BlockType[] renderedGrid; + public static final double TICK_TIME = 0.25; + private BlockColor[] grid; + private BlockColor[] hovergrid; + private volatile BlockColor[] renderedGrid; private GameStatus gameStatus; private int score; private double currentTime; + private double tickTimer; + private Tetromino currentTetromino; + private Tetromino nextTetromino; public TetrisGame() { resetVariables(); @@ -17,19 +23,29 @@ public class TetrisGame { void playAgain() { resetVariables(); gameStatus = GameStatus.PLAYING; + placeNextTetromino(); } private void resetVariables() { - grid = new BlockType[WIDTH * HEIGHT]; - hovergrid = new BlockType[WIDTH * HEIGHT]; - renderedGrid = new BlockType[WIDTH * HEIGHT]; + grid = new BlockColor[WIDTH * HEIGHT]; + hovergrid = new BlockColor[WIDTH * HEIGHT]; + renderedGrid = new BlockColor[WIDTH * HEIGHT]; score = 0; currentTime = 0; + tickTimer = 0; gameStatus = GameStatus.INITIAL; + currentTetromino = null; + nextTetromino = generateRandomTetromino(); + nextTetromino.fixInitialPosition(); } - public void gameTick(float dt, boolean leftPressed, boolean rightPressed, boolean downPressed, boolean okPressed, boolean backPressed) { + public void update(float dt, boolean leftPressed, boolean rightPressed, boolean downPressed, boolean okPressed, boolean backPressed) { currentTime += dt; + tickTimer += dt; + while (tickTimer >= TICK_TIME) { + tickTimer -= TICK_TIME; + gameTick(leftPressed, rightPressed, downPressed, okPressed, backPressed); + } if (gameStatus == GameStatus.INITIAL) { playAgain(); } else { @@ -37,18 +53,54 @@ public class TetrisGame { } renderGrid(); } + + public void gameTick(boolean leftPressed, boolean rightPressed, boolean downPressed, boolean okPressed, boolean backPressed) { + this.currentTetromino.setY((byte) (this.currentTetromino.getY() - 1)); + } public void renderGrid() { - this.renderedGrid = new BlockType[WIDTH*HEIGHT]; + this.renderedGrid = Arrays.copyOf(grid, grid.length); + drawCurrentTetromino(this.renderedGrid); for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { final int offset = x+y*WIDTH; - renderedGrid[offset] = hovergrid[offset] != null ? hovergrid[offset] : grid[offset]; + renderedGrid[offset] = hovergrid[offset] != null ? hovergrid[offset] : renderedGrid[offset]; } } } - public BlockType[] getRenderedGrid() { + private void placeNextTetromino() { + currentTetromino = nextTetromino; + nextTetromino = generateRandomTetromino(); + nextTetromino.fixInitialPosition(); + } + + private Tetromino generateRandomTetromino() { + int s = (int) (Math.random() * 7); + final byte middleX = (byte)((WIDTH - 1)/2), middleY = (byte)(HEIGHT - 1), rotation = (byte) (Math.random() * 4); + switch (s) { + case 0: + return new TetrominoICyan(middleX, middleY, rotation); + case 1: + return new TetrominoJBlue(middleX, middleY, rotation); + case 2: + return new TetrominoLOrange(middleX, middleY, rotation); + case 3: + return new TetrominoOYellow(middleX, middleY, rotation); + case 4: + return new TetrominoSGreen(middleX, middleY, rotation); + case 5: + return new TetrominoTPurple(middleX, middleY, rotation); + default: + return new TetrominoZRed(middleX, middleY, rotation); + } + } + + private void drawCurrentTetromino(BlockColor[] grid) { + currentTetromino.draw(grid, WIDTH); + } + + public BlockColor[] getRenderedGrid() { return renderedGrid; } } diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java index 04606048..2604a6cd 100644 --- a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrisScreen.java @@ -63,7 +63,7 @@ public class TetrisScreen extends Screen { @Override public void beforeRender(final float dt) { Engine.INSTANCE.getHardwareDevice().getDisplayManager().renderer.glClearColor(0xff000000); - g.gameTick(dt, leftPressed, rightPressed, downPressed, okPressed, backPressed); + g.update(dt, leftPressed, rightPressed, downPressed, okPressed, backPressed); } @Override @@ -72,17 +72,20 @@ public class TetrisScreen extends Screen { TetrisScreen.skin.use(e); } r.glColor3f(1, 1, 1); - BlockType[] renderedGrid = g.getRenderedGrid(); + BlockColor[] renderedGrid = g.getRenderedGrid(); int centerScreen = StaticVars.screenSize[0]/2; - int centerGrid = TetrisGame.WIDTH*5/2-1; + int centerGrid = TetrisGame.WIDTH*6/2-1; final int leftOffset = centerScreen - centerGrid; - final int topOffset = StaticVars.screenSize[1] - TetrisGame.HEIGHT*5-1; + final int topOffset = StaticVars.screenSize[1] - TetrisGame.HEIGHT*6-1; for (int y = 0; y < TetrisGame.HEIGHT; y++) { for (int x = 0; x < TetrisGame.WIDTH; x++) { final int offset = x+y*TetrisGame.WIDTH; - final BlockType type = renderedGrid[offset]; - if (type != null) r.glFillRect(leftOffset + x * 4, y * 4, 4, 4, renderedGrid[offset].ordinal() * 4, 0, 4, 4); - else r.glFillRect(leftOffset + x * 5, topOffset + y * 5, 4, 4, 2 * 4, 0, 4, 4); + final BlockColor type = renderedGrid[offset]; + if (type != null) { + r.glFillRect(leftOffset + x * 5, topOffset + (TetrisGame.HEIGHT+3-y) * 5, 5, 5, renderedGrid[offset].ordinal() * 5, 0, 5, 5); + } else { +// r.glFillRect(leftOffset + x * 5, topOffset + y * 5, 5, 5, 1 * 5, 0, 5, 5); + } } } } diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java new file mode 100644 index 00000000..3d54e370 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/Tetromino.java @@ -0,0 +1,126 @@ +package it.cavallium.warppi.extra.tetris; + +public abstract class Tetromino { + private byte x, y, rotation; + private final TetrominoType type; + public final boolean o = false, w = true; + + public Tetromino(byte x, byte y, byte rotation, TetrominoType type) { + super(); + this.x = x; + this.y = y; + this.rotation = rotation; + this.type = type; + } + + public byte getX() { + return x; + } + + public void setX(byte x) { + this.x = x; + } + + public byte getY() { + return y; + } + + public void setY(byte y) { + this.y = y; + } + + public byte getRotation() { + return rotation; + } + + public void setRotation(byte rotation) { + this.rotation = rotation; + } + + public TetrominoType getType() { + return type; + } + + public BlockColor getColor() { + switch(type) { + case I_CYAN: + return BlockColor.CYAN; + case J_BLUE: + return BlockColor.BLUE; + case L_ORANGE: + return BlockColor.ORANGE; + case O_YELLOW: + return BlockColor.YELLOW; + case S_GREEN: + return BlockColor.GREEN; + case T_PURPLE: + return BlockColor.PURPLE; + case Z_RED: + return BlockColor.RED; + default: + return BlockColor.RED; + } + } + + public void draw(BlockColor[] grid, final int WIDTH) { + boolean[] blockGrid = getRenderedBlock(); + final int tetrominoGridSize = getTetrominoGridSize(); + final int centerOffset = (int) Math.floor((double)tetrominoGridSize/2d); + final BlockColor type = getColor(); + for (int bx = 0; bx < tetrominoGridSize; bx++) { + for (int by = 0; by < tetrominoGridSize; by++) { + if (blockGrid[bx+by*tetrominoGridSize] == w) { + final int index = x+bx-centerOffset + (y+by-centerOffset) * WIDTH; + if (index < grid.length && index >= 0) { + grid[index] = type; + } + } + } + } + } + + public void fixInitialPosition() { + this.y -= (byte) (this.getTetrominoGridSize()/2); + } + + public abstract int getTetrominoGridSize(); + protected abstract boolean[] getRenderedBlock(); + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + rotation; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + x; + result = prime * result + y; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Tetromino other = (Tetromino) obj; + if (rotation != other.rotation) + return false; + if (type != other.type) + return false; + if (x != other.x) + return false; + if (y != other.y) + return false; + return true; + } + + @Override + public String toString() { + return "Tetromino = {\n\t\"x\": \"" + x + "\",\n\ty\": \"" + y + "\",\n\trotation\": \"" + rotation + "\",\n\ttype\": \"" + type + "\"\n}"; + } + + +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoICyan.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoICyan.java new file mode 100644 index 00000000..a6eb84a4 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoICyan.java @@ -0,0 +1,48 @@ +package it.cavallium.warppi.extra.tetris; + +public class TetrominoICyan extends Tetromino { + public TetrominoICyan(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.I_CYAN); + } + + @Override + public int getTetrominoGridSize() { + return 4; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + o,o,o,o, + w,w,w,w, + o,o,o,o, + o,o,o,o + }; + case 1: + return new boolean[] { + o,o,w,o, + o,o,w,o, + o,o,w,o, + o,o,w,o + }; + case 2: + return new boolean[] { + o,o,o,o, + o,o,o,o, + w,w,w,w, + o,o,o,o + }; + case 3: + return new boolean[] { + o,w,o,o, + o,w,o,o, + o,w,o,o, + o,w,o,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoJBlue.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoJBlue.java new file mode 100644 index 00000000..e1835e22 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoJBlue.java @@ -0,0 +1,44 @@ +package it.cavallium.warppi.extra.tetris; + +public class TetrominoJBlue extends Tetromino { + public TetrominoJBlue(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.J_BLUE); + } + + @Override + public int getTetrominoGridSize() { + return 3; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + w,o,o, + w,w,w, + o,o,o + }; + case 1: + return new boolean[] { + o,w,w, + o,w,o, + o,w,o + }; + case 2: + return new boolean[] { + o,o,o, + w,w,w, + o,o,w + }; + case 3: + return new boolean[] { + o,w,o, + o,w,o, + w,w,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoLOrange.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoLOrange.java new file mode 100644 index 00000000..ec7ad80d --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoLOrange.java @@ -0,0 +1,44 @@ +package it.cavallium.warppi.extra.tetris; + +public class TetrominoLOrange extends Tetromino { + public TetrominoLOrange(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.L_ORANGE); + } + + @Override + public int getTetrominoGridSize() { + return 3; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + o,o,w, + w,w,w, + o,o,o + }; + case 1: + return new boolean[] { + o,w,o, + o,w,o, + o,w,w + }; + case 2: + return new boolean[] { + o,o,o, + w,w,w, + w,o,o + }; + case 3: + return new boolean[] { + w,w,o, + o,w,o, + o,w,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoOYellow.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoOYellow.java new file mode 100644 index 00000000..124c6c16 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoOYellow.java @@ -0,0 +1,19 @@ +package it.cavallium.warppi.extra.tetris; + +public class TetrominoOYellow extends Tetromino { + public TetrominoOYellow(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.O_YELLOW); + } + @Override + public int getTetrominoGridSize() { + return 2; + } + + @Override + public boolean[] getRenderedBlock() { + return new boolean[] { + w,w, + w,w, + }; + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoSGreen.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoSGreen.java new file mode 100644 index 00000000..1bfb5354 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoSGreen.java @@ -0,0 +1,44 @@ +package it.cavallium.warppi.extra.tetris; + +public class TetrominoSGreen extends Tetromino { + public TetrominoSGreen(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.S_GREEN); + } + + @Override + public int getTetrominoGridSize() { + return 3; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + o,w,w, + w,w,o, + o,o,o + }; + case 1: + return new boolean[] { + o,w,o, + o,w,w, + o,o,w + }; + case 2: + return new boolean[] { + o,o,o, + o,w,w, + w,w,o + }; + case 3: + return new boolean[] { + w,o,o, + w,w,o, + o,w,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java new file mode 100644 index 00000000..9d56e4db --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoTPurple.java @@ -0,0 +1,44 @@ +package it.cavallium.warppi.extra.tetris; + +public class TetrominoTPurple extends Tetromino { + public TetrominoTPurple(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.I_CYAN); + } + + @Override + public int getTetrominoGridSize() { + return 3; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + o,w,o, + w,w,w, + o,o,o + }; + case 1: + return new boolean[] { + o,w,o, + o,w,w, + o,w,o + }; + case 2: + return new boolean[] { + o,o,o, + w,w,w, + o,w,o + }; + case 3: + return new boolean[] { + o,w,o, + w,w,o, + o,w,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoType.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoType.java new file mode 100644 index 00000000..c6091e39 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoType.java @@ -0,0 +1,11 @@ +package it.cavallium.warppi.extra.tetris; + +public enum TetrominoType { + I_CYAN, + O_YELLOW, + T_PURPLE, + S_GREEN, + Z_RED, + J_BLUE, + L_ORANGE +} diff --git a/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoZRed.java b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoZRed.java new file mode 100644 index 00000000..22daa3f9 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/extra/tetris/TetrominoZRed.java @@ -0,0 +1,44 @@ +package it.cavallium.warppi.extra.tetris; + +public class TetrominoZRed extends Tetromino { + public TetrominoZRed(byte x, byte y, byte rotation) { + super(x, y, rotation, TetrominoType.Z_RED); + } + + @Override + public int getTetrominoGridSize() { + return 3; + } + + @Override + public boolean[] getRenderedBlock() { + switch(getRotation()) { + case 0: + return new boolean[] { + w,w,o, + o,w,w, + o,o,o + }; + case 1: + return new boolean[] { + o,o,w, + o,w,w, + o,w,o + }; + case 2: + return new boolean[] { + o,o,o, + w,w,o, + o,w,w + }; + case 3: + return new boolean[] { + o,w,o, + w,w,o, + w,o,o + }; + default: + throw new NullPointerException(); + } + } +} diff --git a/core/src/main/resources/tetrisskin.png b/core/src/main/resources/tetrisskin.png index 7cc22a6636fc1910f38a1767f435415fb3ef1d9c..f73890af931f6f15c31fc9bf4f50622dac8c6acc 100644 GIT binary patch delta 2232 zcmV;p2uJt24e=3>BYy`%dQ@0+Qek%>aB^>EX>4U6ba`-PAZ2)IW&i+q+U=KFj_tM% zMgKjD48dwBO2bh{ATxNw&!UcRb-4-B{XC$KQEp_JBDps=RoDLCuXg>yFIJ6L7Oka~ za`VeAx7fL<_v`n%hC840`Q_&o&!5Jxw-=G2#5KJh%YNo}#((Ag1ARUS?JbS4nt~ zYU)1rI!7N#=ibkEYCq52^>+Iud2e@La(DMp-kbgU~Rn7J*x}FP}D%TU#k5JAX6Ul2%=d;40{0aAZj>b`0W`fuP&A~CR z&_X1q7IIjj2MhaLXPCH{nEcEv015WwR8D@+aed&; z{Q=knf^yPa39#SeWnxHp=Ts#A z<9SRu4iE?;afQpkCZz&6HTv3oqv6br5?{ke3qVLJNr9%N4p=!0{Lx(DP?se65|JV* zRhsmYN`Ed=O3~sP)ir5W(W0tVoA#P&u2M_YT5GGl#g>pTX{G7aYHO``7VVhseBGJR z`-mfrJYp*Jg?Gc9Xv*x5yvg0Ri~6X zFMnHEZRFlpHYK*Wr=o8)qosmF+e>~Vk?w}b4_4YzGfK}#q>s5YV~B@VhA~0tIX9ZPS1^Yl9qzwz@Eue zm_?H;U<|%+oPG|tCalxe9@{QO#yeW8tAC&od+`}i%w@V*f3|T$CYgr8tYxKb5p;U8 z)ohi)-M&R_Uh_n{7rEqG`hp*#dF`8|G~FlVMNgh|fV+D^kQFyVFLr|D#np3bmQ$sT zOU~20thTom40Po(>kF?6Vx?MVWoXrwjcRe=J(XC|+$zHr%J?bV+W2jKZ-UlV_|PlV@s&v4nX;q9$qf3kYL12<$0u zc|s+nJ}mY+HIFciVk9juzhZ&;b${Do)S@j=WvrcaUftHT);0xib%Z)gS_5WGi5r?G zqkuqTzlhsj#Dxi=QN?+*0%`l`?Xt%7+Jg0MOW*}1ywyMj>^Ir%1P_Va5IH8maI)8z zOek02W~9G>tx<+Eb6Y3U4Oa8Wph*B zlYp}X*#f-XZ?pJD^RHbM<-8K?TwYq+^iaY3I)L>R%JO&AF8t=z!ttBn2ntWU1e2Fw zW^b;oU6pNU#dFf8g%YrXUum^$KeE^Ovdv52sh~R`k{L@-tcMOOMUbS{wij%th$syg zH!y@qI#Mafku(m$4OtY%AigDXH0n|Wj`XcPhe5;; zx=9KYGcv!Vqw#4E=nt%SiL=2=T6Tp;L<($&$6gaL;TFxxXg^^uHYTW9E$Ed}BDKtf z6`D?Jg2EJ(RaBivX#?O2lD+JgcP6XRE0td04VFK=r6W;Ijhthib$=U+u3~qwnZg48 zf-C3?v+TW|bg-GESYG8JPkEqPln3OGb@217Q5DGnMIl{T@pMy{DbF?7Ov$>tS2T;d zq+pf05%$M4_FQq>T|Y$Wm324mZ?O|)1ae!85~9sY)mE&OBC96B?Q!FFKn=lSc?qh)Uj4Q>fPqcbB4i!-CBrJ9lkVr3XwLdN#Ox{ z*c(8@o@vDq@xQ7>1QFgf#QR1-5_=l7q|;yttw-i0-}(5R|8x6~uGjr94SXB=HuP=i z+t9b6Z$tmrhS>0|cHh@;w#3P$xw0|>lYRv8lZVHI804b^LJpRub86A%` z2>Cj4GiM@hnOlf$8_)OMck^~LJA%j;&WNIj6N>?gZoKK#41lf}o25hRim_P=KykD$ ze;xKcZN~$yuln~uIJAsxh+`wmI0tLoZ&yG&tl&=7I2=U}j#mxG(LK;K6{{7rZNEZr z-d^D{hIjAR^A;Xn_9P^gpE+CM^)j9)HGsH(eDKY$DVxfq~c`sACjUf8$W&)bI0 zLfoAH3AhwV5xNbKBYy=^dQ@0+Qek%>aB^>EX>4U6ba`-PAZ2)IW&i+q+U-_RlI120 z{O1%o0zwGEaR5nGxj~Mf#`eD1nVsEKviY3ndN!6pNZl>j$^ZH@(jWMh+Iy;^y^Yo~ zes$DQCI&sA;r2FyHA4kuF zZw~qFTrha%^~VvH+fL7Y8qc~}zf0%&ug4udlRn;81sEpq$0ZeK;*4`UyD%c)&hyxC z&OFLDOXh}iIrBclF1>~yzlipXQruw7z~;iS6AdarHmH)( zq#(bpn>IqM#f#!v$}W7_I%^qaU!>G?P!#3CmcU5i4b~Xu@`{D7eaE%mrjDJBFtZSx z5mx+gq|c21<#;)YIMOA`{e>0cDo4`@<1ly5m<2*89)C>b!uLATce=^n5zAmuE|@zK zoO#_Yme8BF_~Mx{PjXF9Ig{Gs01#nsK`_K%AU9&6Mp;WyBH-A;&qU;fYgu4`M2b-g zaKqd{PA_L~tWmn=7AdzGtp!8~N<=};;0dy773fE6hlZxG)FdKJRJshAjazNfTGMuV z)Ku20qJK?QyAGY5d+pL&*WUZ+bCkhoVKUm#H2N50PHZ~idSdm&j5$|Xyy}wGmae|W znj7-jb<1vBci&@AE5?TMw`Q8G+py{6kdjvyZ?4{b_zY+*IBH0=(C9H@zF2!$ea{+w zVD8tf(ZkwdtX}i4|6~A_6ch?uQv)`1f428m}U+% zHm7;5-LcfG*;;I9PqJiV8LRHGv^g(tYgqSs{O_iKX%ro!hnX5&Y(?IvwW*H2*a8beWpRVYzIbd`TQ=8@X70SZ zSrm6eNYQ6GI3VB!8*&&PX+Xg%kd3UN5OZ`=j;s|;8hpwZiYz70-hbZ4d~vvFlM zQ=Zl*jl~j{@n2McSX*siJI@AW)FyU9W`7v~NDtowjXIRPVaK(=L-Pc8(b!jQJ?7Q` z)5Bu*C=1-K00bw|SUD?AmkOPG@vyu03juHqqQJ{wUoe^Y}*G31rqRH>fpUMs%ML8*ncU8 ztyaTYB5)#Dd<8(fh&fqt$tC8upi ze7*co`1vk^AAVPn@6UNV&*wvbYFof(y5)Ov(ip0m+jh zcH(uD!Y;gUKA~dnfIw4Bilzxpw1ug&ppQQ8Pmj1O^8dj_M4dxJd9CzRZeY|!H zumli)+Q0uxzxUq;&G4{_mUcbIEs~N6Wr7IKjwFbP4^{t+_S(|H_C xDjrf)i+H+G?G8e9{8!nq1Id=$ww}COGcXF!?{B`TGHF%E*=Wo^_f;f!@dTMzjx+!O delta 261 zcmX@WbB%j~6Qle_XG0br=Kt)>-2c@XUi@FW^glBL12YJN*#A$T{{MgRVxZKY!Z~va z*%`iosEx;uZ!DQTyF{IV87RbD$h?sm#0DC|{9k|aO%@#wW*`fU8?_nUzzl=YjhUH^ z`Va$|8Ge8aj8037)(05`!qM75`ytx(CpWO_ID?D@iU2K8XZV2ZItcp@$bnGv*ctwS d90oEA=rEw8AbRyDbFwK4i39Z`a6P{U0{|T|S_uFE