forked from jzitnik/twodcraft
372 lines
13 KiB
Java
372 lines
13 KiB
Java
package cz.jzitnik.game;
|
|
|
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
import cz.jzitnik.game.entities.Block;
|
|
import cz.jzitnik.game.entities.GameStates;
|
|
import cz.jzitnik.game.entities.Player;
|
|
import cz.jzitnik.game.generation.Generation;
|
|
import cz.jzitnik.game.entities.items.Item;
|
|
import cz.jzitnik.game.entities.items.ItemType;
|
|
import cz.jzitnik.game.handlers.place.CustomPlaceHandler;
|
|
import cz.jzitnik.game.mobs.EntitySpawnProvider;
|
|
import cz.jzitnik.game.sprites.Breaking;
|
|
import cz.jzitnik.game.sprites.Steve;
|
|
import cz.jzitnik.game.blocks.Chest;
|
|
import cz.jzitnik.game.blocks.Furnace;
|
|
import cz.jzitnik.game.ui.Window;
|
|
import cz.jzitnik.game.ui.Inventory;
|
|
import cz.jzitnik.game.handlers.rightclick.RightClickHandlerProvider;
|
|
import cz.jzitnik.tui.ScreenMovingCalculationProvider;
|
|
import cz.jzitnik.tui.ScreenRenderer;
|
|
import lombok.Getter;
|
|
import lombok.Setter;
|
|
import org.jline.terminal.Terminal;
|
|
|
|
import java.util.List;
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
|
|
|
@Getter
|
|
public class Game {
|
|
@SuppressWarnings("unchecked")
|
|
private final List<Block>[][] world = (List<Block>[][]) new CopyOnWriteArrayList[256][512];
|
|
private final Player player = new Player();
|
|
private boolean mining = false;
|
|
@Setter
|
|
private Window window = Window.WORLD;
|
|
private final Inventory inventory = new Inventory();
|
|
|
|
@JsonIgnore
|
|
private final EntitySpawnProvider entitySpawnProvider = new EntitySpawnProvider();
|
|
|
|
@JsonIgnore
|
|
private final GameStates gameStates = new GameStates(this);
|
|
|
|
public Game() {
|
|
Generation.generateWorld(this);
|
|
}
|
|
|
|
public 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") && block.getSpriteState().isPresent() && block.getSpriteState().get() == Steve.SteveState.SECOND) {
|
|
return new int[]{j, i};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void movePlayerRight(ScreenRenderer screenRenderer, Terminal terminal) {
|
|
if (window != Window.WORLD) {
|
|
return;
|
|
}
|
|
int[] cords = getPlayerCords();
|
|
|
|
if (isSolid(world[cords[1]][cords[0] + 1]) || isSolid(world[cords[1] - 1][cords[0] + 1])) {
|
|
return;
|
|
}
|
|
|
|
world[cords[1]][cords[0] + 1].add(player.getPlayerBlock2());
|
|
world[cords[1]][cords[0]].remove(player.getPlayerBlock2());
|
|
world[cords[1]-1][cords[0] + 1].add(player.getPlayerBlock1());
|
|
world[cords[1]-1][cords[0]].remove(player.getPlayerBlock1());
|
|
screenRenderer.render(this);
|
|
|
|
entitySpawnProvider.update(this, terminal);
|
|
|
|
update(screenRenderer);
|
|
}
|
|
|
|
public void movePlayerLeft(ScreenRenderer screenRenderer, Terminal terminal) {
|
|
if (window != Window.WORLD) {
|
|
return;
|
|
}
|
|
int[] cords = getPlayerCords();
|
|
|
|
if (isSolid(world[cords[1]][cords[0] - 1]) || isSolid(world[cords[1] - 1][cords[0] - 1])) {
|
|
return;
|
|
}
|
|
|
|
world[cords[1]][cords[0] - 1].add(player.getPlayerBlock2());
|
|
world[cords[1]][cords[0]].remove(player.getPlayerBlock2());
|
|
world[cords[1]-1][cords[0] - 1].add(player.getPlayerBlock1());
|
|
world[cords[1]-1][cords[0]].remove(player.getPlayerBlock1());
|
|
screenRenderer.render(this);
|
|
|
|
entitySpawnProvider.update(this, terminal);
|
|
|
|
update(screenRenderer);
|
|
}
|
|
|
|
public void movePlayerUp(ScreenRenderer screenRenderer) {
|
|
if (window != Window.WORLD) {
|
|
return;
|
|
}
|
|
int[] cords = getPlayerCords();
|
|
|
|
if (isSolid(world[cords[1] - 2][cords[0]]) || !isSolid(world[cords[1] + 1][cords[0]])) {
|
|
return;
|
|
}
|
|
|
|
world[cords[1] - 1][cords[0]].remove(player.getPlayerBlock1());
|
|
world[cords[1] - 1][cords[0]].add(player.getPlayerBlock2());
|
|
world[cords[1] - 2][cords[0]].add(player.getPlayerBlock1());
|
|
world[cords[1]][cords[0]].remove(player.getPlayerBlock2());
|
|
|
|
new Thread(() -> {
|
|
try {
|
|
Thread.sleep(400);
|
|
} catch (InterruptedException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
|
|
update(screenRenderer);
|
|
}).start();
|
|
}
|
|
|
|
public void hit(ScreenRenderer screenRenderer, int x, int y) {
|
|
if (mining || window != Window.WORLD) {
|
|
return;
|
|
}
|
|
|
|
List<Block> mobs = world[y][x].stream().filter(Block::isMob).toList();
|
|
|
|
for (Block mob : mobs) {
|
|
int dealDamage = inventory.getItemInHand().map(Item::getDealDamage).orElse(1);
|
|
if (mob.getHp() - dealDamage <= 0) {
|
|
// Mob is killed
|
|
gameStates.dependencies.entityKill.get(mob.getBlockId()).killed(this, mob);
|
|
world[y][x].remove(mob);
|
|
} else {
|
|
mob.decreaseHp(dealDamage);
|
|
mob.setSpriteState(gameStates.dependencies.entityHurtAnimation.get(mob.getBlockId()).setHurtAnimation(true, mob.getSpriteState().get()));
|
|
}
|
|
}
|
|
screenRenderer.render(this);
|
|
|
|
new Thread(() -> {
|
|
try {
|
|
Thread.sleep(500);
|
|
} catch (InterruptedException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
|
|
for (Block mob : mobs) {
|
|
mob.setSpriteState(gameStates.dependencies.entityHurtAnimation.get(mob.getBlockId()).setHurtAnimation(false, mob.getSpriteState().get()));
|
|
}
|
|
screenRenderer.render(this);
|
|
}).start();
|
|
}
|
|
|
|
public void mine(ScreenRenderer screenRenderer, int x, int y) {
|
|
if (mining || window != Window.WORLD) {
|
|
return;
|
|
}
|
|
|
|
Block breakingBlock = new Block("breaking", SpriteLoader.SPRITES.BREAKING);
|
|
world[y][x].add(breakingBlock);
|
|
screenRenderer.render(this);
|
|
|
|
double hardness = world[y][x].stream().filter(block -> !block.isGhost()).toList().getFirst().calculateHardness(inventory);
|
|
|
|
this.mining = true;
|
|
|
|
new Thread(() -> {
|
|
try {
|
|
Thread.sleep((long) (hardness * 166));
|
|
} catch (InterruptedException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
breakingBlock.setSpriteState(Breaking.BreakingState.SECOND);
|
|
screenRenderer.render(this);
|
|
|
|
try {
|
|
Thread.sleep((long) (hardness * 166));
|
|
} catch (InterruptedException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
breakingBlock.setSpriteState(Breaking.BreakingState.THIRD);
|
|
screenRenderer.render(this);
|
|
|
|
try {
|
|
Thread.sleep((long) (hardness * 166));
|
|
} catch (InterruptedException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
|
|
mining = false;
|
|
|
|
var blocks = world[y][x];
|
|
for (Block block : blocks) {
|
|
if (!block.isMineable()) {
|
|
continue;
|
|
}
|
|
|
|
if (block.getToolVariants().isEmpty()) {
|
|
// Add to inv
|
|
block.getDrops().forEach(inventory::addItem);
|
|
continue;
|
|
}
|
|
|
|
var toolVariants = block.getToolVariants();
|
|
if (inventory.getItemInHand().isPresent() && inventory.getItemInHand().get().getToolVariant().isPresent() && block.getTool().isPresent() && block.getTool().get().equals(inventory.getItemInHand().get().getType()) && toolVariants.contains(inventory.getItemInHand().get().getToolVariant().get())) {
|
|
block.getDrops().forEach(inventory::addItem);
|
|
}
|
|
}
|
|
|
|
for (Block block : blocks) {
|
|
if (block.getBlockId().equals("chest")) {
|
|
((Chest) block.getData()).breakBlock(this);
|
|
} else if (block.getBlockId().equals("furnace")) {
|
|
((Furnace) block.getData()).breakBlock(this);
|
|
}
|
|
}
|
|
|
|
CustomPlaceHandler customPlaceHandler = gameStates.dependencies.placeHandler.get(blocks.stream().filter(Block::isMineable).toList().getFirst().getBlockId());
|
|
customPlaceHandler.mine(this, x, y);
|
|
inventory.getItemInHand().ifPresent(Item::use);
|
|
|
|
screenRenderer.render(this);
|
|
|
|
update(screenRenderer);
|
|
}).start();
|
|
}
|
|
|
|
public boolean isMineable(int x, int y, Terminal terminal) {
|
|
List<Block> blocks = world[y][x];
|
|
|
|
int[] cords = getPlayerCords();
|
|
|
|
int playerX = cords[0];
|
|
int playerY = cords[1];
|
|
|
|
int distanceX = Math.abs(playerX - x);
|
|
int distanceY = Math.abs(playerY - y);
|
|
|
|
int[] data = ScreenMovingCalculationProvider.calculate(playerX, playerY, terminal.getHeight(), terminal.getWidth(), world[0].length, world.length);
|
|
|
|
int startX = data[0];
|
|
int endX = data[1];
|
|
int startY = data[2];
|
|
int endY = data[3];
|
|
|
|
return
|
|
y >= startY && y < endY - 1 && x >= startX && x < endX - 1
|
|
&& distanceX <= 5 && distanceY <= 5
|
|
&& !(playerX == x && playerY == y)
|
|
&& !(playerX == x && playerY - 1 == y)
|
|
&& blocks.stream().anyMatch(Block::isMineable);
|
|
}
|
|
|
|
public boolean isHitable(int x, int y, Terminal terminal) {
|
|
List<Block> blocks = world[y][x];
|
|
|
|
int[] cords = getPlayerCords();
|
|
|
|
int playerX = cords[0];
|
|
int playerY = cords[1];
|
|
|
|
int distanceX = Math.abs(playerX - x);
|
|
int distanceY = Math.abs(playerY - y);
|
|
|
|
int[] data = ScreenMovingCalculationProvider.calculate(playerX, playerY, terminal.getHeight(), terminal.getWidth(), world[0].length, world.length);
|
|
|
|
int startX = data[0];
|
|
int endX = data[1];
|
|
int startY = data[2];
|
|
int endY = data[3];
|
|
|
|
return
|
|
y >= startY && y < endY - 1 && x >= startX && x < endX - 1
|
|
&& distanceX <= 5 && distanceY <= 5
|
|
&& !(playerX == x && playerY == y)
|
|
&& !(playerX == x && playerY - 1 == y)
|
|
&& blocks.stream().anyMatch(Block::isMob);
|
|
}
|
|
|
|
public void update(ScreenRenderer screenRenderer) {
|
|
while (true) {
|
|
try {
|
|
Thread.sleep(100);
|
|
} catch (InterruptedException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
int[] cords2 = getPlayerCords();
|
|
|
|
if (!isSolid(world[cords2[1] + 1][cords2[0]])) {
|
|
world[cords2[1] - 1][cords2[0]].remove(player.getPlayerBlock1());
|
|
world[cords2[1]][cords2[0]].add(player.getPlayerBlock1());
|
|
world[cords2[1] + 1][cords2[0]].add(player.getPlayerBlock2());
|
|
world[cords2[1]][cords2[0]].remove(player.getPlayerBlock2());
|
|
player.addFalling();
|
|
|
|
screenRenderer.render(this);
|
|
} else {
|
|
player.fell();
|
|
screenRenderer.render(this);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void build(int x, int y, ScreenRenderer screenRenderer) {
|
|
if (window != Window.WORLD) {
|
|
return;
|
|
}
|
|
if (inventory.getItemInHand().isPresent() && inventory.getItemInHand().get().getType() == ItemType.FOOD) {
|
|
if (player.getHunger() >= 10) {
|
|
return;
|
|
}
|
|
|
|
player.setHunger(Math.min(10, player.getHunger() + inventory.getItemInHand().get().getAddHunger()));
|
|
inventory.decreaseItemInHand();
|
|
screenRenderer.render(this);
|
|
return;
|
|
}
|
|
var blocks = world[y][x];
|
|
|
|
int[] cords = getPlayerCords();
|
|
|
|
int playerX = cords[0];
|
|
int playerY = cords[1];
|
|
|
|
int distanceX = Math.abs(playerX - x);
|
|
int distanceY = Math.abs(playerY - y);
|
|
|
|
if (distanceX > 5 || distanceY > 5) {
|
|
return;
|
|
}
|
|
|
|
if (!blocks.stream().allMatch(block -> block.getBlockId().equals("air") || block.isFlowing())) {
|
|
RightClickHandlerProvider.handle(x, y, this, screenRenderer);
|
|
screenRenderer.render(this);
|
|
return;
|
|
}
|
|
|
|
if (!(inventory.getItemInHand().isPresent() && inventory.getItemInHand().get().getType() == ItemType.BLOCK)) {
|
|
return;
|
|
}
|
|
|
|
Item item = inventory.getItemInHand().get();
|
|
|
|
CustomPlaceHandler placeHandler = gameStates.dependencies.placeHandler.get(item.getId());
|
|
|
|
if (placeHandler.place(this, x, y)) {
|
|
screenRenderer.render(this);
|
|
}
|
|
}
|
|
|
|
public void changeSlot(int slot, ScreenRenderer screenRenderer) {
|
|
if (window != Window.WORLD) {
|
|
return;
|
|
}
|
|
inventory.setItemInhHandIndex(slot);
|
|
screenRenderer.render(this);
|
|
}
|
|
|
|
public boolean isSolid(List<Block> blocks) {
|
|
return !blocks.stream().allMatch(Block::isGhost);
|
|
}
|
|
} |