Archived
Private
Public Access
1
0

Initial commit

This commit is contained in:
2022-09-04 12:45:01 +02:00
commit f4a01d6a69
11601 changed files with 4206660 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: com.jdh.microcraft.Main

View File

@@ -0,0 +1,221 @@
package com.jdh.microcraft;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.entity.SpawnProperties;
import com.jdh.microcraft.entity.mob.EntityAirWizard;
import com.jdh.microcraft.gfx.Font;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.gui.*;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.level.Level;
import com.jdh.microcraft.level.gen.LevelGenerator;
import com.jdh.microcraft.level.tile.Tile;
import com.jdh.microcraft.sound.Sound;
import com.jdh.microcraft.util.ControlHandler;
import com.jdh.microcraft.util.Time;
import com.jdh.microcraft.util.Window;
public class GameState implements State {
private static final int LEVEL_DEPTH_OFFSET = LevelGenerator.UNDERWORLD_LEVELS;
// all world levels
private final Level[] levels = new Level[LevelGenerator.OVERWORLD_LEVELS + LevelGenerator.UNDERWORLD_LEVELS];
// current level depth, ranges from [-UNDERWORLD_LEVELS, OVERWORLD_LEVELS)
private int levelDepth;
// global, must be kept on game state to keep unique across levels
private int nextEntityId, nextItemInstanceId;
// overall game difficulty multiplier
public double difficulty;
public int score;
public long playerDiedTicks;
public HUD hud;
public Menu menu;
private final int playerColor;
public GameState(double difficulty, int playerColor) {
this.difficulty = difficulty;
this.playerColor = playerColor;
this.nextEntityId = 1;
this.nextItemInstanceId = 1;
this.score = 0;
this.playerDiedTicks = 0;
}
public void init() {
// generate levels
for (int i = 0; i < levels.length; i++) {
this.levels[i] = new Level(Global.ticks + Time.now() + i, i - LEVEL_DEPTH_OFFSET, 256, 256);
LevelGenerator.getGenerator(this.levels[i]).generate();
}
// generate stairs
Global.mainMenu.loadingMenu.setProgress(
Font.Colors.YELLOW + "PLACING STAIRS...", 0.0);
for (int i = 0; i < levels.length - 1; i++) {
LevelGenerator.getStairsGenerator(this.levels[i], this.levels[i + 1]).generate();
Global.mainMenu.loadingMenu.setProgress(i / (double) levels.length);
}
// spawn entities
Global.mainMenu.loadingMenu.setProgress(
Font.Colors.YELLOW + "SPAWNING...", 0.0);
for (int i = 0; i < levels.length; i++) {
Level level = this.levels[i];
level.addEntitySpawns(SpawnProperties.getSpawnProperties(level));
level.populate();
if (level.depth == 1) {
EntityAirWizard wizard = new EntityAirWizard(level);
level.spawnOnTile(wizard, Tile.CLOUD.id, level.width / 2, (level.height / 2) - 6);
}
Global.mainMenu.loadingMenu.setProgress(i / (double) levels.length);
}
// spawn player in overworld
Level level = this.getLevel(0);
this.setLevel(level.depth);
EntityPlayer player = new EntityPlayer(level, playerColor);
this.hud = new HUD(player);
player.x = Level.toPixel(level.width / 2);
player.y = Level.toPixel((level.height / 2) + 1);
player.updateCamera();
level.addEntity(player);
this.setMenu(new WelcomeMenu());
}
@Override
public void tick() {
if (!Window.hasFocus()) {
if (!(this.menu instanceof FocusMenu)) {
this.setMenu(new FocusMenu(this.menu));
}
return;
}
// death screen
if (!(this.menu instanceof LoseMenu) &&
this.playerDiedTicks != 0 &&
(this.playerDiedTicks + 120) <= Global.ticks) {
Sound.LOSE.play();
this.setMenu(new LoseMenu());
}
this.hud.tick();
if (this.menu != null) {
Menu oldMenu = this.menu;
this.menu.tick();
// interact pressed + nothing closed = close the current menu
if (oldMenu == this.menu &&
ControlHandler.INTERACT.pressedTick()) {
this.setMenu(null);
}
} else {
this.getCurrentLevel().tick();
// don't allow toggles on ticks where a new menu was opened
if (this.menu != null) {
return;
}
}
// menu controls
if (ControlHandler.MENU_QUIT.pressedTick()) {
if (this.menu == null) {
this.setMenu(new PauseMenu());
} else {
// quit the current menu
Global.game.setMenu(null);
}
}
}
@Override
public void update() {
if (this.menu != null) {
this.menu.update();
} else {
this.getCurrentLevel().update();
}
this.hud.update();
}
@Override
public void render() {
Level level = this.getCurrentLevel();
level.render();
Renderer.clearLights();
if (level.depth < 0) {
Renderer.addLights(this.getCurrentLevel().getLights(Renderer.getAABB()));
Renderer.light(-111);
}
Renderer.pushCamera();
Renderer.camera.tx = 0;
Renderer.camera.ty = 0;
this.hud.render();
if (this.menu != null) {
this.menu.render();
}
Renderer.popCamera();
}
public void setMenu(Menu menu) {
if (this.menu != null) {
this.menu.destroy();
}
if (menu != null) {
menu.init();
}
this.menu = menu;
}
public Menu getMenu() {
return this.menu;
}
public Level getCurrentLevel() {
return this.levels[this.levelDepth + LEVEL_DEPTH_OFFSET];
}
public Level getLevel(int depth) {
assert (this.isDepthValid(depth));
return this.levels[depth + LEVEL_DEPTH_OFFSET];
}
public void setLevel(int depth) {
assert (this.isDepthValid(depth));
this.levelDepth = depth;
}
public boolean isDepthValid(int depth) {
return depth >= -LEVEL_DEPTH_OFFSET && depth < LevelGenerator.OVERWORLD_LEVELS;
}
public int getNextEntityId() {
return this.nextEntityId++;
}
public int getNextItemInstanceId() {
return this.nextItemInstanceId++;
}
}

View File

@@ -0,0 +1,60 @@
package com.jdh.microcraft;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.gui.mainmenu.MainMenu;
import com.jdh.microcraft.sound.Sound;
import com.jdh.microcraft.util.Time;
import com.jdh.microcraft.util.Window;
import java.util.Random;
// Encapsulates (statically) all game state
public class Global {
public enum StateType {
MENU, GAME
}
public static StateType currentStateType;
public static GameState game;
public static MainMenuState mainMenu;
public static State currentState;
public static Random random = new Random(Time.now());
public static long ticks = 0, frames = 0;
public static void setState(StateType state) {
Global.currentStateType = state;
Renderer.reset();
switch (state) {
case GAME -> {
Sound.START.play();
Global.game = new GameState(
Global.mainMenu.difficultySelectMenu.getSelectedDifficulty(),
Color.get(
Global.mainMenu.colorSelectMenu.colors
));
Global.mainMenu.menu = Global.mainMenu.loadingMenu;
Global.mainMenu.loadingMenu.setProgress("LOADING", 0.0);
Global.game.init();
Global.currentState = Global.game;
}
case MENU -> {
Global.game = null;
Global.mainMenu.mainMenu = new MainMenu();
Global.mainMenu.menu = Global.mainMenu.mainMenu;
Global.currentState = Global.mainMenu;
}
}
}
public static void setLoadingInfo(String text, double progress) {
Global.mainMenu.loadingMenu.progress = progress;
Global.mainMenu.loadingMenu.text = text;
Renderer.clear();
Global.mainMenu.loadingMenu.render();
Window.renderFrame();
}
}

View File

@@ -0,0 +1,92 @@
package com.jdh.microcraft;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.util.Keyboard;
import com.jdh.microcraft.util.Window;
import java.awt.*;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.Paths;
import java.nio.file.spi.FileSystemProvider;
import java.util.Map;
import java.util.Optional;
public class Main implements Runnable {
public static void main(String[] args) {
System.out.println("CWD is " + Paths.get("").toAbsolutePath().toString());
// initialize FS
Optional<URI> testURI = Optional.ofNullable(Main.class.getResource("/tiles.png")).map(url -> {
try {
return url.toURI();
} catch (URISyntaxException e) {
throw new Error(e);
}
});
if (testURI.isPresent() && testURI.get().getScheme().equals("jar")) {
for (FileSystemProvider provider : FileSystemProvider.installedProviders()) {
if (provider.getScheme().equalsIgnoreCase("jar")) {
try {
provider.getFileSystem(testURI.get());
} catch (FileSystemNotFoundException e) {
try {
provider.newFileSystem(testURI.get(), Map.of());
} catch (IOException ioe) {
throw new Error(ioe);
}
}
}
}
}
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
Window.init("MICROCRAFT", screen.width / 2, screen.height / 2);
new Thread(new Main()).start();
}
@Override
public void run() {
try {
Window.loop(
this::init,
this::destroy,
this::tick,
this::update,
this::render
);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
private void init() {
Global.mainMenu = new MainMenuState();
Global.currentState = Global.mainMenu;
}
private void destroy() {
}
private void tick() {
Global.ticks++;
Keyboard.tick();
Global.currentState.tick();
}
private void update() {
Keyboard.update();
Global.currentState.update();
}
private void render() {
Global.frames++;
Renderer.clear();
Global.currentState.render();
}
}

View File

@@ -0,0 +1,45 @@
package com.jdh.microcraft;
import com.jdh.microcraft.gui.ColorSelectMenu;
import com.jdh.microcraft.gui.DifficultySelectMenu;
import com.jdh.microcraft.gui.Menu;
import com.jdh.microcraft.gui.mainmenu.AboutMenu;
import com.jdh.microcraft.gui.mainmenu.HowToPlayMenu;
import com.jdh.microcraft.gui.mainmenu.LoadingMenu;
import com.jdh.microcraft.gui.mainmenu.MainMenu;
public class MainMenuState implements State {
public Menu menu;
public MainMenu mainMenu;
public LoadingMenu loadingMenu;
public HowToPlayMenu howToPlayMenu;
public AboutMenu aboutMenu;
public ColorSelectMenu colorSelectMenu;
public DifficultySelectMenu difficultySelectMenu;
public MainMenuState() {
this.mainMenu = new MainMenu();
this.loadingMenu = new LoadingMenu();
this.howToPlayMenu = new HowToPlayMenu();
this.aboutMenu = new AboutMenu();
this.colorSelectMenu = new ColorSelectMenu();
this.difficultySelectMenu = new DifficultySelectMenu();
this.menu = this.mainMenu;
}
@Override
public void tick() {
this.menu.tick();
}
@Override
public void update() {
this.menu.update();
}
@Override
public void render() {
this.menu.render();
}
}

View File

@@ -0,0 +1,7 @@
package com.jdh.microcraft;
public interface State {
void tick();
void update();
void render();
}

View File

@@ -0,0 +1,235 @@
package com.jdh.microcraft.entity;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.ai.AI;
import com.jdh.microcraft.gfx.Light;
import com.jdh.microcraft.level.Level;
import com.jdh.microcraft.level.LevelEntityMetadata;
import com.jdh.microcraft.level.tile.Tile;
import com.jdh.microcraft.level.tile.TileLiquid;
import com.jdh.microcraft.level.tile.TileStair;
import com.jdh.microcraft.util.Direction;
import com.jdh.microcraft.util.FMath;
import java.util.List;
public class Entity {
// current location
public int x, y;
// size
public int width, height;
// level this entity belongs to
public Level level;
// set to true on ticks where entity moves
public boolean moving;
// set to true if the entity is in a liquid
public boolean swimming;
// true if the entity is currently on stairs (onStairs) or has entered stairs this tick (onStairsTick)
public boolean onStairs;
// metadata about this entity, tracked by the current level
public final LevelEntityMetadata metadata;
// current tile locations, managed by Level
// location of CENTER of entity
public int tileX = -1, tileY = -1;
// unique entity id
public final int id;
// entity AI, optional
public AI ai;
// current direction and direction last tick
protected Direction direction = Direction.DOWN;
private Direction lastDirection;
// positions at the LAST tick
private int lastX, lastY;
// knockback velocity
private double kx, ky;
public Entity(Level level) {
this.level = level;
this.id = Global.game.getNextEntityId();
this.width = 6;
this.height = 6;
this.metadata = new LevelEntityMetadata(this);
}
public Direction getDirection() {
return this.direction;
}
public Light getLight() {
return null;
}
public void remove() {
this.metadata.remove = true;
}
// schedules this entity to be moved to another level on the next tick
public void moveLevel(int depth, int x, int y) {
this.metadata.moveLevel(depth, x, y);
}
// called when this entity moves between levels
public void onDepthChange(int prevDepth) {
}
public void knockback(double strength, Direction d) {
this.knockback(strength, d.x, d.y);
}
public void knockback(double strength, int x, int y) {
this.kx = Math.signum(x) * strength;
this.ky = Math.signum(y) * strength;
}
public int getRenderOffsetX() {
return 0;
}
public int getRenderOffsetY() {
return 0;
}
protected void onDirectionChange() {
}
public void tick() {
this.moving = this.lastX != this.x || this.lastY != this.y;
int dx = this.x - this.lastX, dy = this.y - this.lastY;
if (dx != 0 || dy != 0) {
this.direction = Direction.get(dx, dy);
}
if (this.direction != this.lastDirection) {
this.onDirectionChange();
}
this.lastDirection = this.direction;
this.lastX = this.x;
this.lastY = this.y;
this.swimming = Tile.TILES[this.level.getTile(this.tileX, this.tileY)] instanceof TileLiquid;
this.onStairs = Tile.TILES[this.level.getTile(this.tileX, this.tileY)] instanceof TileStair;
// apply knockback
this.move(
FMath.tickedDoubleToInt(Global.ticks, this.kx),
FMath.tickedDoubleToInt(Global.ticks, this.ky)
);
this.kx *= 0.85;
this.ky *= 0.85;
if (this.ai != null) {
this.ai.tick();
}
}
public void update() {
if (this.ai != null) {
this.ai.update();
}
}
public void render() {
}
public boolean collides(Entity e) {
return true;
}
public void collide(Entity e) {
}
// called if THIS entity hits E
public boolean hit(Entity e) {
return false;
}
// called when E hits THIS entity
public void onHit(Entity e) {
}
public boolean interact(Entity e) {
return false;
}
public void move(int dx, int dy) {
this.moveAxis(dx, 0);
this.moveAxis(0, dy);
}
// moves along a single axis
protected boolean moveAxis(int dx, int dy) {
assert(dx == 0 || dy == 0);
int ox = this.x, oy = this.y;
this.x += dx;
this.y += dy;
// deny any movement that would move this entity off of the map
if (this.x < 0 || this.y < 0 ||
(this.x + this.width) >= Level.toPixel(this.level.width) ||
(this.y + this.height >= Level.toPixel(this.level.height))) {
this.x = ox;
this.y = oy;
return false;
}
List<Entity> entities = level.getEntityCollisions(this);
for (Entity e : entities) {
if (e != this && this.collides(e) && e.collides(this)) {
this.collide(e);
e.collide(this);
// deny movement if it is TOWARDS the colliding entity
if ((dx != 0 && FMath.sameSign(dx, e.x - this.x)) ||
(dy != 0 && FMath.sameSign(dy, e.y - this.y))) {
this.x = ox;
this.y = oy;
return false;
}
}
}
for (int[] p : level.getTileCollisions(this)) {
Tile t = Tile.TILES[level.getTile(p[0], p[1])];
t.bump(level, p[0], p[1], this);
if (t.collides(level, p[0], p[1], this)) {
this.x = ox;
this.y = oy;
return false;
}
}
return true;
}
public boolean canSwimIn(Tile tile, int x, int y) {
return false;
}
public int getCenterX() {
return this.x + (this.width / 2);
}
public int getCenterY() {
return this.y + (this.height / 2);
}
}

View File

@@ -0,0 +1,115 @@
package com.jdh.microcraft.entity;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.mob.EntityMob;
import com.jdh.microcraft.entity.projectile.EntityProjectile;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.level.Level;
import com.jdh.microcraft.util.Direction;
import com.jdh.microcraft.util.Time;
public class EntityItem extends Entity {
public final ItemStack stack;
public int timeToLive, timeToPickup;
private int z;
private double vx, vy, vz, dx, dy;
public EntityItem(Level level, ItemStack stack, int x, int y, double vx, double vy, double vz) {
super(level);
this.stack = stack;
this.x = x;
this.y = y;
this.z = 4;
this.vx = vx;
this.vy = vy;
this.vz = vz;
this.dx = 0;
this.dy = 0;
this.timeToLive = Time.TPS * (180 + Global.random.nextInt(60));
this.timeToPickup = 20;
this.width = 8;
this.height = 8;
}
@Override
public boolean collides(Entity e) {
return e instanceof EntityMob ?
((EntityMob) e).canPickup(this) && this.timeToPickup == 0 :
!(e instanceof EntityItem);
}
public static EntityItem spawn(Level level, ItemStack stack, int x, int y, Direction d) {
Global.random.setSeed(stack.hashCode() + (x * 31) ^ (y * 17));
EntityItem result = new EntityItem(
level, stack, x, y,
d.x * (0.5 + Global.random.nextDouble() * 0.1),
d.y * (0.5 + Global.random.nextDouble() * 0.1),
1.0 + (Global.random.nextDouble() * 0.2)
);
level.addEntity(result);
return result;
}
public static EntityItem spawn(Level level, ItemStack stack, int x, int y) {
Global.random.setSeed(stack.hashCode() + (x * 31) ^ (y * 17));
EntityItem result = new EntityItem(
level, stack, x, y,
(Global.random.nextBoolean() ? -1.0 : 1.0) * (0.3 + Global.random.nextDouble() * 0.1),
(Global.random.nextBoolean() ? -1.0 : 1.0) * (0.3 + Global.random.nextDouble() * 0.1),
1.0 + (Global.random.nextDouble() * 0.2)
);
level.addEntity(result);
return result;
}
public boolean canPickup(Entity e) {
return this.timeToPickup == 0;
}
@Override
public void tick() {
super.tick();
if (--this.timeToLive == 0) {
this.remove();
}
this.timeToPickup = Math.max(this.timeToPickup - 1, 0);
this.dx += this.vx;
this.dy += this.vy;
while (Math.abs(this.dx) >= 1.0) {
if (!this.moveAxis((int) Math.signum(this.dx), 0)) {
this.vx *= -0.8;
}
this.dx += -Math.signum(this.dx);
}
while (Math.abs(this.dy) >= 1.0) {
if (!this.moveAxis(0, (int) Math.signum(this.dy))) {
this.vy *= -0.8;
}
this.dy += -Math.signum(this.dy);
}
this.z += this.vz;
// bounce
if (this.z < 0) {
this.z = 0;
this.vx *= 0.4;
this.vy *= 0.4;
this.vz *= -0.6;
}
// gravity
this.vz -= 0.2;
}
@Override
public void render() {
this.stack.instance.item.render(this.stack.instance, this.level, this.x, this.y);
}
}

View File

@@ -0,0 +1,289 @@
package com.jdh.microcraft.entity;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.mob.EntityHumanoid;
import com.jdh.microcraft.entity.particle.EntitySmashParticle;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.gfx.Light;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.gui.PlayerInventoryMenu;
import com.jdh.microcraft.gui.TransitionMenu;
import com.jdh.microcraft.gui.crafting.InventoryCraftingMenu;
import com.jdh.microcraft.item.Inventory;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.consumable.ItemConsumable;
import com.jdh.microcraft.item.tool.ItemTool;
import com.jdh.microcraft.level.Level;
import com.jdh.microcraft.level.tile.Tile;
import com.jdh.microcraft.sound.Sound;
import com.jdh.microcraft.util.ControlHandler;
import com.jdh.microcraft.util.Direction;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class EntityPlayer extends EntityHumanoid {
// maintains a list of the previous N equipped items, used so when
// this.equipped is null we can go back to the last existing item from this
// list in the player's current inventory. fx.:
// - player has sword equipped
// - player picks up lantern, lantern is equipped
// - player puts down lantern
// - sword should be re-equipped
private final List<Integer> lastEquippedItems;
private final int color;
public EntityPlayer(Level level, int color) {
super(level, new Inventory(256));
this.lastEquippedItems = new ArrayList<>();
this.color = color;
ItemStack glove = new ItemStack(new ItemInstance(Item.GLOVE, 0), 1);
this.inventory.add(glove);
this.equipped = glove;
}
@Override
public boolean hurt(int amount, boolean ignoreArmor) {
if (super.hurt(amount, ignoreArmor)) {
Sound.PLAYER_HURT.play();
return true;
}
return false;
}
@Override
public Light getLight() {
Light parent = super.getLight();
return parent != null ? parent : new Light(
this.x + (this.width / 2),
this.y + (this.height / 2),
2
);
}
@Override
public void die() {
super.die();
EntitySmashParticle.spawn(
this.level,
this.getCenterX(), this.getCenterY(),
this.getColor(),
10, 30
);
Global.game.playerDiedTicks = Global.ticks;
}
@Override
public boolean canPickup(EntityItem e) {
return true;
}
@Override
public void pickup(ItemStack stack) {
super.pickup(stack);
Sound.PICKUP.play();
Global.game.score++;
}
@Override
public void onDepthChange(int prevDepth) {
super.onDepthChange(prevDepth);
Global.game.setLevel(this.level.depth);
Global.game.setMenu(new TransitionMenu());
}
@Override
public void tick() {
super.tick();
// track currently equipped item
if (this.equipped != null &&
(this.lastEquippedItems.size() == 0 ||
this.lastEquippedItems.get(0) != this.equipped.instance.id)) {
this.lastEquippedItems.add(0, this.equipped.instance.id);
}
// if nothing is equipped, try to equip previously equipped items
// if there are no previous items to equip, equip the POW GLOVE
if (this.equipped == null) {
while (this.lastEquippedItems.size() != 0 && this.equipped == null) {
this.equipped = this.inventory.findByInstanceId(
this.lastEquippedItems.remove(0));
}
if (this.equipped == null) {
this.equipped = this.inventory.find(Item.GLOVE);
}
}
// crafting menu
if (ControlHandler.TOGGLE_CRAFTING.pressedTick()) {
Global.game.setMenu(new InventoryCraftingMenu(this));
return;
}
// item dropping
if (ControlHandler.DROP.pressedTick() &&
this.equipped != null &&
this.equipped.instance.item.isDroppable()) {
this.drop(this.equipped);
}
// get tile player is currently facing
int fx = this.getFacingTileX(), fy = this.getFacingTileY();
Tile ft = Tile.TILES[level.getTile(fx, fy)];
// entities in this tile and the tile being faced, excluding this one
List<Entity> facingEntities = Stream.concat(
level.getEntities(this.tileX, this.tileY).stream(),
level.getEntities(fx, fy).stream()
).filter(e -> e != this).collect(Collectors.toList());
if (ControlHandler.INTERACT.pressedTick()) {
boolean interactEntity = false, interactTile = false;
if (!this.swimming) {
// try to interact with facing entities
for (Entity e : facingEntities) {
interactEntity |= e.interact(this);
}
// interact with facing tile if no entities were interacted with
if (!interactEntity) {
interactTile = ft.interact(this.level, fx, fy, this);
}
}
// toggle inventory
if (!interactEntity && !interactTile) {
Global.game.setMenu(new PlayerInventoryMenu(this, true));
return;
}
}
if (!this.swimming && ControlHandler.HIT.pressedTick()) {
this.updateAnimationFrame(true);
boolean hitEntity = false;
for (Entity e : facingEntities) {
if (this.hit(e)) {
e.onHit(this);
hitEntity = true;
}
}
if (hitEntity) {
this.animateAction();
} else if (this.equipped.instance.item instanceof ItemConsumable) {
this.equipped.instance.item.use(this.equipped.instance, level, this.tileX, this.tileY, this);
} else if (this.equipped.instance.item instanceof ItemTool) {
// try to use the active tool on the facing tile
ItemTool tool = (ItemTool) this.equipped.instance.item;
boolean ideal = (ft.getIdealTools() & tool.type) != 0,
usable = (ft.getUsableTools() & tool.type) != 0;
if (ideal || usable) {
int staminaCost = (int) (tool.getStaminaCostMultiplier() *
(ideal ?
(1 + Global.random.nextInt(2)) :
(3 + Global.random.nextInt(3))));
if (this.takeStamina(staminaCost)) {
Item equippedItem = this.equipped.instance.item;
if (ft.hit(this.level, fx, fy, this)) {
this.animateHit(equippedItem, fx, fy);
} else {
// give stamina back if hit failed
this.stamina += staminaCost;
}
}
}
} else {
// try to use the active item on the facing tile
Item equippedItem = this.equipped.instance.item;
if (equippedItem.use(this.equipped.instance, level, fx, fy, this)) {
this.animateHit(equippedItem, fx, fy);
}
}
if (this.hitTicks == 0 && !hitEntity) {
// no hit? animate an action
this.animateAction();
Sound.MISS.play();
} else {
Sound.HIT.play();
}
}
int dx = 0, dy = 0;
if (ControlHandler.UP.down()) {
dy--;
}
if (ControlHandler.DOWN.down()) {
dy++;
}
if (ControlHandler.LEFT.down()) {
dx--;
}
if (ControlHandler.RIGHT.down()) {
dx++;
}
if (!this.swimming || Global.ticks % 2 == 0) {
this.move(dx, dy);
}
// override entity direction, the player should be able to change their
// direction even when stationary
if (dx != 0 || dy != 0) {
this.direction = Direction.get(dx, dy);
}
// center camera on the player
this.updateCamera();
}
// updates current renderer camera to track the player
public void updateCamera() {
Renderer.camera.centerOn(
this.x + (this.width / 2), this.y + (this.height / 2),
0, 0,
Level.toPixel(this.level.width), Level.toPixel(this.level.height)
);
}
@Override
public double getStaminaRechargeRate() {
return 0.11;
}
@Override
public int getMaxStamina() {
return 10;
}
@Override
public int getMaxHealth() {
return 10;
}
@Override
public int getColor() {
return this.color;
}
@Override
public int getHurtColor() {
return Color.addAll(this.color, 222);
}
}

View File

@@ -0,0 +1,61 @@
package com.jdh.microcraft.entity;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.mob.EntitySkeleton;
import com.jdh.microcraft.entity.mob.EntitySlime;
import com.jdh.microcraft.entity.mob.EntityZombie;
import com.jdh.microcraft.level.Level;
import java.util.List;
import java.util.function.Function;
public class SpawnProperties {
public final Class<? extends Entity> cls;
public final Function<Level, Entity> spawnFunction;
public final int chance, cap;
// chance is spawn rate/minute
// cap and chance are per 128x128 area
public SpawnProperties(Level level, Class<? extends Entity> cls, Function<Level, Entity> spawnFunction, int chance, int cap) {
this.cls = cls;
this.spawnFunction = spawnFunction;
double scale = ((level.width * level.height) / (128.0 * 128.0)) * Global.game.difficulty;
this.chance = (int) (chance * scale);
this.cap = (int) (cap * scale);
}
public static List<SpawnProperties> getSpawnProperties(Level level) {
return switch (level.depth) {
case 1 -> List.of(
new SpawnProperties(level, EntitySlime.class, EntitySlime::createBlueSlime, 50, 4),
new SpawnProperties(level, EntitySkeleton.class, EntitySkeleton::createSkeleton, 100, 4)
);
case 0 -> List.of(
new SpawnProperties(level, EntitySlime.class, EntitySlime::createGreenSlime, 300, 24),
new SpawnProperties(level, EntityZombie.class, EntityZombie::createEasyZombie, 80, 3)
);
case -1 -> List.of(
new SpawnProperties(level, EntitySlime.class, EntitySlime::createGreenSlime, 200, 24),
new SpawnProperties(level, EntitySlime.class, EntitySlime::createBlueSlime, 100, 4),
new SpawnProperties(level, EntityZombie.class, EntityZombie::createEasyZombie, 200, 20),
new SpawnProperties(level, EntitySkeleton.class, EntitySkeleton::createSkeleton, 200, 24)
);
case -2 -> List.of(
new SpawnProperties(level, EntitySlime.class, EntitySlime::createGreenSlime, 200, 32),
new SpawnProperties(level, EntitySlime.class, EntitySlime::createBlueSlime, 220, 32),
new SpawnProperties(level, EntitySlime.class, EntitySlime::createRedSlime, 80, 4),
new SpawnProperties(level, EntityZombie.class, EntityZombie::createMediumZombie, 200, 24),
new SpawnProperties(level, EntitySkeleton.class, EntitySkeleton::createSkeleton, 250, 24)
);
case -3 -> List.of(
new SpawnProperties(level, EntitySlime.class, EntitySlime::createGreenSlime, 100, 32),
new SpawnProperties(level, EntitySlime.class, EntitySlime::createBlueSlime, 100, 32),
new SpawnProperties(level, EntitySlime.class, EntitySlime::createRedSlime, 400, 32),
new SpawnProperties(level, EntityZombie.class, EntityZombie::createHardZombie, 250, 32),
new SpawnProperties(level, EntitySkeleton.class, EntitySkeleton::createSkeleton, 250, 32)
);
default -> List.of();
};
}
}

View File

@@ -0,0 +1,19 @@
package com.jdh.microcraft.entity.ai;
import com.jdh.microcraft.entity.Entity;
public class AI {
protected final Entity entity;
public AI(Entity entity) {
this.entity = entity;
}
public void tick() {
}
public void update() {
}
}

View File

@@ -0,0 +1,54 @@
package com.jdh.microcraft.entity.ai;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.attack.WizardAttackBeam;
import com.jdh.microcraft.entity.attack.WizardAttackBurst;
import com.jdh.microcraft.entity.attack.WizardAttackSpiral;
import com.jdh.microcraft.entity.mob.EntityAirWizard;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.sound.Sound;
import com.jdh.microcraft.util.Direction;
public class AIAirWizard extends AIHostileHumanoid {
private long lastAttack;
private final EntityAirWizard wizard;
private Direction randomDirection;
public AIAirWizard(EntityAirWizard wizard) {
super(wizard, 0.75, 3.0 * wizard.strength);
this.wizard = wizard;
this.lastAttack = Global.ticks;
}
@Override
public void tick() {
if (this.wizard.attack == null) {
super.tick();
} else {
// move randomly
if (this.randomDirection == null || Global.random.nextInt(80) == 0) {
this.randomDirection = Direction.ALL.get(Global.random.nextInt(4));
}
this.moveTowards(
this.wizard.x + this.randomDirection.x * 10,
this.wizard.y + this.randomDirection.y * 10
);
}
if ((Global.ticks - this.lastAttack) > 120 + Global.random.nextInt(480)) {
this.wizard.attack = switch (Global.random.nextInt(3)) {
case 0 -> new WizardAttackBurst(this.wizard);
case 1 -> new WizardAttackSpiral(this.wizard);
default -> new WizardAttackBeam(this.wizard);
};
if (Renderer.inBounds(this.wizard.getCenterX(), this.wizard.getCenterY())) {
Sound.WIZARD_ATTACK.play();
}
this.lastAttack = Global.ticks;
}
}
}

View File

@@ -0,0 +1,68 @@
package com.jdh.microcraft.entity.ai;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.mob.EntityHumanoid;
import com.jdh.microcraft.util.Direction;
import com.jdh.microcraft.util.FMath;
public class AIHostileHumanoid extends AIHostileMob {
private final EntityHumanoid humanoid;
private final double speed, strength;
private Direction randomMoveDirection;
public AIHostileHumanoid(EntityHumanoid humanoid, double speed, double strength) {
super(humanoid, strength);
this.humanoid = humanoid;
this.speed = speed;
this.strength = strength;
}
@Override
protected void moveTowards(int x, int y) {
double dx = FMath.sign(x - this.humanoid.x),
dy = FMath.sign(y - this.humanoid.y),
l = FMath.norm(dx, dy);
dx /= l;
dy /= l;
dx = Double.isNaN(dx) ? 0 : dx;
dy = Double.isNaN(dy) ? 0 : dy;
dx *= this.speed;
dy *= this.speed;
if (!this.humanoid.swimming || Global.ticks % 2 == 0) {
int mx = FMath.tickedDoubleToInt(Global.ticks, dx),
my = FMath.tickedDoubleToInt(Global.ticks, dy);
if (this.shouldMove(mx, 0)) {
this.humanoid.move(mx, 0);
}
if (this.shouldMove(0, my)) {
this.humanoid.move(0, my);
}
}
}
@Override
public void tick() {
super.tick();
if (this.target == null) {
if (this.randomMoveDirection == null && Global.random.nextInt(240) == 0) {
this.randomMoveDirection = Direction.ALL.get(Global.random.nextInt(4));
} else if (this.randomMoveDirection != null) {
this.moveTowards(
this.humanoid.x + this.randomMoveDirection.x * 10,
this.humanoid.y + this.randomMoveDirection.y * 10
);
if (Global.random.nextInt(120) == 0) {
this.randomMoveDirection = null;
}
}
}
}
}

View File

@@ -0,0 +1,95 @@
package com.jdh.microcraft.entity.ai;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.level.Level;
import com.jdh.microcraft.level.tile.Tile;
import com.jdh.microcraft.level.tile.TileLava;
import com.jdh.microcraft.util.Direction;
import com.jdh.microcraft.util.FMath;
import com.jdh.microcraft.util.Time;
import java.util.ArrayList;
import java.util.stream.Collectors;
public abstract class AIHostileMob extends AI {
protected final double sightDistance;
protected final int targetPermanenceTicks;
protected final double strength;
protected int ticksSinceTargetSeen;
public Entity target;
public AIHostileMob(Entity entity, double strength) {
super(entity);
this.strength = strength;
this.sightDistance = (16 * 10) * this.strength;
this.targetPermanenceTicks = (int) (180 * this.strength);
}
// avoid really dangerous stuff
protected boolean shouldMove(int dx, int dy) {
int cx = this.entity.getCenterX() + dx,
cy = this.entity.getCenterY() + dy,
tx = Level.toTile(cx),
ty = Level.toTile(cy);
return !(Tile.TILES[this.entity.level.getTile(tx, ty)] instanceof TileLava);
}
protected abstract void moveTowards(int x, int y);
@Override
public void tick() {
super.tick();
if (this.entity.level.player != null &&
FMath.norm(this.entity.x - this.entity.level.player.x,
this.entity.y - this.entity.level.player.y) <= this.sightDistance) {
this.target = this.entity.level.player;
this.ticksSinceTargetSeen = 0;
} else {
this.ticksSinceTargetSeen++;
// lose target if it has been too long
if (this.ticksSinceTargetSeen >= this.targetPermanenceTicks) {
this.target = null;
}
}
if (this.target != null) {
int tcx = this.target.x + (this.target.width / 2),
tcy = this.target.y + (this.target.height / 2),
tdx = tcx - this.entity.x,
tdy = tcy - this.entity.y;
double pd = this.getPreferredTargetDistance(),
pdt = this.getPreferredTargetDistanceThreshold();
if (Math.abs(tdx) - pd > pdt ||
Math.abs(tdy) - pd > pdt) {
this.moveTowards(this.target.x, this.target.y);
} else if (pd != 0.0 && pdt != 0.0) {
// ranged mobs: strafe in directions not towards the target
Global.random.setSeed(Global.ticks / Time.TPS);
if (Global.random.nextInt(3) == 0) {
Direction td = Direction.get(tdx, tdy);
Direction strafeDir = new ArrayList<>(Direction.ALL)
.stream()
.filter(d -> d != td && d != td.opposite())
.collect(Collectors.toList())
.get(Global.random.nextInt(2));
this.moveTowards(this.entity.x + strafeDir.x, this.entity.y + strafeDir.y);
}
}
}
}
protected double getPreferredTargetDistance() {
return 0.0;
}
protected double getPreferredTargetDistanceThreshold() {
return 0.0;
}
}

View File

@@ -0,0 +1,29 @@
package com.jdh.microcraft.entity.ai;
import com.jdh.microcraft.entity.mob.EntitySkeleton;
public class AISkeleton extends AIHostileHumanoid {
private final EntitySkeleton skeleton;
public AISkeleton(EntitySkeleton skeleton) {
super(skeleton, 0.5 * skeleton.strength, skeleton.strength);
this.skeleton = skeleton;
}
@Override
public void tick() {
super.tick();
if (this.target != null) {
this.skeleton.shoot(this.target.x, this.target.y);
}
}
protected double getPreferredTargetDistance() {
return 16 * 3;
}
protected double getPreferredTargetDistanceThreshold() {
return 24;
}
}

View File

@@ -0,0 +1,41 @@
package com.jdh.microcraft.entity.ai;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.mob.EntitySlime;
import com.jdh.microcraft.util.Direction;
public class AISlime extends AIHostileMob {
private final EntitySlime slime;
private long lastJump;
private final int jumpDelay;
public AISlime(EntitySlime slime) {
super(slime, slime.strength);
this.slime = slime;
this.jumpDelay = (int) Math.max(140 - (20 * slime.strength), 60);
}
private boolean canJump() {
return Global.ticks - this.lastJump >= this.jumpDelay;
}
@Override
protected void moveTowards(int x, int y) {
if (!this.slime.jumping && this.canJump()) {
this.lastJump = Global.ticks;
this.slime.jump(x - this.slime.x, y - this.slime.y, 0.5 * slime.strength);
}
}
@Override
public void tick() {
super.tick();
Global.random.setSeed(Global.ticks * this.slime.id);
if (this.target == null && Global.random.nextInt(180) == 0) {
this.lastJump = Global.ticks;
this.slime.jump(Direction.ALL.get(Global.random.nextInt(4)), 0.5);
}
}
}

View File

@@ -0,0 +1,9 @@
package com.jdh.microcraft.entity.ai;
import com.jdh.microcraft.entity.mob.EntityZombie;
public class AIZombie extends AIHostileHumanoid {
public AIZombie(EntityZombie zombie) {
super(zombie, 0.45 * zombie.strength, zombie.strength);
}
}

View File

@@ -0,0 +1,21 @@
package com.jdh.microcraft.entity.attack;
import com.jdh.microcraft.entity.mob.EntityAirWizard;
public abstract class WizardAttack {
protected final EntityAirWizard wizard;
public int time;
public WizardAttack(EntityAirWizard wizard, int time) {
this.wizard = wizard;
this.time = time;
}
public boolean done() {
return this.time <= 0;
}
public void tick() {
this.time--;
}
}

View File

@@ -0,0 +1,33 @@
package com.jdh.microcraft.entity.attack;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.ai.AIHostileMob;
import com.jdh.microcraft.entity.mob.EntityAirWizard;
import com.jdh.microcraft.entity.projectile.EntityAirBlast;
public class WizardAttackBeam extends WizardAttack {
public WizardAttackBeam(EntityAirWizard wizard) {
super(wizard, 60);
}
@Override
public void tick() {
super.tick();
if ((Global.ticks % 2) != 0) {
return;
}
AIHostileMob ai = (AIHostileMob) this.wizard.ai;
if (ai.target != null) {
this.wizard.level.addEntity(
new EntityAirBlast(
this.wizard.level, this.wizard,
this.wizard.getCenterX(), this.wizard.getCenterY(),
ai.target.getCenterX(), ai.target.getCenterY(),
1.4,
(int) (3 * this.wizard.strength))
);
}
}
}

View File

@@ -0,0 +1,42 @@
package com.jdh.microcraft.entity.attack;
import com.jdh.microcraft.entity.mob.EntityAirWizard;
import com.jdh.microcraft.entity.projectile.EntityAirBlast;
public class WizardAttackBurst extends WizardAttack {
private static final int SIZE = 32;
private boolean attacked = false;
public WizardAttackBurst(EntityAirWizard wizard) {
super(wizard, 8);
}
@Override
public void tick() {
super.tick();
if (this.attacked) {
return;
}
this.attacked = true;
for (int i = 0; i < SIZE; i++) {
int dx = (int) (Math.cos((i / (double) SIZE) * (2 * Math.PI)) * 16),
dy = (int) (Math.sin((i / (double) SIZE) * (2 * Math.PI)) * 16);
this.wizard.level.addEntity(
new EntityAirBlast(
this.wizard.level, this.wizard,
this.wizard.getCenterX() + dx,
this.wizard.getCenterY() + dy,
this.wizard.getCenterX() + dx * 10,
this.wizard.getCenterY() + dy * 10,
1.2,
(int) (3 * this.wizard.strength)
)
);
}
}
}

View File

@@ -0,0 +1,35 @@
package com.jdh.microcraft.entity.attack;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.mob.EntityAirWizard;
import com.jdh.microcraft.entity.projectile.EntityAirBlast;
public class WizardAttackSpiral extends WizardAttack {
public WizardAttackSpiral(EntityAirWizard wizard) {
super(wizard, 240);
}
@Override
public void tick() {
super.tick();
if ((Global.ticks % 2) != 0) {
return;
}
int dx = (int) (Math.cos(((Global.ticks % 60) / 60.0) * (2 * Math.PI)) * 16),
dy = (int) (Math.sin(((Global.ticks % 60) / 60.0) * (2 * Math.PI)) * 16);
this.wizard.level.addEntity(
new EntityAirBlast(
this.wizard.level, this.wizard,
this.wizard.getCenterX() + dx,
this.wizard.getCenterY() + dy,
this.wizard.getCenterX() + dx * 10,
this.wizard.getCenterY() + dy * 10,
1.4,
(int) (3 * this.wizard.strength)
)
);
}
}

View File

@@ -0,0 +1,24 @@
package com.jdh.microcraft.entity.furniture;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.gui.crafting.AnvilCraftingMenu;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.level.Level;
public class EntityAnvil extends EntityFurniture {
public EntityAnvil(Level level) {
super(level, Item.ANVIL, 15, 10);
}
@Override
public boolean interact(Entity e) {
if (!(e instanceof EntityPlayer)) {
return false;
}
Global.game.setMenu(new AnvilCraftingMenu((EntityPlayer) e, this));
return true;
}
}

View File

@@ -0,0 +1,42 @@
package com.jdh.microcraft.entity.furniture;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.EntityItem;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.gui.ChestMenu;
import com.jdh.microcraft.item.Inventory;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.level.Level;
public class EntityChest extends EntityFurniture {
public Inventory inventory = new Inventory(512);
public EntityChest(Level level) {
super(level, Item.CHEST, 15, 13);
}
@Override
public boolean hit(Entity e) {
if (super.hit(e)) {
for (ItemStack s : this.inventory.stacks) {
EntityItem.spawn(this.level, s, Level.toCenter(e.tileX), Level.toCenter(e.tileY));
}
return true;
}
return false;
}
@Override
public boolean interact(Entity e) {
if (!(e instanceof EntityPlayer)) {
return false;
}
Global.game.setMenu(new ChestMenu((EntityPlayer) e, this));
return true;
}
}

View File

@@ -0,0 +1,24 @@
package com.jdh.microcraft.entity.furniture;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.gui.crafting.BenchCraftingMenu;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.level.Level;
public class EntityCraftingBench extends EntityFurniture {
public EntityCraftingBench(Level level) {
super(level, Item.CRAFTING_BENCH, 15, 12);
}
@Override
public boolean interact(Entity e) {
if (!(e instanceof EntityPlayer)) {
return false;
}
Global.game.setMenu(new BenchCraftingMenu((EntityPlayer) e, this));
return true;
}
}

View File

@@ -0,0 +1,24 @@
package com.jdh.microcraft.entity.furniture;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.gui.crafting.FurnaceMenu;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.level.Level;
public class EntityFurnace extends EntityFurniture {
public EntityFurnace(Level level) {
super(level, Item.FURNACE, 15, 14);
}
@Override
public boolean interact(Entity e) {
if (!(e instanceof EntityPlayer)) {
return false;
}
Global.game.setMenu(new FurnaceMenu((EntityPlayer) e, this));
return true;
}
}

View File

@@ -0,0 +1,106 @@
package com.jdh.microcraft.entity.furniture;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.EntityItem;
import com.jdh.microcraft.entity.mob.EntityMob;
import com.jdh.microcraft.entity.projectile.EntityProjectile;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.furniture.ItemFurniture;
import com.jdh.microcraft.level.Level;
import com.jdh.microcraft.level.tile.Tile;
import com.jdh.microcraft.util.Direction;
import java.util.List;
public abstract class EntityFurniture extends Entity {
private final ItemFurniture item;
private int pushTime;
public EntityFurniture(Level level, ItemFurniture item, int width, int height) {
super(level);
this.item = item;
this.width = width;
this.height = height;
}
@Override
public void render() {
super.render();
for (int i = 0; i <= 1; i++) {
for (int j = 0; j <= 1; j++) {
Renderer.render(
this.getSpriteOffsetX() + i, this.getSpriteOffsetY() + j,
this.x + this.getRenderOffsetX() + (i * 8),
this.y + this.getRenderOffsetY() + (j * 8),
this.getColor(), Renderer.FLIP_NONE
);
}
}
}
@Override
public boolean collides(Entity e) {
return true;
}
@Override
public void collide(Entity e) {
super.collide(e);
if (e instanceof EntityMob) {
this.pushTime++;
if (this.pushTime == 3) {
// push in the direction the mob is moving
EntityMob mob = ((EntityMob) e);
Direction d = mob.getDirection();
this.move(d.x, d.y);
this.pushTime = 0;
}
} else if (e instanceof EntityProjectile) {
for (ItemStack s : this.getDrops()) {
EntityItem.spawn(level, s, Level.toCenter(this.tileX), Level.toCenter(this.tileY));
}
this.remove();
}
}
@Override
public void onHit(Entity e) {
if (!(e instanceof EntityMob)) {
return;
}
for (ItemStack s : this.getDrops()) {
EntityItem.spawn(level, s, Level.toCenter(this.tileX), Level.toCenter(this.tileY));
}
this.remove();
}
@Override
public boolean canSwimIn(Tile tile, int x, int y) {
return false;
}
protected List<ItemStack> getDrops() {
return List.of(new ItemStack(new ItemInstance(this.item)));
}
public int getSpriteOffsetX() {
return this.item.getTileSpriteX();
}
public int getSpriteOffsetY() {
return this.item.getTileSpriteY();
}
public int getColor() {
return this.item.getColor();
}
@Override
public abstract boolean interact(Entity e);
}

View File

@@ -0,0 +1,50 @@
package com.jdh.microcraft.entity.furniture;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.mob.EntityMob;
import com.jdh.microcraft.gfx.Light;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.level.Level;
public class EntityLantern extends EntityFurniture {
public static final int LIGHT_POWER = 6;
public EntityLantern(Level level) {
super(level, Item.LANTERN, 13, 13);
}
@Override
public int getRenderOffsetX() {
return -2;
}
@Override
public int getRenderOffsetY() {
return -3;
}
@Override
public Light getLight() {
return new Light(
this.getCenterX(),
this.getCenterY(),
LIGHT_POWER
);
}
@Override
public boolean interact(Entity e) {
ItemStack s = new ItemStack(new ItemInstance(Item.LANTERN, 0), 1);
if (e instanceof EntityMob && ((EntityMob) e).inventory.add(s)) {
EntityMob mob = (EntityMob) e;
mob.equipped = s;
this.remove();
return true;
}
return false;
}
}

View File

@@ -0,0 +1,24 @@
package com.jdh.microcraft.entity.furniture;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.gui.crafting.OvenMenu;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.level.Level;
public class EntityOven extends EntityFurniture {
public EntityOven(Level level) {
super(level, Item.OVEN, 15, 13);
}
@Override
public boolean interact(Entity e) {
if (!(e instanceof EntityPlayer)) {
return false;
}
Global.game.setMenu(new OvenMenu((EntityPlayer) e, this));
return true;
}
}

View File

@@ -0,0 +1,120 @@
package com.jdh.microcraft.entity.mob;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.entity.ai.AIAirWizard;
import com.jdh.microcraft.entity.attack.WizardAttack;
import com.jdh.microcraft.entity.particle.EntitySmokeParticle;
import com.jdh.microcraft.entity.projectile.EntityProjectile;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.gui.WinMenu;
import com.jdh.microcraft.item.Inventory;
import com.jdh.microcraft.level.Level;
import com.jdh.microcraft.level.tile.Tile;
import com.jdh.microcraft.sound.Sound;
public class EntityAirWizard extends EntityHumanoid {
public final double strength = 1.0 * Global.game.difficulty;
public WizardAttack attack;
public EntityAirWizard(Level level) {
super(level, Inventory.NONE);
this.health = this.getMaxHealth();
this.stamina = this.getMaxStamina();
this.ai = new AIAirWizard(this);
}
@Override
public void die() {
super.die();
Sound.WIZARD_DEATH.play();
Global.game.score += 5000;
Global.game.setMenu(new WinMenu());
}
@Override
public boolean hurt(int amount) {
return super.hurt(amount == 1 ? 0 : amount);
}
@Override
public boolean collides(Entity e) {
return !(e instanceof EntityProjectile) && super.collides(e);
}
@Override
public void collide(Entity e) {
if (e instanceof EntityPlayer) {
this.hit(e);
}
}
@Override
public void tick() {
super.tick();
if (Global.random.nextInt(10) == 0) {
EntitySmokeParticle.spawn(this.level, this.getCenterX(), this.getCenterY(), Tile.CLOUD.getColor(), 1, 4);
}
if (this.attack != null) {
if (this.attack.done()) {
this.attack = null;
} else {
this.attack.tick();
}
}
}
@Override
public int getHitDamage(Entity e) {
return (int) ((2 + Global.random.nextInt(5)) * this.strength);
}
@Override
public int getBaseSpriteX() {
return 8;
}
@Override
public int getBaseSpriteY() {
return 9;
}
@Override
public int getCarrySpriteX() {
return 8;
}
@Override
public int getCarrySpriteY() {
return 9;
}
@Override
public int getColor() {
return Color.get(111, 224, 441, 555);
}
@Override
public int getHurtColor() {
return Color.get(444, 335, 552, 555);
}
@Override
public double getStaminaRechargeRate() {
return 0.3;
}
@Override
public int getMaxStamina() {
return (int) (this.strength * 100);
}
@Override
public int getMaxHealth() {
return (int) (this.strength * 80);
}
}

View File

@@ -0,0 +1,335 @@
package com.jdh.microcraft.entity.mob;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.EntityItem;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.gfx.Light;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.item.Inventory;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.armor.*;
import com.jdh.microcraft.item.tool.ItemTool;
import com.jdh.microcraft.level.Level;
import com.jdh.microcraft.level.tile.Tile;
import com.jdh.microcraft.level.tile.TileLiquid;
import com.jdh.microcraft.util.Direction;
public abstract class EntityHumanoid extends EntityMob {
public static final int BASE_SPRITE_X = 0, BASE_SPRITE_Y = 11;
public static final int CARRY_SPRITE_X = 0, CARRY_SPRITE_Y = 9;
private static final int SWIM_SPRITE_X = 9, SWIM_SPRITE_Y = 11;
private static final int SWIM_COLOR = Color.get(334, 445, 555, 555);
// current sprite location
private int spriteX, spriteY;
// current animation frame ticks and flipped/frame state
private int animationTicks;
private boolean animationFlipped, animationXFrame;
// equipped armor
public ItemStack[] armor = new ItemStack[ItemArmor.NUM_TYPES];
public EntityHumanoid(Level level, Inventory inventory) {
super(level, inventory);
this.width = 11;
this.height = 11;
this.animationTicks = this.getAnimationFrameTicks();
this.updateAnimationFrame(false);
}
// level must be in [1..3]
public static void giveRandomEquipment(EntityHumanoid entity, int level, boolean armor, boolean weapon) {
assert (level >= 1 && level <= 3);
Item[][] armorTable = new Item[][]{
{Item.IRON_HELMET, Item.IRON_CHESTPLATE, Item.IRON_LEGGINGS, Item.IRON_BOOTS},
{Item.GOLD_HELMET, Item.GOLD_CHESTPLATE, Item.GOLD_LEGGINGS, Item.GOLD_BOOTS},
{Item.GOLD_HELMET, Item.GOLD_CHESTPLATE, Item.GOLD_LEGGINGS, Item.GOLD_BOOTS}
};
Item[][] weaponTable = new Item[][]{
{Item.ROCK_SWORD, Item.ROCK_AXE},
{Item.IRON_SWORD, Item.IRON_AXE},
{Item.GOLD_SWORD, Item.GEM_SWORD, Item.MITHRIL_SWORD}
};
if (armor) {
for (int i = 0; i < ItemArmor.NUM_TYPES; i++) {
if (Global.random.nextInt((4 - level) * 7) == 0) {
ItemStack item = new ItemStack(new ItemInstance(armorTable[level - 1][i], 0), 1);
entity.armor[i] = item;
entity.inventory.add(item);
}
}
}
if (weapon && Global.random.nextInt((4 - level) * 8) == 0) {
ItemStack item = new ItemStack(
new ItemInstance(
weaponTable[level - 1][Global.random.nextInt(weaponTable[level - 1].length)],
0),
1
);
entity.equipped = item;
entity.inventory.add(item);
}
}
@Override
public ItemStack removeItem(Item item, int count) {
ItemStack removed = super.removeItem(item, count);
for (int i = 0; i < this.armor.length; i++) {
if (this.armor[i] == removed) {
this.armor[i] = null;
}
}
return removed;
}
@Override
public void die() {
super.die();
for (ItemStack s : this.inventory.stacks) {
EntityItem.spawn(this.level, s, this.getCenterX(), this.getCenterY());
}
}
@Override
public void pickup(ItemStack stack) {
super.pickup(stack);
if (stack.instance.item instanceof ItemArmor) {
ItemArmor armor = (ItemArmor) stack.instance.item;
if (this.armor[armor.slot] == null) {
this.armor[armor.slot] = stack;
}
}
}
protected int getAnimationFrameTicks() {
return 10;
}
public boolean hurt(int amount, boolean ignoreArmor) {
if (ignoreArmor) {
return super.hurt(amount);
}
double reduction = 0;
for (ItemStack s : this.armor) {
if (s != null) {
reduction += ((ItemArmor) s.instance.item).getDamageReduction(s.instance);
}
}
reduction = reduction > 0 ? Global.random.nextInt(Math.max((int) (reduction / 2.5), 2)) : 0;
return super.hurt((int) Math.max(amount - reduction, 0));
}
@Override
public boolean hurt(int amount) {
return this.hurt(amount, false);
}
@Override
public Light getLight() {
if (this.equipped != null) {
int power = this.equipped.instance.item.getLightPower();
if (power > 0) {
return new Light(
this.getCenterX(),
this.getCenterY(),
power
);
}
}
return null;
}
@Override
public int getHitDamage(Entity e) {
return (this.equipped != null && (this.equipped.instance.item instanceof ItemTool)) ?
((ItemTool) this.equipped.instance.item).getDamage(this.equipped.instance) : 1;
}
@Override
public int getRenderOffsetX() {
return -3;
}
@Override
public int getRenderOffsetY() {
return -2;
}
@Override
protected void onDirectionChange() {
super.onDirectionChange();
this.updateAnimationFrame(false);
}
@Override
public void tick() {
super.tick();
// drowning
if (this.swimming && (Global.ticks % 60) == 0) {
if (this.stamina == 0) {
this.hurt(1, true);
} else {
this.takeStamina(1);
}
}
if (this.moving) {
this.animationTicks--;
if (this.animationTicks == 0) {
this.updateAnimationFrame(true);
}
}
// pick the correct current animation frame but don't switch
this.updateAnimationFrame(false);
}
protected boolean carrying() {
return this.equipped != null && this.equipped.instance.item.carry(this);
}
public int getBaseSpriteX() {
return BASE_SPRITE_X;
}
public int getBaseSpriteY() {
return BASE_SPRITE_Y;
}
public int getCarrySpriteX() {
return CARRY_SPRITE_X;
}
public int getCarrySpriteY() {
return CARRY_SPRITE_Y;
}
protected int getSpritesheetOffsetX() {
return this.carrying() ? this.getCarrySpriteX() : this.getBaseSpriteX();
}
protected int getSpritesheetOffsetY() {
return this.carrying() ? this.getCarrySpriteY() : this.getBaseSpriteY();
}
protected void updateAnimationFrame(boolean switchFrame) {
if (switchFrame) {
this.animationTicks = this.getAnimationFrameTicks();
}
this.spriteY = this.getSpritesheetOffsetY();
switch (this.direction) {
case NORTH, SOUTH -> {
if (switchFrame) {
this.animationFlipped = !this.animationFlipped;
}
this.spriteX = this.getSpritesheetOffsetX() +
(this.direction == Direction.NORTH ? 2 : 0);
}
case EAST, WEST -> {
if (switchFrame) {
this.animationXFrame = !this.animationXFrame;
}
this.animationFlipped = this.direction == Direction.LEFT;
this.spriteX = this.getSpritesheetOffsetX() +
(this.animationXFrame ? 4 : 6);
}
}
}
private void renderArmor(ItemArmor item) {
if (this.swimming && item.type != ItemArmor.TYPE_HELMET) {
return;
}
int sx = this.spriteX - this.getCarrySpriteX(),
sy = this.spriteY - this.getCarrySpriteY();
for (int i = 0; i <= 1; i++) {
for (int j = 0; j <= 1; j++) {
Renderer.render(
sx + item.getSpriteBaseX() + (this.animationFlipped ? (1 - i) : i),
sy + item.getSpriteBaseY() + j,
this.x + (i * 8) + this.getRenderOffsetX(),
this.y + (j * 8) + this.getRenderOffsetY(),
item.getColor(),
this.animationFlipped ? Renderer.FLIP_X : Renderer.FLIP_NONE
);
}
}
}
@Override
public void render() {
super.render();
// flash with hurt color on hurt
int color = this.invulnerableTicks > 0 && ((Global.ticks / 4) % 2) == 0 ?
this.getHurtColor() :
this.getColor();
for (int i = 0; i <= 1; i++) {
for (int j = 0; j <= (this.swimming ? 0 : 1); j++) {
Renderer.render(
this.spriteX + (this.animationFlipped ? (1 - i) : i), this.spriteY + j,
this.x + (i * 8) + this.getRenderOffsetX(),
this.y + (j * 8) + this.getRenderOffsetY(),
color,
this.animationFlipped ? Renderer.FLIP_X : Renderer.FLIP_NONE
);
}
}
// render armor
for (ItemStack s : this.armor) {
if (s != null) {
this.renderArmor((ItemArmor) s.instance.item);
}
}
if (this.swimming) {
for (int i = 0; i <= 1; i++) {
Renderer.render(
SWIM_SPRITE_X, SWIM_SPRITE_Y,
this.x + (i * 8) + this.getRenderOffsetX(),
this.y + 2 + this.getRenderOffsetY(),
SWIM_COLOR,
i == 0 ? Renderer.FLIP_NONE : Renderer.FLIP_X
);
}
}
// render carrying item
if (this.equipped != null && this.equipped.instance.item.carry(this)) {
this.equipped.instance.item.renderCarry(this.equipped.instance, this.level, this);
}
}
@Override
public boolean canSwimIn(Tile tile, int x, int y) {
return tile instanceof TileLiquid;
}
public abstract int getColor();
public abstract int getHurtColor();
}

View File

@@ -0,0 +1,327 @@
package com.jdh.microcraft.entity.mob;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.EntityItem;
import com.jdh.microcraft.entity.furniture.EntityFurniture;
import com.jdh.microcraft.entity.particle.EntityTextParticle;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.item.Inventory;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.level.Level;
import com.jdh.microcraft.util.Direction;
import com.jdh.microcraft.util.FMath;
public abstract class EntityMob extends Entity {
private static final int HIT_SPRITE_X = 9, HIT_SPRITE_Y = 12;
private static final int HIT_COLOR = Color.get(222, 533, 544, 555);
private static final int ACTION_SPRITE_X = 8, ACTION_SPRITE_Y = 11;
private static final int ACTION_COLOR = Color.get(555, 555, 555, 555);
private static final int ACTION_TICKS = 6;
private static final int HIT_TICKS = 4;
private static final int USE_ITEM_TICKS = 4;
private static final int STAMINA_RECHARGE_DELAY_TICKS = 40;
private static final int INVULNERABLE_TICKS = 30;
protected int actionTicks;
protected int hitTicks, hitX, hitY;
protected Item useItem;
protected int useItemTicks;
public int health, stamina;
public int invulnerableTicks;
public int staminaRechargeDelayTicks;
public final Inventory inventory;
public ItemStack equipped;
public EntityMob(Level level, Inventory inventory) {
super(level);
this.health = this.getMaxHealth();
this.stamina = this.getMaxStamina();
this.inventory = inventory;
}
public void drop(ItemStack stack) {
assert(this.inventory.contains(stack));
assert(stack.instance.item.isDroppable());
EntityItem item = EntityItem.spawn(
this.level,
this.removeItem(this.equipped.instance.item, this.equipped.size),
this.getCenterX(), this.getCenterY(),
this.getDirection()
);
item.timeToPickup = 80;
}
public int getFacingTileX() {
return switch (this.direction) {
case EAST -> tileX + 1;
case WEST -> tileX - 1;
default -> tileX;
};
}
public int getFacingTileY() {
return switch (this.direction) {
case NORTH -> tileY - 1;
case SOUTH -> tileY + 1;
default -> tileY;
};
}
public ItemStack removeItem(Item item, int count) {
ItemStack removed = this.inventory.remove(item, count);
// remove equipped item if equipped stack was removed
if (this.equipped == removed && count == this.equipped.size) {
this.equipped = null;
}
return removed;
}
@Override
protected void onDirectionChange() {
this.actionTicks = 0;
this.useItemTicks = 0;
this.hitTicks = 0;
}
public int getHitDamage(Entity e) {
return 1;
}
public boolean canPickup(EntityItem e) {
return false;
}
public void pickup(ItemStack stack) {
}
@Override
public boolean hit(Entity e) {
if (!this.swimming && e instanceof EntityMob && this.takeApproxStamina(2)) {
EntityMob mob = (EntityMob) e;
mob.hurt(Math.max(this.getHitDamage(e), 1), this);
return true;
}
return e instanceof EntityFurniture;
}
@Override
public void collide(Entity e) {
super.collide(e);
if (!e.metadata.remove && e instanceof EntityItem &&
((EntityItem) e).canPickup(this) &&
this.canPickup(((EntityItem) e))) {
EntityItem item = ((EntityItem) e);
if (this.inventory.add(item.stack)) {
this.pickup(item.stack);
item.remove();
}
}
}
@Override
public boolean collides(Entity e) {
return !(e instanceof EntityMob && this.swimming);
}
protected void animateHit(Item used, int x, int y) {
this.useItem = used;
this.useItemTicks = USE_ITEM_TICKS;
this.hitTicks = HIT_TICKS;
this.hitX = Level.toPixel(x);
this.hitY = Level.toPixel(y);
}
protected void animateAction() {
this.actionTicks = ACTION_TICKS;
}
@Override
public void render() {
super.render();
// used item
if (this.useItemTicks > 0) {
ItemInstance instance = new ItemInstance(this.useItem, 0);
switch (this.direction) {
case NORTH -> this.useItem.render(
instance, this.level,
this.x + 2, this.y - 6);
case SOUTH -> this.useItem.render(
instance, this.level,
this.x + 2, this.y + this.height);
case EAST -> this.useItem.render(
instance, this.level,
this.x + this.width, this.y + 2);
case WEST -> this.useItem.render(
instance, this.level,
this.x - 6, this.y + 2);
}
}
if (this.hitTicks > 0) {
Renderer.render(HIT_SPRITE_X, HIT_SPRITE_Y,
this.hitX + 0, this.hitY + 0, HIT_COLOR, Renderer.FLIP_Y);
Renderer.render(HIT_SPRITE_X, HIT_SPRITE_Y,
this.hitX + 8, this.hitY + 0, HIT_COLOR, Renderer.FLIP_XY);
Renderer.render(HIT_SPRITE_X, HIT_SPRITE_Y,
this.hitX + 0, this.hitY + 8, HIT_COLOR, Renderer.FLIP_NONE);
Renderer.render(HIT_SPRITE_X, HIT_SPRITE_Y,
this.hitX + 8, this.hitY + 8, HIT_COLOR, Renderer.FLIP_X);
} else if (this.actionTicks > 0) {
int px = this.x + this.getRenderOffsetX(), py = this.y + this.getRenderOffsetY();
switch (this.direction) {
case NORTH -> {
Renderer.render(ACTION_SPRITE_X, ACTION_SPRITE_Y,
px, py - 4, ACTION_COLOR, Renderer.FLIP_X);
Renderer.render(ACTION_SPRITE_X, ACTION_SPRITE_Y,
px + 8, py - 4, ACTION_COLOR, Renderer.FLIP_NONE);
}
case SOUTH -> {
Renderer.render(ACTION_SPRITE_X, ACTION_SPRITE_Y,
px, py + this.height + 1, ACTION_COLOR, Renderer.FLIP_XY);
Renderer.render(ACTION_SPRITE_X, ACTION_SPRITE_Y,
px + 8, py + this.height + 1, ACTION_COLOR, Renderer.FLIP_Y);
}
case EAST -> {
Renderer.render(ACTION_SPRITE_X, ACTION_SPRITE_Y + 1,
px + this.width, py + 0, ACTION_COLOR, Renderer.FLIP_NONE);
Renderer.render(ACTION_SPRITE_X, ACTION_SPRITE_Y + 1,
px + this.width, py + 8, ACTION_COLOR, Renderer.FLIP_Y);
}
case WEST -> {
Renderer.render(ACTION_SPRITE_X, ACTION_SPRITE_Y + 1,
px - 4, py + 0, ACTION_COLOR, Renderer.FLIP_X);
Renderer.render(ACTION_SPRITE_X, ACTION_SPRITE_Y + 1,
px - 4, py + 8, ACTION_COLOR, Renderer.FLIP_XY);
}
}
}
}
public void heal(int amount) {
assert (amount >= 0);
this.health = Math.min(this.health + amount, this.getMaxHealth());
}
// hurt from entity
public boolean hurt(int amount, Entity e) {
return this.hurt(
amount,
this.moving ?
this.getDirection().opposite() :
Direction.get(this.x - e.x, this.y - e.y)
);
}
// hurt with knockback
public boolean hurt(int amount, Direction direction) {
if (this.hurt(amount)) {
this.knockback(4.0, direction);
return true;
}
return false;
}
public boolean hurt(int amount) {
assert (amount >= 0);
if (this.invulnerableTicks > 0) {
return false;
}
EntityTextParticle.spawn(
this.level, this.x, this.y,
Integer.toString(amount),
amount == 0 ? 114 : 500);
this.invulnerableTicks = INVULNERABLE_TICKS;
this.health -= amount;
if (this.health <= 0) {
this.die();
this.remove();
}
return true;
}
// amount is not exact, it's a multiplier. the true amount is random.
public boolean takeApproxStamina(int amount) {
return this.takeStamina(amount - 1 + Global.random.nextInt(1 + amount + (amount / 2)));
}
// returns true if stamina was taken
public boolean takeStamina(int amount) {
if (this.stamina - amount >= 0) {
this.stamina -= amount;
return true;
}
return false;
}
private void rechargeStamina() {
if (this.swimming) {
return;
}
double rr = this.getStaminaRechargeRate();
if (rr >= 1.0) {
this.stamina += (int) rr;
} else if (Global.ticks % ((int) (1.0 / this.getStaminaRechargeRate())) == 0) {
this.stamina++;
}
this.stamina = FMath.clamp(this.stamina, 1, this.getMaxStamina());
}
@Override
public void tick() {
super.tick();
this.invulnerableTicks = Math.max(this.invulnerableTicks - 1, 0);
this.hitTicks = Math.max(this.hitTicks - 1, 0);
this.actionTicks = Math.max(this.actionTicks - 1, 0);
this.useItemTicks = Math.max(this.useItemTicks - 1, 0);
this.staminaRechargeDelayTicks = Math.max(this.staminaRechargeDelayTicks - 1, 0);
if (this.staminaRechargeDelayTicks > 0) {
this.staminaRechargeDelayTicks--;
if (this.staminaRechargeDelayTicks == 0) {
this.rechargeStamina();
}
} else if (this.stamina <= 0) {
this.staminaRechargeDelayTicks = STAMINA_RECHARGE_DELAY_TICKS;
this.stamina = 0;
} else if (this.stamina != this.getMaxStamina()) {
this.rechargeStamina();
}
}
public void die() {
}
// per tick
public abstract double getStaminaRechargeRate();
public abstract int getMaxStamina();
public abstract int getMaxHealth();
}

View File

@@ -0,0 +1,137 @@
package com.jdh.microcraft.entity.mob;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.EntityItem;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.entity.ai.AISkeleton;
import com.jdh.microcraft.entity.particle.EntitySmashParticle;
import com.jdh.microcraft.entity.projectile.EntityArrow;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.item.Inventory;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.level.Level;
import com.jdh.microcraft.level.tile.Tile;
public class EntitySkeleton extends EntityHumanoid {
private static final Item[] LOOT_TABLE = {
Item.GEM, Item.GOLD_INGOT,
Item.COAL, Item.COAL, Item.IRON_INGOT, Item.IRON_INGOT,
Item.COAL, Item.COAL, Item.IRON_ORE, Item.IRON_ORE, Item.IRON_ORE,
Item.GLASS, Item.GLASS, Item.GLASS, Item.PIE, Item.IRON_ORE,
Item.ROCK, Item.ROCK, Item.ROCK, Item.ROCK, Item.SAND, Item.SAND, Item.SAND
};
private long lastShotTicks;
public final double strength;
public EntitySkeleton(Level level, int x, int y, double strength) {
super(level, new Inventory(4));
this.x = x;
this.y = y;
this.strength = strength * Global.game.difficulty;
this.ai = new AISkeleton(this);
this.health = this.getMaxHealth();
this.stamina = this.getMaxStamina();
}
public static EntitySkeleton createSkeleton(Level level) {
EntitySkeleton skeleton = new EntitySkeleton(level, 0, 0, 1.0);
EntityHumanoid.giveRandomEquipment(skeleton, 1, true, false);
return skeleton;
}
private boolean canShoot() {
return (Global.ticks - this.lastShotTicks) >= Math.max(180 - (30 * this.strength), 80);
}
public void shoot(int dx, int dy) {
if (Tile.TILES[this.level.getTile(this.getFacingTileX(), this.getFacingTileY())].isSolid()) {
// only shoot if facing tile is empty
return;
} else if (!this.canShoot()) {
return;
}
this.updateAnimationFrame(true);
this.lastShotTicks = Global.ticks;
this.level.addEntity(new EntityArrow(
this.level, this,
this.getCenterX(), this.getCenterY(),
dx, dy,
2.0,
(int) (1 * this.strength + Global.random.nextInt((int) (2 * this.strength)))
));
}
@Override
protected int getAnimationFrameTicks() {
return Math.max(12 - ((int) (2.0 / this.strength)), 6);
}
@Override
public void die() {
super.die();
EntitySmashParticle.spawn(this.level, this.x, this.y, this.getColor(), 4, 8);
for (int i = Global.random.nextInt(3); i > 0; i--) {
EntityItem.spawn(
this.level,
new ItemStack(new ItemInstance(Item.BONE)),
this.x, this.y
);
}
if (Global.random.nextBoolean()) {
EntityItem.spawn(
this.level,
new ItemStack(new ItemInstance(LOOT_TABLE[Global.random.nextInt(LOOT_TABLE.length)], 0), 1),
this.x, this.y
);
}
Global.game.score += 25 * this.strength;
}
@Override
public int getHitDamage(Entity e) {
return 1;
}
@Override
public void collide(Entity e) {
super.collide(e);
if (e instanceof EntityPlayer) {
this.hit(e);
e.onHit(this);
}
}
@Override
public int getColor() {
return Color.get(331, 555, 555, 554);
}
@Override
public int getHurtColor() {
return Color.get(444, 555, 555, 554);
}
@Override
public double getStaminaRechargeRate() {
return 0.2;
}
@Override
public int getMaxStamina() {
return (int) (this.strength * 10);
}
@Override
public int getMaxHealth() {
return (int) (this.strength * 10);
}
}

View File

@@ -0,0 +1,209 @@
package com.jdh.microcraft.entity.mob;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.EntityItem;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.entity.ai.AISlime;
import com.jdh.microcraft.entity.particle.EntitySmashParticle;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.item.Inventory;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.level.Level;
import com.jdh.microcraft.level.tile.Tile;
import com.jdh.microcraft.util.Direction;
import com.jdh.microcraft.util.FMath;
public class EntitySlime extends EntityMob {
private static final int BASE_SPRITE_X = 16, BASE_SPRITE_Y = 3;
public boolean jumping;
private double vx, vy, vz;
private final boolean jumpTrail;
private final int color, hitColor, trailColor;
public final double strength;
public EntitySlime(Level level, int x, int y, int color, int hitColor,
boolean jumpTrail, int trailColor, double strength) {
super(level, Inventory.NONE);
this.x = x;
this.y = y;
this.color = color;
this.hitColor = hitColor;
this.jumpTrail = jumpTrail;
this.trailColor = trailColor;
this.strength = strength * Global.game.difficulty;
this.width = 12;
this.height = 8;
this.health = this.getMaxHealth();
this.stamina = this.getMaxStamina();
this.ai = new AISlime(this);
}
public static EntitySlime createGreenSlime(Level level) {
return new EntitySlime(
level,
0, 0,
Color.get(010, 020, 151, 353),
Color.get(010, 020, 353, 454),
false, 0,
0.6
);
}
public static EntitySlime createBlueSlime(Level level) {
return new EntitySlime(
level,
0, 0,
Color.get(001, 002, 114, 225),
Color.get(001, 002, 335, 445),
false, 0,
1.0
);
}
public static EntitySlime createRedSlime(Level level) {
return new EntitySlime(
level,
0, 0,
Color.get(100, 200, 411, 522),
Color.get(100, 200, 533, 544),
true, Color.get(330, 440, 550, 551),
1.15
);
}
@Override
public int getHitDamage(Entity e) {
return (int) (this.strength + Global.random.nextInt((int) (4 * this.strength)));
}
@Override
public void collide(Entity e) {
super.collide(e);
if (e instanceof EntityPlayer) {
this.hit(e);
e.onHit(this);
}
}
@Override
public void die() {
super.die();
EntitySmashParticle.spawn(this.level, this.x, this.y, this.color, 4, 10);
for (int i = 1 + Global.random.nextInt(1 + (2 * (int) this.strength)); i > 0; i--) {
EntityItem.spawn(
this.level,
new ItemStack(new ItemInstance(Item.SLIME, 0), 1),
this.x, this.y
);
}
Global.game.score += 20 * this.strength;
}
public void jump(double dx, double dy, double v) {
double l = FMath.norm(dx, dy);
this.jumping = true;
this.vx = 5.0 * (dx / l) * v;
this.vy = 5.0 * (dy / l) * v;
this.vz = 5.0 * v;
}
public void jump(Direction d, double v) {
this.jump(
d.x + (d == Direction.NORTH || d == Direction.SOUTH ? 0.1 : 0.0),
d.y + (d == Direction.EAST || d == Direction.WEST ? 0.3 : 0.0),
v
);
}
@Override
public void tick() {
super.tick();
if (this.jumping && this.jumpTrail && Global.random.nextInt(6) == 0) {
EntitySmashParticle.spawn(this.level, this.x, this.y, this.trailColor, 2, 4);
}
if (this.vz > 0.0) {
this.vx *= 0.97;
this.vy *= 0.97;
this.vz -= 0.11;
} else if (this.vz <= 0.0) {
this.jumping = false;
this.vz = 0.0;
this.vx *= 0.6;
this.vy *= 0.6;
}
if (Math.abs(this.vx) < 0.001) {
this.vx = 0.0;
}
if (Math.abs(this.vy) < 0.001) {
this.vy = 0.0;
}
this.move(
FMath.tickedDoubleToInt(Global.ticks, this.vx),
FMath.tickedDoubleToInt(Global.ticks, this.vy)
);
}
@Override
public int getRenderOffsetX() {
return -2;
}
@Override
public int getRenderOffsetY() {
return -8;
}
@Override
public void render() {
super.render();
Global.random.setSeed(this.id);
int color = this.invulnerableTicks > 0 && ((Global.ticks / 4) % 2) == 0 ? this.hitColor : this.color;
boolean sprite = this.jumping || ((Global.ticks + this.id) / 30) % 2 == 0;
int sx = sprite ? BASE_SPRITE_X + 2 : BASE_SPRITE_X, sy = BASE_SPRITE_Y;
for (int i = 0; i <= 1; i++) {
for (int j = 0; j <= 1; j++) {
Renderer.render(
sx + i, sy + j,
this.x + this.getRenderOffsetX() + (i * 8),
this.y + this.getRenderOffsetY() + (j * 8),
color, Renderer.FLIP_NONE
);
}
}
}
@Override
public double getStaminaRechargeRate() {
return 0.2 * this.strength;
}
@Override
public int getMaxStamina() {
return (int) (10 * this.strength);
}
@Override
public int getMaxHealth() {
return (int) (10 * this.strength);
}
@Override
public boolean canSwimIn(Tile tile, int x, int y) {
return false;
}
}

View File

@@ -0,0 +1,148 @@
package com.jdh.microcraft.entity.mob;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.EntityItem;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.entity.ai.AIZombie;
import com.jdh.microcraft.entity.particle.EntitySmashParticle;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.item.Inventory;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.armor.ItemArmor;
import com.jdh.microcraft.item.tool.ItemTool;
import com.jdh.microcraft.level.Level;
public class EntityZombie extends EntityHumanoid {
private static final Item[] LOOT_TABLE = {
Item.GEM, Item.GOLD_INGOT, Item.MITHRIL_INGOT,
Item.IRON_INGOT, Item.IRON_INGOT, Item.IRON_INGOT, Item.IRON_INGOT,
Item.IRON_ORE, Item.IRON_ORE, Item.IRON_ORE, Item.IRON_ORE, Item.IRON_ORE,
Item.IRON_ORE, Item.IRON_ORE, Item.IRON_ORE, Item.IRON_ORE, Item.IRON_ORE,
Item.ROCK, Item.ROCK, Item.ROCK, Item.ROCK, Item.ROCK, Item.ROCK, Item.ROCK
};
private final int pantsColor, shirtColor;
public final double strength;
public EntityZombie(Level level, int x, int y, double strength) {
super(level, new Inventory(5));
this.strength = strength * Global.game.difficulty;
this.x = x;
this.y = y;
this.pantsColor = Color.randomRGB(1, 5);
this.shirtColor = Color.randomRGB(1, 5);
this.ai = new AIZombie(this);
this.health = this.getMaxHealth();
this.stamina = this.getMaxStamina();
}
public static EntityZombie createEasyZombie(Level level) {
EntityZombie zombie = new EntityZombie(
level, 0, 0, 0.9
);
EntityHumanoid.giveRandomEquipment(zombie, 1, true, true);
return zombie;
}
public static EntityZombie createMediumZombie(Level level) {
EntityZombie zombie = new EntityZombie(
level, 0, 0, 1.0
);
EntityHumanoid.giveRandomEquipment(zombie, 2, true, true);
return zombie;
}
public static EntityZombie createHardZombie(Level level) {
EntityZombie zombie = new EntityZombie(
level, 0, 0, 1.15
);
EntityHumanoid.giveRandomEquipment(zombie, 3, true, true);
return zombie;
}
@Override
protected int getAnimationFrameTicks() {
return Math.max(10 - ((int) (2.0 / this.strength)), 6);
}
@Override
public void die() {
super.die();
EntitySmashParticle.spawn(this.level, this.x, this.y, this.getColor(), 4, 8);
if (Global.random.nextBoolean()) {
EntityItem.spawn(
this.level,
new ItemStack(new ItemInstance(LOOT_TABLE[Global.random.nextInt(LOOT_TABLE.length)], 0), 1),
this.x, this.y
);
}
Global.game.score += 30 * this.strength;
}
@Override
public int getHitDamage(Entity e) {
return (int) (super.getHitDamage(e) * this.strength);
}
@Override
public void collide(Entity e) {
super.collide(e);
if (e instanceof EntityPlayer) {
this.hit(e);
e.onHit(this);
}
}
@Override
public boolean canPickup(EntityItem e) {
return (e.stack.instance.item instanceof ItemTool && this.equipped == null) ||
(e.stack.instance.item instanceof ItemArmor &&
this.armor[((ItemArmor) e.stack.instance.item).slot] == null);
}
@Override
public void pickup(ItemStack stack) {
super.pickup(stack);
if (stack.instance.item instanceof ItemTool && this.equipped == null) {
this.equipped = stack;
} else if (stack.instance.item instanceof ItemArmor) {
ItemArmor armor = (ItemArmor) stack.instance.item;
if (this.armor[armor.slot] == null) {
this.armor[armor.slot] = stack;
}
}
}
@Override
public int getColor() {
return Color.get(000, this.pantsColor, this.shirtColor, 141);
}
@Override
public int getHurtColor() {
return Color.get(544, Color.add(this.pantsColor, 222), Color.add(this.shirtColor, 222), 555);
}
@Override
public double getStaminaRechargeRate() {
return 0.15;
}
@Override
public int getMaxStamina() {
return (int) (10 * this.strength);
}
@Override
public int getMaxHealth() {
return (int) (10 * this.strength);
}
}

View File

@@ -0,0 +1,70 @@
package com.jdh.microcraft.entity.particle;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.level.Level;
import com.jdh.microcraft.level.tile.Tile;
public class EntityParticle extends Entity {
protected int timeToLive;
protected double vx, vy, dx, dy;
protected boolean gravity;
public EntityParticle(Level level, int x, int y) {
super(level);
this.x = x;
this.y = y;
this.width = 2;
this.height = 2;
this.timeToLive = 45 + Global.random.nextInt(60);
this.vx = (Global.random.nextBoolean() ? -1.0 : 1.0) * (0.2 + (Global.random.nextDouble() * 0.4));
this.vy = -(0.8 + (Global.random.nextDouble() * 0.5));
this.gravity = true;
}
protected int getRenderX() {
return (int) (this.x + this.dx);
}
protected int getRenderY() {
return (int) (this.y + this.dy);
}
@Override
public void tick() {
if (--this.timeToLive == 0) {
this.remove();
return;
}
this.dx += this.vx;
this.dy += this.vy;
if (this.gravity && this.dy >= 8.0) {
// bounce at bottom
this.vx *= 0.3;
this.vy *= (this.vy > 0 ? -1.0 : 1.0) * 0.4;
} else if (Math.abs(this.vy) < 0.01) {
// clamp small values
this.vx = 0.0;
this.vy = 0.0;
} else if (this.gravity) {
// gravity
this.vy += 0.1;
}
if (Math.abs(this.vx) < 0.01) {
this.vx = 0.0;
}
}
@Override
public boolean collides(Entity e) {
return false;
}
@Override
public boolean canSwimIn(Tile tile, int x, int y) {
return true;
}
}

View File

@@ -0,0 +1,33 @@
package com.jdh.microcraft.entity.particle;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.level.Level;
public class EntitySmashParticle extends EntityParticle {
private static final int SPRITE_X = 12, SPRITE_Y = 2;
private final int color;
public EntitySmashParticle(Level level, int x, int y, int color) {
super(level, x, y);
this.color = color;
}
public static void spawn(Level level, int x, int y, int color, int min, int max) {
int n = min + Global.random.nextInt(max - min + 1);
for (int i = 0; i < n; i++) {
level.addEntity(new EntitySmashParticle(level, x, y, color));
}
}
@Override
public void render() {
Global.random.setSeed(this.id);
Renderer.render(
SPRITE_X, SPRITE_Y, this.getRenderX(), this.getRenderY(), color,
(Global.random.nextBoolean() ? Renderer.FLIP_X : Renderer.FLIP_NONE) |
(Global.random.nextBoolean() ? Renderer.FLIP_Y : Renderer.FLIP_NONE)
);
}
}

View File

@@ -0,0 +1,36 @@
package com.jdh.microcraft.entity.particle;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.level.Level;
public class EntitySmokeParticle extends EntityParticle {
private static final int SPRITE_X = 12, SPRITE_Y = 2;
private final int color;
public EntitySmokeParticle(Level level, int x, int y, int color) {
super(level, x, y);
this.color = color;
this.gravity = false;
this.vx = (Global.random.nextBoolean() ? -1.0 : 1.0) * (0.1 + (Global.random.nextDouble() * 0.1));
this.vy = -(0.4 + (Global.random.nextDouble() * 0.3));
}
public static void spawn(Level level, int x, int y, int color, int min, int max) {
int n = min + Global.random.nextInt(max - min + 1);
for (int i = 0; i < n; i++) {
level.addEntity(new EntitySmokeParticle(level, x, y, color));
}
}
@Override
public void render() {
Global.random.setSeed(this.id);
Renderer.render(
SPRITE_X, SPRITE_Y, this.getRenderX(), this.getRenderY(), color,
(Global.random.nextBoolean() ? Renderer.FLIP_X : Renderer.FLIP_NONE) |
(Global.random.nextBoolean() ? Renderer.FLIP_Y : Renderer.FLIP_NONE)
);
}
}

View File

@@ -0,0 +1,29 @@
package com.jdh.microcraft.entity.particle;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.gfx.Font;
import com.jdh.microcraft.level.Level;
public class EntityTextParticle extends EntityParticle {
private final String text;
private final int color;
public EntityTextParticle(Level level, int x, int y, String text, int color) {
super(level, x, y);
this.text = text;
this.color = color;
this.timeToLive = 30 + Global.random.nextInt(30);
}
public static void spawn(Level level, int x, int y, String text, int color) {
level.addEntity(new EntityTextParticle(level, x, y, text, color));
}
@Override
public void render() {
Global.random.setSeed(this.id);
Font.render(this.text, this.getRenderX() + 1, this.getRenderY() + 1, Color.add(this.color, -222));
Font.render(this.text, this.getRenderX(), this.getRenderY(), this.color);
}
}

View File

@@ -0,0 +1,64 @@
package com.jdh.microcraft.entity.projectile;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.EntityItem;
import com.jdh.microcraft.entity.mob.EntityMob;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.level.Level;
public class EntityAirBlast extends EntityProjectile {
public EntityAirBlast(Level level, Entity shooter, int x, int y, int dx, int dy, double s, int damage) {
super(level, shooter, x, y, dx, dy, s, damage, 180);
this.width = 4;
this.height = 4;
}
@Override
public boolean hit(Entity e) {
if (e instanceof EntityItem) {
e.remove();
return true;
} else if (!(e instanceof EntityMob)) {
return false;
}
((EntityMob) e).hurt(this.damage, this);
this.remove();
return true;
}
@Override
protected boolean moveAxis(int dx, int dy) {
if (!super.moveAxis(dx, dy)) {
this.remove();
return false;
}
return true;
}
@Override
public int getColor() {
return Color.get(222, 333, 445, 555);
}
@Override
protected int getHorizontalSpriteX() {
return 14;
}
@Override
protected int getHorizontalSpriteY() {
return 8;
}
@Override
protected int getVerticalSpriteX() {
return 14;
}
@Override
protected int getVerticalSpriteY() {
return 8;
}
}

View File

@@ -0,0 +1,54 @@
package com.jdh.microcraft.entity.projectile;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.EntityItem;
import com.jdh.microcraft.entity.mob.EntityMob;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.level.Level;
public class EntityArrow extends EntityProjectile {
public EntityArrow(Level level, Entity shooter, int x, int y, int dx, int dy, double s, int damage) {
super(level, shooter, x, y, dx, dy, s, damage, 180);
this.width = 4;
this.height = 4;
}
@Override
public boolean hit(Entity e) {
if (e instanceof EntityItem) {
e.remove();
return true;
} else if (!(e instanceof EntityMob)) {
return false;
}
((EntityMob) e).hurt(this.damage, this);
this.remove();
return true;
}
@Override
public int getColor() {
return Color.get(220, 330, 333, 444);
}
@Override
protected int getHorizontalSpriteX() {
return 1;
}
@Override
protected int getHorizontalSpriteY() {
return 8;
}
@Override
protected int getVerticalSpriteX() {
return 0;
}
@Override
protected int getVerticalSpriteY() {
return 8;
}
}

View File

@@ -0,0 +1,122 @@
package com.jdh.microcraft.entity.projectile;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.EntityItem;
import com.jdh.microcraft.entity.furniture.EntityFurnace;
import com.jdh.microcraft.entity.furniture.EntityFurniture;
import com.jdh.microcraft.entity.mob.EntityMob;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.level.Level;
import com.jdh.microcraft.level.tile.Tile;
import com.jdh.microcraft.util.Direction;
import com.jdh.microcraft.util.FMath;
public abstract class EntityProjectile extends Entity {
protected final int damage;
protected final Entity shooter;
private int timeToLive;
private double vx, vy;
public EntityProjectile(Level level, Entity shooter, int x, int y, int dx, int dy, double s, int damage, int ttl) {
super(level);
this.shooter = shooter;
this.damage = damage;
this.timeToLive = ttl;
this.x = x;
this.y = y;
// compute velocities in direction of dx, dy
double xx = FMath.sign(dx - this.x),
yy = FMath.sign(dy - this.y),
l = FMath.norm(xx, yy);
xx = (xx / l) * s;
yy = (yy / l) * s;
this.vx = Double.isNaN(xx) ? 0 : xx;
this.vy = Double.isNaN(yy) ? 0 : yy;
}
@Override
public void tick() {
super.tick();
this.move(
FMath.tickedDoubleToInt(Global.ticks, this.vx),
FMath.tickedDoubleToInt(Global.ticks, this.vy)
);
if (--this.timeToLive == 0) {
this.remove();
}
}
@Override
public void render() {
int sx = 0, sy = 0, flip = 0;
switch (this.direction) {
case NORTH, SOUTH -> {
sx = this.getVerticalSpriteX();
sy = this.getVerticalSpriteY();
flip = this.direction == Direction.SOUTH ? Renderer.FLIP_Y : 0;
}
case EAST, WEST -> {
sx = this.getHorizontalSpriteX();
sy = this.getHorizontalSpriteY();
flip = this.direction == Direction.WEST ? Renderer.FLIP_X : 0;
}
}
Renderer.render(
sx, sy,
this.x + this.getRenderOffsetX(), this.y + this.getRenderOffsetY(),
this.getColor(), flip
);
}
@Override
public boolean collides(Entity e) {
return e != this.shooter &&
!(e instanceof EntityProjectile) &&
(Math.abs(this.vx) > 0.0 || Math.abs(this.vy) > 0.0);
}
@Override
public void collide(Entity e) {
super.collide(e);
if (this.hit(e)) {
e.onHit(this);
}
}
@Override
protected boolean moveAxis(int dx, int dy) {
if (!super.moveAxis(dx, dy)) {
this.vx = 0.0;
this.vy = 0.0;
return false;
}
return true;
}
@Override
public boolean canSwimIn(Tile tile, int x, int y) {
return true;
}
@Override
public abstract boolean hit(Entity e);
public abstract int getColor();
protected abstract int getHorizontalSpriteX();
protected abstract int getHorizontalSpriteY();
protected abstract int getVerticalSpriteX();
protected abstract int getVerticalSpriteY();
}

View File

@@ -0,0 +1,13 @@
package com.jdh.microcraft.gfx;
import com.jdh.microcraft.util.FMath;
public class Camera {
// translation
public int tx = 0, ty = 0;
public void centerOn(int x, int y, int minX, int minY, int maxX, int maxY) {
this.tx = FMath.clamp(x - (Renderer.WIDTH / 2), minX, maxX - Renderer.WIDTH);
this.ty = FMath.clamp(y - (Renderer.HEIGHT / 2), minY, maxY - Renderer.HEIGHT);
}
}

View File

@@ -0,0 +1,87 @@
package com.jdh.microcraft.gfx;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.util.FMath;
public class Color {
// get(a, b, c, d) but with array elements
public static int get(int[] abcd) {
return Color.get(abcd[0], abcd[1], abcd[2], abcd[3]);
}
// gets color palette integer of specified 4 colors in format RGB where each component is one (base 10) digit
public static int get(int a, int b, int c, int d) {
return ((Color.map(d) & 0xFF) << 24) |
((Color.map(c) & 0xFF) << 16) |
((Color.map(b) & 0xFF) << 8) |
(Color.map(a) & 0xFF);
}
// replaces color c component i with RGB triple x
public static int withComponent(int c, int x, int i) {
return (c & ~(0xFF << (i * 8))) | ((Color.map(x) & 0xFF) << (i * 8));
}
// gets a single component of a color as a triple
public static int component(int c, int i) {
return Color.iToRGB((c >> (i * 8)) & 0xFF);
}
// palette index to rgb triple
public static int iToRGB(int i) {
int r = i / 36,
g = ((i - (r * 36)) / 6),
b = ((i - (r * 36)) % 6);
return (r * 100) + (g * 10) + b;
}
// maps RGB (where R, G, B are base 10 digits in [0..5]) to palette index
public static int map(int d) {
if (d < 0) {
return 0;
}
return ((d / 100) % 10) * 36 + ((d / 10) % 10) * 6 + (d % 10);
}
// adds and clamps a single RGB triple
public static int add(int c, int v) {
return FMath.clamp(((c / 100) % 10) + ((v / 100) % 10), 0, 5) * 100 +
FMath.clamp(((c / 10) % 10) + ((v / 10) % 10), 0, 5) * 10 +
FMath.clamp((c % 10) + (v % 10), 0, 5);
}
// multiplies and clamps a single RGB triple
public static int mul(int c, int v) {
return FMath.clamp(((c / 100) % 10) * ((v / 100) % 10), 0, 5) * 100 +
FMath.clamp(((c / 10) % 10) * ((v / 10) % 10), 0, 5) * 10 +
FMath.clamp((c % 10) * (v % 10), 0, 5);
}
// adds and clamps all RGB triples in a color
public static int addAll(int c, int v) {
return get(
add(component(c, 0), v),
add(component(c, 1), v),
add(component(c, 2), v),
add(component(c, 3), v)
);
}
public static int randomRGB(int min, int max) {
return
(Global.random.nextInt(max - min + 1)) * 100 +
(Global.random.nextInt(max - min + 1)) * 10 +
(Global.random.nextInt(max - min + 1));
}
// gets r, g, or b from an RGB triple
public static int getRGBComponent(int rgb, int i) {
return (rgb / ((int) Math.pow(10, i))) % 10;
}
// gets RGB triple from R, G, B components in 0..5 range
public static int getRGB(int r, int g, int b) {
return (r * 100) + (g * 10) + b;
}
}

View File

@@ -0,0 +1,125 @@
package com.jdh.microcraft.gfx;
import java.util.Arrays;
import java.util.Optional;
public class Font {
public enum Colors {
RED('R', 500),
ORANGE('O', 441),
YELLOW('Y', 550),
GREEN('G', 050),
BLUE('B', 005),
PURPLE('P', 404),
PINK('I', 533),
WHITE('W', 555),
GREY('E', 333),
BLACK('L', 000),
DARK_GREY('D', 111);
public final char c;
public final int color;
Colors(char c, int color) {
this.c = c;
this.color = color;
}
@Override
public String toString() {
return "$" + this.c;
}
}
private static final int BASE_OFFSET_X = 0, BASE_OFFSET_Y = 13;
private static final String[] LAYOUT = {
"ABCDEFGHIJKLMNOP",
"QRSTUVWXYZ!/<>:+",
"1234567890&=().?"
};
public static int width(String s) {
int w = 0;
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
if (chars[i] == '$' && i != (chars.length - 1)) {
if (chars[i + 1] != '$') {
i++;
}
continue;
}
w += 8;
}
return w;
}
public static int offset(char c) {
for (int i = 0; i < LAYOUT.length; i++) {
int j;
if ((j = LAYOUT[i].indexOf(c)) != -1) {
return ((BASE_OFFSET_Y + i) * Renderer.spritesheet.sizeSprites) + (BASE_OFFSET_X + j);
}
}
// character not found -> ?
return 15 * Renderer.spritesheet.sizeSprites + 15;
}
public static void render(char c, int x, int y, int color) {
Font.render(c, x, y, color, -1);
}
public static void render(char c, int x, int y, int color, int backgroundColor) {
if (Character.isWhitespace(c)) {
return;
}
if (backgroundColor != -1) {
Renderer.fill(x, y, 8, 8, backgroundColor);
}
Renderer.render(
Font.offset(c),
x, y,
Color.get(color, color, color, color),
Renderer.FLIP_NONE
);
}
public static void render(String s, int x, int y, int color) {
Font.render(s, x, y, color, -1);
}
// color, backgroundColor should be single RGB digit triple
// no background if backgroundColor == -1
public static void render(String s, int x, int y, int color, int backgroundColor) {
int count = 0;
int currentColor = color;
for (int i = 0; i < s.length(); i++) {
char c = Character.toUpperCase(s.charAt(i));
if (c == '$' && (i != (s.length() - 1))) {
char n = s.charAt(i + 1);
if (n == '$') {
continue;
} else {
Optional<Colors> cl = Arrays.stream(Colors.values()).filter(e -> e.c == n).findFirst();
if (cl.isEmpty()) {
throw new IllegalStateException();
}
currentColor = cl.get().color;
i++;
continue;
}
}
render(c, x + (count * 8), y, currentColor, backgroundColor);
count++;
}
}
}

View File

@@ -0,0 +1,11 @@
package com.jdh.microcraft.gfx;
public class Light {
public final int x, y, power;
public Light(int x, int y, int power) {
this.x = x;
this.y = y;
this.power = power;
}
}

View File

@@ -0,0 +1,224 @@
package com.jdh.microcraft.gfx;
import com.jdh.microcraft.util.AABB;
import com.jdh.microcraft.util.FMath;
import java.util.*;
public class Renderer {
public static final int[] DITHER = new int[]{0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5};
// renderer flags
public static final int FLIP_NONE = 0x00, FLIP_X = 0x01, FLIP_Y = 0x02, FLIP_XY = FLIP_X | FLIP_Y;
// target width/height
public static final int WIDTH = 256, HEIGHT = 144;
// screen pixels, each entry in [0..216) referring to palette entry
// palette is determined by Window
public static int[] pixels = new int[WIDTH * HEIGHT];
// global spritesheet
public static Spritesheet spritesheet = new Spritesheet("/tiles.png", 8);
// stored camera stack
private static Stack<Camera> cameraStack = new Stack<>();
// lights
private static List<Light> lights = new ArrayList<>();
// current camera
public static Camera camera = new Camera();
// generates color palette, 24-bpp RGB
public static int[] generatePalette() {
int[] result = new int[256];
int i = 0;
for (int r = 0; r < 6; r++) {
for (int g = 0; g < 6; g++) {
for (int b = 0; b < 6; b++) {
int rr = (r * 255) / 5,
gg = (g * 255) / 5,
bb = (b * 255) / 5,
m = (rr * 30 + gg * 59 + bb * 11) / 100;
result[i++] = ((((rr + m) / 2) * 230 / 255 + 10) << 16) |
((((gg + m) / 2) * 230 / 255 + 10) << 8) |
((((bb + m) / 2) * 230 / 255 + 10) << 0);
}
}
}
return result;
}
public static void reset() {
Renderer.clear();
Renderer.clearLights();
Renderer.cameraStack.clear();
Renderer.camera = new Camera();
}
public static void pushCamera() {
Renderer.cameraStack.push(Renderer.camera);
Renderer.camera = new Camera();
}
public static void popCamera() {
Renderer.camera = Renderer.cameraStack.pop();
}
public static void clearLights() {
Renderer.lights.clear();
}
public static void addLights(Collection<Light> lights) {
Renderer.lights.addAll(lights);
}
public static AABB getAABB() {
return new AABB(
Renderer.camera.tx,
Renderer.camera.ty,
Renderer.camera.tx + Renderer.WIDTH,
Renderer.camera.ty + Renderer.HEIGHT
);
}
public static boolean inBounds(int x, int y) {
int xt = x - Renderer.camera.tx, yt = y - Renderer.camera.ty;
return xt >= 0 && yt >= 0 && xt < Renderer.WIDTH && yt < Renderer.HEIGHT;
}
public static void clear() {
Arrays.fill(Renderer.pixels, 0);
}
public static void fill(int x, int y, int w, int h, int color) {
int xt = x - Renderer.camera.tx, yt = y - Renderer.camera.ty;
// check if entirely offscreen
if (xt + w < 0 || yt + h < 0) {
return;
}
for (int yy = yt; yy < yt + h && yy < Renderer.HEIGHT; yy++) {
if (yy < 0) {
continue;
}
for (int xx = xt; xx < xt + w && xx < Renderer.WIDTH; xx++) {
if (xx < 0) {
continue;
}
Renderer.pixels[yy * Renderer.WIDTH + xx] = Color.map(color);
}
}
}
public static void render(Sprite sprite, int x, int y, int color) {
int xt = x - Renderer.camera.tx, yt = y - Renderer.camera.ty;
if (xt + sprite.width < 0 || yt + sprite.height < 0) {
return;
}
for (int yy = yt, ys = 0; yy < yt + sprite.height && yy < Renderer.HEIGHT; yy++, ys++) {
if (yy < 0) {
continue;
}
for (int xx = xt, xs = 0; xx < xt + sprite.width && xx < Renderer.WIDTH; xx++, xs++) {
if (xx < 0) {
continue;
}
int p = sprite.pixels[ys * sprite.width + xs];
if (p >= 0) {
Renderer.pixels[yy * Renderer.WIDTH + xx] = (color >> (p * 8)) & 0xFF;
}
}
}
}
public static void render(int s, int x, int y, int color, int flags) {
Renderer.render(
s % Renderer.spritesheet.sizeSprites,
s / Renderer.spritesheet.sizeSprites,
x, y, color, flags
);
}
public static void render(int sx, int sy, int x, int y, int color, int flags) {
int posX = x - camera.tx, posY = y - camera.ty,
minX = sx * Renderer.spritesheet.size, minY = sy * Renderer.spritesheet.size,
maxX = minX + Renderer.spritesheet.size, maxY = minY + Renderer.spritesheet.size;
// sprite will not be shown at all
if (posX + Renderer.spritesheet.size < 0 || posY + Renderer.spritesheet.size < 0) {
return;
}
for (int ys = minY, yr = posY; ys < maxY && yr < Renderer.HEIGHT; ys++, yr++) {
if (yr < 0) {
continue;
}
for (int xs = minX, xr = posX; xs < maxX && xr < Renderer.WIDTH; xs++, xr++) {
if (xr < 0) {
continue;
}
int p = Renderer.spritesheet.pixels[
((flags & FLIP_Y) == 0 ? ys : (Renderer.spritesheet.size - (ys - minY) - 1 + minY))
* Renderer.spritesheet.width +
((flags & FLIP_X) == 0 ? xs : (Renderer.spritesheet.size - (xs - minX) - 1 + minX))];
if (p >= 0) {
Renderer.pixels[yr * Renderer.WIDTH + xr] = (color >> (p * 8)) & 0xFF;
}
}
}
}
// lights the current frame stored by the renderer
public static void light(int d) {
AABB aabb = Renderer.getAABB();
// compute (approx.) lights which affect this frame
Light[] lights = Renderer.lights.stream()
.filter(l -> {
int p = l.power * 16;
return AABB.collide(
aabb.minX, aabb.minY, aabb.maxX, aabb.maxY,
l.x - p, l.y - p, l.x + p, l.y + p
);
}).toArray(Light[]::new);
int[] oldPixels = Renderer.pixels.clone();
Arrays.fill(Renderer.pixels, 0);
int tx = Renderer.camera.tx, ty = Renderer.camera.ty;
for (int i = 0; i < (Renderer.WIDTH * Renderer.HEIGHT); i++) {
int x = i % Renderer.WIDTH, y = i / Renderer.WIDTH;
// compute contribution from each light
for (Light l : lights) {
int dx = l.x - (x + tx),
dy = l.y - (y + ty),
dist = (int) FMath.norm(dx, dy);
if (dist < (l.power * 4) ||
(dist / l.power) <= DITHER[(((Math.abs(dy) % 4) * 4) + (Math.abs(dx) % 4))]) {
Renderer.pixels[i] = d != 0 ?
Color.map(Color.add(Color.iToRGB(oldPixels[i]), d)) :
oldPixels[i];
break;
}
}
}
}
}

View File

@@ -0,0 +1,37 @@
package com.jdh.microcraft.gfx;
import com.jdh.microcraft.Main;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
public class Sprite {
// sprite size in pixels
public int width, height;
// sprite pixels, mapped into [0..3] space
public int[] pixels;
public Sprite(String path) {
BufferedImage image;
try {
image = ImageIO.read(Main.class.getResourceAsStream(path));
} catch (IOException e) {
throw new Error(e);
}
this.width = image.getWidth();
this.height = image.getHeight();
this.pixels = new int[this.width * this.height];
int[] imagePixels = image.getRGB(
0, 0, image.getWidth(), image.getHeight(),
null, 0, image.getWidth());
for (int i = 0; i < this.width * this.height; i++) {
this.pixels[i] = ((imagePixels[i] >> 24) & 0xFF) != 0xFF ? -1 : (imagePixels[i] & 0xFF) / 64;
}
}
}

View File

@@ -0,0 +1,48 @@
package com.jdh.microcraft.gfx;
import com.jdh.microcraft.Main;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
public class Spritesheet {
// spritesheet size in pixels
public int width, height;
// spritesheet sprite size
public int size;
// spritesheet size in sprites
public int sizeSprites;
// spritesheet pixels, mapped into [0..3] space
public int[] pixels;
public Spritesheet(String path, int size) {
this.size = size;
BufferedImage image;
try {
image = ImageIO.read(Main.class.getResourceAsStream(path));
} catch (IOException e) {
throw new Error(e);
}
this.width = image.getWidth();
this.height = image.getHeight();
this.pixels = new int[this.width * this.height];
assert(this.width == this.height);
this.sizeSprites = this.width / this.size;
int[] imagePixels = image.getRGB(
0, 0, image.getWidth(), image.getHeight(),
null, 0, image.getWidth());
for (int i = 0; i < this.width * this.height; i++) {
this.pixels[i] = ((imagePixels[i] >> 24) & 0xFF) != 0xFF ? -1 : (imagePixels[i] & 0xFF) / 64;
}
}
}

View File

@@ -0,0 +1,106 @@
package com.jdh.microcraft.gui;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.entity.furniture.EntityChest;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.util.ControlHandler;
import java.util.List;
public class ChestMenu extends Menu {
private class ChestPlayerInventoryMenu extends PlayerInventoryMenu {
public ChestPlayerInventoryMenu(EntityPlayer player) {
super(player, false);
this.focused = true;
}
}
private class ChestInventoryMenu extends InventoryMenu {
public ChestInventoryMenu(EntityChest chest) {
super(chest.inventory, false);
this.focused = false;
}
@Override
public String getName() {
return "CHEST";
}
}
private final ChestPlayerInventoryMenu playerMenu;
private final ChestInventoryMenu chestMenu;
private final List<Menu> submenus;
public ChestMenu(EntityPlayer player, EntityChest chest) {
this.playerMenu = new ChestPlayerInventoryMenu(player);
this.chestMenu = new ChestInventoryMenu(chest);
this.submenus = List.of(this.playerMenu, this.chestMenu);
}
@Override
public void init() {
super.init();
for (Menu m : this.submenus) {
m.init();
}
}
@Override
public void destroy() {
super.destroy();
for (Menu m : this.submenus) {
m.destroy();
}
}
@Override
public void tick() {
super.tick();
if (ControlHandler.MENU_LEFT.pressedTick() && !this.playerMenu.focused) {
this.playerMenu.focused = true;
this.chestMenu.focused = false;
}
if (ControlHandler.MENU_RIGHT.pressedTick() && !this.chestMenu.focused) {
this.playerMenu.focused = false;
this.chestMenu.focused = true;
}
boolean drop = ControlHandler.DROP.pressedTick();
if (ControlHandler.MENU_SELECT.pressedTick() || drop) {
InventoryMenu from = this.playerMenu.focused ? this.playerMenu : this.chestMenu,
to = from == this.playerMenu ? this.chestMenu : this.playerMenu;
List<ItemStack> fromItems = from.getItems();
if (from.selectedIndex >= 0 && from.selectedIndex < fromItems.size()) {
ItemStack s = fromItems.get(from.selectedIndex);
if (s.instance.item.isDroppable()) {
to.inventory.add(from.inventory.remove(s.instance.item, drop ? 1 : s.size));
}
}
}
for (Menu m : this.submenus) {
m.tick();
}
}
@Override
public void update() {
super.update();
for (Menu m : this.submenus) {
m.update();
}
}
@Override
public void render() {
super.render();
for (Menu m : this.submenus) {
m.render();
}
}
}

View File

@@ -0,0 +1,184 @@
package com.jdh.microcraft.gui;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.mob.EntityHumanoid;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.gfx.Font;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.sound.Sound;
import com.jdh.microcraft.util.ControlHandler;
import com.jdh.microcraft.util.FMath;
public class ColorSelectMenu extends Menu {
private static final String[] TITLE = new String[]{
"CHOOSE YOUR",
Font.Colors.YELLOW + "APPEARANCE"
};
private static final String[] LABELS = new String[]{
"OUTLINE/HAIR", "PANTS", "SHIRT", "SKIN"
};
private static final String[] COLOR_LABELS = new String[]{
"R", "G", "B"
};
private static final String[] INSTRUCTIONS = new String[] {
Font.Colors.GREY + "(" + Font.Colors.YELLOW + "X" + Font.Colors.GREY + " TO DECREASE/" +
Font.Colors.YELLOW + "C" + Font.Colors.GREY + " TO INCREASE)",
Font.Colors.GREY + "(" + Font.Colors.YELLOW + "R" + Font.Colors.GREY + " TO RANDOMIZE" +
Font.Colors.GREY + ")",
"",
Font.Colors.GREY + "PRESS " + Font.Colors.YELLOW + "ENTER"
};
public int[] colors;
private int colorIndex, rgbIndex;
public ColorSelectMenu() {
this.colorIndex = 0;
this.rgbIndex = 0;
this.randomize();
}
private void randomize() {
this.colors = new int[]{
Color.randomRGB(0, 2),
Color.randomRGB(0, 5),
Color.randomRGB(0, 5),
Color.randomRGB(0, 5)
};
}
// updates currently selected color component by the specified change (d)
private void updateCurrentComponent(int d) {
int rgb = this.colors[this.colorIndex];
int[] components = {
Color.getRGBComponent(rgb, 0),
Color.getRGBComponent(rgb, 1),
Color.getRGBComponent(rgb, 2)
};
components[2 - this.rgbIndex] = FMath.clamp(components[2 - this.rgbIndex] + d, 0, 5);
this.colors[this.colorIndex] = Color.getRGB(
components[2],
components[1],
components[0]
);
}
@Override
public void tick() {
super.tick();
if (ControlHandler.MENU_UP.pressedTick() && this.colorIndex > 0) {
this.colorIndex--;
}
if (ControlHandler.MENU_DOWN.pressedTick() && this.colorIndex < (LABELS.length - 1)) {
this.colorIndex++;
}
if (ControlHandler.MENU_LEFT.pressedTick() && this.rgbIndex > 0) {
this.rgbIndex--;
}
if (ControlHandler.MENU_RIGHT.pressedTick() && this.rgbIndex < (COLOR_LABELS.length - 1)) {
this.rgbIndex++;
}
if (ControlHandler.MENU_INCREASE.pressedTick()) {
this.updateCurrentComponent(+1);
}
if (ControlHandler.MENU_DECREASE.pressedTick()) {
this.updateCurrentComponent(-1);
}
if (ControlHandler.MENU_QUIT.pressedTick()) {
Global.mainMenu.menu = Global.mainMenu.mainMenu;
}
if (ControlHandler.MENU_SELECT.pressedTick()) {
Sound.CRAFT.play();
Global.mainMenu.menu = Global.mainMenu.difficultySelectMenu;
}
if (ControlHandler.MENU_RANDOMIZE.pressedTick()) {
this.randomize();
}
}
@Override
public void render() {
super.render();
int y = 8;
for (String s : TITLE) {
Font.render(s, (Renderer.WIDTH - Font.width(s)) / 2, y, 555);
y += 8;
}
y += 8;
int px = (Renderer.WIDTH - 16) / 2, py = y,
sx = EntityHumanoid.BASE_SPRITE_X, sy = EntityHumanoid.BASE_SPRITE_Y;
boolean flip = (Global.ticks / 15) % 2 == 0;
for (int i = 0; i <= 1; i++) {
for (int j = 0; j <= 1; j++) {
Renderer.render(
sx + (flip ? (1 - i) : i), sy + j,
px + (i * 8), py + (j * 8),
Color.get(colors[0], colors[1], colors[2], colors[3]),
flip ? Renderer.FLIP_X : Renderer.FLIP_NONE
);
}
}
y += 24;
// labels/colors
int maxWidth = 0;
for (String label : LABELS) {
int w = Font.width(label);
if (w > maxWidth) {
maxWidth = w;
}
}
for (int i = 0; i < 4; i++) {
int x = (Renderer.WIDTH - (maxWidth + 8 + (24 * 3) + 4 + 8)) / 2;
if (i == this.colorIndex) {
Font.render(">", x - 8, y, 555);
}
Font.render(LABELS[i], x, y, i == this.colorIndex ? 555 : 333);
x += maxWidth + 8;
for (int j = 0; j < 3; j++) {
Font.render(COLOR_LABELS[j], x, y, (i == this.colorIndex && j == this.rgbIndex) ? 550 : 333);
Renderer.fill(
x + 12, y, 8, 8,
Color.getRGBComponent(this.colors[i], 2 - j) * (int) Math.pow(10, 2 - j)
);
x += 24;
}
x += 4;
Renderer.fill(x, y, 8, 8, this.colors[i]);
x += 8;
if (i == this.colorIndex) {
Font.render("<", x, y, 555);
}
y += 12;
}
y += 8;
for (String s : INSTRUCTIONS) {
Font.render(s, (Renderer.WIDTH - Font.width(s)) / 2, y, 555);
y += 8;
}
}
}

View File

@@ -0,0 +1,97 @@
package com.jdh.microcraft.gui;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.gfx.Font;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.util.Keyboard;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DialogMenu extends Menu {
protected final String title;
protected final String[] lines;
protected final int w;
protected final int h;
protected final Runnable onClose;
protected final int x, y;
public DialogMenu(String title, String[] lines, int w, int h, Runnable onClose) {
this.title = title;
this.lines = lines;
this.w = w;
this.h = h;
this.x = (Renderer.WIDTH - (this.w * 8)) / 2;
this.y = (Renderer.HEIGHT - (this.h * 8)) / 2;
this.onClose = onClose;
this.focused = true;
}
protected boolean shouldCenterText() {
return false;
}
protected boolean showPressAnyKey() {
return true;
}
protected boolean shouldAlignToMaxWidth() {
return false;
}
protected String[] getLines() {
return this.lines;
}
@Override
public void render() {
super.render();
this.renderMenu(title, x, y, this.w, this.h);
List<String> allLines = new ArrayList<>(Arrays.asList(this.getLines()));
if (this.showPressAnyKey()) {
allLines.add(" ");
allLines.add(Font.Colors.GREY + "PRESS ANY KEY");
}
int maxWidth = 0;
for (String line : this.getLines()) {
maxWidth = Math.max(maxWidth, Font.width(line));
}
for (int i = 0; i < allLines.size(); i++) {
String s = allLines.get(i);
int tx;
if (this.shouldCenterText() ||
(this.shouldAlignToMaxWidth() && i == (allLines.size() - 1))) {
tx = x + (((this.w * 8) - Font.width(s)) / 2);
} else if (this.shouldAlignToMaxWidth()) {
tx = x + (((this.w * 8) - maxWidth) / 2);
} else {
tx = x + 8;
}
Font.render(s, tx, y + ((i + 1) * 8), 555, -1);
}
}
@Override
public void tick() {
super.tick();
if (this.showPressAnyKey()) {
for (Keyboard.Key k : Keyboard.keys) {
if (k.pressedTick) {
if (Global.game != null) {
Global.game.setMenu(null);
}
this.onClose.run();
}
}
}
}
}

View File

@@ -0,0 +1,99 @@
package com.jdh.microcraft.gui;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.gfx.Font;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.util.ControlHandler;
public class DifficultySelectMenu extends Menu {
private static final String[] TITLE = {
Font.Colors.WHITE + "CHOOSE YOUR",
Font.Colors.RED + "DIFFICULTY"
};
private static final String[] INSTRUCTIONS = new String[] {
Font.Colors.GREY + "PRESS " + Font.Colors.YELLOW + "ENTER" + Font.Colors.GREY + " TO START!"
};
private static final int COUNT = 5;
private static final String[] DIFFICULTY_NAMES = {
"TOO EASY",
"EASY",
"NORMAL",
"KINDA HARD",
"HARD"
};
private static final double[] DIFFICULTY_VALUES = {
0.70,
0.85,
1.0,
1.15,
1.30
};
private int index;
public DifficultySelectMenu() {
this.index = 2;
}
public double getSelectedDifficulty() {
return DIFFICULTY_VALUES[this.index];
}
@Override
public void tick() {
super.tick();
if (ControlHandler.MENU_UP.pressedTick() && this.index > 0) {
this.index--;
}
if (ControlHandler.MENU_DOWN.pressedTick() && this.index < (COUNT - 1)) {
this.index++;
}
if (ControlHandler.MENU_QUIT.pressedTick()) {
Global.mainMenu.menu = Global.mainMenu.colorSelectMenu;
}
if (ControlHandler.MENU_SELECT.pressedTick()) {
Global.setState(Global.StateType.GAME);
}
}
@Override
public void render() {
super.render();
int y = 8;
for (String s : TITLE) {
Font.render(s, (Renderer.WIDTH - Font.width(s)) / 2, y, 555);
y += 8;
}
y += 32;
int maxWidth = 0;
for (String s : DIFFICULTY_NAMES) {
maxWidth = Math.max(maxWidth, Font.width(s));
}
for (int i = 0; i < COUNT; i++) {
String s = DIFFICULTY_NAMES[i];
Font.render(
s,
(Renderer.WIDTH - Font.width(s)) / 2, y + (i * 8),
i == this.index ? 555 : 333);
}
Font.render(">", ((Renderer.WIDTH - maxWidth) / 2) - 8, y + (this.index * 8), 555);
Font.render("<", (Renderer.WIDTH + maxWidth) / 2, y + (this.index * 8), 555);
for (String s : INSTRUCTIONS) {
Font.render(s, (Renderer.WIDTH - Font.width(s)) / 2, Renderer.HEIGHT - 8, 555);
}
}
}

View File

@@ -0,0 +1,31 @@
package com.jdh.microcraft.gui;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.util.Window;
public class FocusMenu extends DialogMenu {
private final Menu oldMenu;
public FocusMenu(Menu oldMenu) {
super("", new String[] { " ", "CLICK TO FOCUS!" }, 16, 5, () -> {});
this.oldMenu = oldMenu;
}
@Override
protected boolean shouldCenterText() {
return true;
}
@Override
protected boolean showPressAnyKey() {
return false;
}
@Override
public void update() {
super.update();
if (Window.hasFocus()) {
Global.game.setMenu(this.oldMenu);
}
}
}

View File

@@ -0,0 +1,74 @@
package com.jdh.microcraft.gui;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.gfx.Font;
import com.jdh.microcraft.gfx.Renderer;
public class HUD extends Menu {
private static final int HEALTH_SPRITE_X = 10, HEALTH_SPRITE_Y = 11;
private static final int STAMINA_SPRITE_X = 10, STAMINA_SPRITE_Y = 12;
private final EntityPlayer player;
public HUD(EntityPlayer player) {
this.player = player;
this.focused = true;
}
@Override
public void render() {
Renderer.fill(0, Renderer.HEIGHT - 16, Renderer.WIDTH, 16, 000);
// health
for (int i = 0; i < this.player.getMaxHealth(); i++) {
boolean flashing = this.player.health <= 2 && ((Global.frames / 10) % 2) == 0;
Renderer.render(
HEALTH_SPRITE_X, HEALTH_SPRITE_Y,
2 + (i * 8), Renderer.HEIGHT - 16,
i < this.player.health ?
(flashing ?
Color.get(311, 511, 533, 555) :
Color.get(100, 400, 511, 533)
) :
Color.get(100, 111, 222, 333),
Renderer.FLIP_NONE
);
}
// stamina
for (int i = 0; i < this.player.getMaxStamina(); i++) {
boolean flashing = this.player.staminaRechargeDelayTicks > 0 && Global.frames % 3 == 0;
Renderer.render(
STAMINA_SPRITE_X, STAMINA_SPRITE_Y,
2 + (i * 8), Renderer.HEIGHT - 8,
i < this.player.stamina ?
Color.get(110, 331, 441, 551) :
(flashing ?
Color.get(333, 444, 555, 555) :
Color.get(110, 222, 333, 444)),
Renderer.FLIP_NONE
);
}
// equipped item
if (player.equipped != null) {
this.player.equipped.instance.item.renderIcon(
this.player.equipped.instance,
Renderer.WIDTH - 100, Renderer.HEIGHT - 12
);
int xx = Renderer.WIDTH - 90;
if (player.equipped.size > 1) {
String s = Integer.toString(player.equipped.size);
Font.render(s, xx, Renderer.HEIGHT - 12, 444);
xx += Font.width(s) + 8;
}
Font.render(
this.player.equipped.instance.item.getName(),
xx, Renderer.HEIGHT - 12, 555
);
}
}
}

View File

@@ -0,0 +1,40 @@
package com.jdh.microcraft.gui;
import com.jdh.microcraft.item.Inventory;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.util.ControlHandler;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
public class InventoryMenu extends ItemSelectMenu {
protected final Inventory inventory;
public InventoryMenu(Inventory inventory, boolean leftSide) {
super(leftSide, ControlHandler.MENU_UP, ControlHandler.MENU_DOWN);
this.inventory = inventory;
}
@Override
public List<ItemStack> getItems() {
// give a sorted list of items with POW GLOVE always on top
return inventory.stacks.stream()
.filter(s -> s.instance.item.showInMenu())
.sorted(Comparator
.<ItemStack, Boolean>comparing(s -> s.instance.item.id != Item.GLOVE.id)
.thenComparing(s -> s.instance.item.getName()))
.collect(Collectors.toList());
}
@Override
public int getItemTextColor(int index) {
return 555;
}
@Override
public String getName() {
return "INVENTORY";
}
}

View File

@@ -0,0 +1,62 @@
package com.jdh.microcraft.gui;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.gfx.Font;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.util.FMath;
import java.util.List;
public abstract class ItemListMenu extends Menu {
public final int x, y, width, height;
private int offset = 0;
public ItemListMenu(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
protected void setOffset(int offset) {
this.offset = FMath.clamp(offset, 0,this.getItems().size());
}
public int getOffset() {
return offset;
}
@Override
public void render() {
super.render();
List<ItemStack> items = this.getItems();
this.renderMenu(this.getName(), this.x, this.y, this.width, this.height);
// render item list
for (int i = 0, j = offset; i < this.height - 2 && j < items.size(); i++, j++) {
int yy = this.y + ((i + 1) * 8);
ItemStack stack = items.get(j);
int color = this.getItemTextColor(j);
stack.instance.item.renderIcon(stack.instance, x + 8, yy);
int xx = x + 16;
if (stack.size > 1) {
String s = Integer.toString(stack.size);
Font.render(s, xx, yy, Color.add(color, -111));
xx += Font.width(s) + 8;
}
Font.render(this.getItemName(j), xx, yy, color);
}
}
public abstract List<ItemStack> getItems();
public abstract String getItemName(int index);
public abstract int getItemTextColor(int index);
public abstract String getName();
}

View File

@@ -0,0 +1,75 @@
package com.jdh.microcraft.gui;
import com.jdh.microcraft.gfx.Font;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.util.Control;
import com.jdh.microcraft.util.FMath;
import java.util.List;
public abstract class ItemSelectMenu extends ItemListMenu {
private static final int WIDTH = 14, HEIGHT = (Renderer.HEIGHT - 32) / 8;
protected final boolean leftSide;
protected final Control up, down;
protected int selectedIndex = 0;
public ItemSelectMenu(boolean leftSide, Control up, Control down) {
super(
leftSide ? 8 : (8 + WIDTH * 8 + 16), 8,
14, (Renderer.HEIGHT - 32) / 8
);
this.leftSide = leftSide;
this.up = up;
this.down = down;
}
protected void updateOffset() {
if (this.selectedIndex < this.getOffset()) {
this.setOffset(this.selectedIndex);
}
if (this.selectedIndex >= this.getOffset() + (HEIGHT - 2)) {
this.setOffset(this.selectedIndex - (HEIGHT - 2) + 1);
}
}
@Override
public void tick() {
super.tick();
List<ItemStack> items = this.getItems();
if (this.focused) {
if (this.up.pressedTick() && this.selectedIndex > 0) {
this.selectedIndex--;
}
if (this.down.pressedTick() && this.selectedIndex < (items.size() - 1)) {
this.selectedIndex++;
}
}
// clamp selection index if its gone out of bounds
this.selectedIndex = FMath.clamp(this.selectedIndex, 0, items.size() - 1);
this.updateOffset();
}
@Override
public void render() {
super.render();
// render selection indicator
if (this.selectedIndex != -1) {
int color = this.focused ? 555 : 333;
int yy = this.y + (((this.selectedIndex - this.getOffset()) + 1) * 8);
Font.render(">", this.x, yy, color, Menu.MENU_BG_COLOR);
Font.render("<", this.x + ((WIDTH - 1) * 8), yy, color, Menu.MENU_BG_COLOR);
}
}
@Override
public String getItemName(int index) {
return this.getItems().get(index).instance.item.getName();
}
}

View File

@@ -0,0 +1,14 @@
package com.jdh.microcraft.gui;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.gfx.Font;
public class LoseMenu extends DialogMenu {
public LoseMenu() {
super(":(", new String[]{
"",
"YOU " + Font.Colors.RED + "DIED",
"SCORE: " + Font.Colors.YELLOW + Global.game.score,
}, 22, 8, () -> Global.setState(Global.StateType.MENU));
}
}

View File

@@ -0,0 +1,75 @@
package com.jdh.microcraft.gui;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.gfx.Font;
import com.jdh.microcraft.gfx.Renderer;
public class Menu {
public boolean focused = false;
private static final int MENU_SPRITES_X = 11, MENU_SPRITES_Y = 11;
public static final int MENU_BG_COLOR = 115;
protected void renderMenu(String title, int x, int y, int wt, int ht) {
int color = this.focused ?
Color.get(111, 333, MENU_BG_COLOR, 444) :
Color.get(000, 111, MENU_BG_COLOR, 222);
// corners
Renderer.render(MENU_SPRITES_X, MENU_SPRITES_Y,
x, y, color, Renderer.FLIP_NONE);
Renderer.render(MENU_SPRITES_X, MENU_SPRITES_Y,
x + ((wt - 1) * 8), y, color, Renderer.FLIP_X);
Renderer.render(MENU_SPRITES_X, MENU_SPRITES_Y,
x, y + ((ht - 1) * 8), color, Renderer.FLIP_Y);
Renderer.render(MENU_SPRITES_X, MENU_SPRITES_Y,
x + ((wt - 1) * 8), y + ((ht - 1) * 8), color, Renderer.FLIP_XY);
// vertical sides
for (int i = 0; i < (ht - 2); i++) {
Renderer.render(MENU_SPRITES_X, MENU_SPRITES_Y + 1,
x, y + ((i + 1) * 8), color, Renderer.FLIP_NONE);
Renderer.render(MENU_SPRITES_X, MENU_SPRITES_Y + 1,
x + ((wt - 1) * 8), y + ((i + 1) * 8), color, Renderer.FLIP_X);
}
// horizontal sides
for (int i = 0; i < (wt - 2); i++) {
Renderer.render(MENU_SPRITES_X + 1, MENU_SPRITES_Y,
x + ((i + 1) * 8), y, color, Renderer.FLIP_NONE);
Renderer.render(MENU_SPRITES_X + 1, MENU_SPRITES_Y,
x + ((i + 1) * 8), y + ((ht - 1) * 8), color, Renderer.FLIP_Y);
}
// center
Renderer.fill(
x + 8, y + 8,
(wt - 2) * 8, (ht - 2) * 8,
MENU_BG_COLOR
);
// title
int titleX = x + (((wt * 8) / 2) - (Font.width(title) / 2));
Font.render(title, titleX, y, this.focused ? 551 : 331, MENU_BG_COLOR);
}
public void init() {
}
public void destroy() {
}
public void tick() {
}
public void update() {
}
public void render() {
}
}

View File

@@ -0,0 +1,54 @@
package com.jdh.microcraft.gui;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.gfx.Font;
import com.jdh.microcraft.util.ControlHandler;
public class PauseMenu extends DialogMenu {
private boolean onYes;
public PauseMenu() {
super("", new String[]{}, 20, 4, () -> {});
this.onYes = false;
}
@Override
protected boolean showPressAnyKey() {
return false;
}
@Override
protected boolean shouldCenterText() {
return true;
}
@Override
protected String[] getLines() {
return new String[] {
"EXIT TO MAIN MENU?",
(this.onYes ? Font.Colors.YELLOW : Font.Colors.GREY) + "YES" +
(this.onYes ? Font.Colors.GREY : Font.Colors.YELLOW) + " NO"
};
}
@Override
public void tick() {
super.tick();
if (ControlHandler.MENU_SELECT.pressedTick()) {
if (this.onYes) {
Global.setState(Global.StateType.MENU);
} else {
Global.game.setMenu(null);
}
}
if (ControlHandler.MENU_LEFT.pressedTick() && !this.onYes) {
this.onYes = true;
}
if (ControlHandler.MENU_RIGHT.pressedTick() && this.onYes) {
this.onYes = false;
}
}
}

View File

@@ -0,0 +1,119 @@
package com.jdh.microcraft.gui;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.gui.crafting.InventoryCraftingMenu;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.armor.*;
import com.jdh.microcraft.sound.Sound;
import com.jdh.microcraft.util.ControlHandler;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class PlayerInventoryMenu extends InventoryMenu {
private class PlayerArmorMenu extends ItemListMenu {
public PlayerArmorMenu() {
super(
PlayerInventoryMenu.this.x + (PlayerInventoryMenu.this.width + 2) * 8,
PlayerInventoryMenu.this.y,
14, 6
);
}
@Override
public List<ItemStack> getItems() {
return Arrays.stream(PlayerInventoryMenu.this.player.armor)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
@Override
public String getItemName(int index) {
return this.getItems().get(index).instance.item.getName();
}
@Override
public int getItemTextColor(int index) {
return 444;
}
@Override
public String getName() {
return "ARMOR";
}
}
private final PlayerArmorMenu armorMenu;
private final EntityPlayer player;
private final boolean showArmor;
public PlayerInventoryMenu(EntityPlayer player, boolean showArmor) {
super(player.inventory, true);
this.player = player;
this.showArmor = showArmor;
this.focused = true;
this.armorMenu = new PlayerArmorMenu();
List<ItemStack> items = this.getItems();
int selected = items.indexOf(this.player.equipped);
this.selectedIndex = selected == -1 ? 0 : selected;
this.updateOffset();
}
@Override
public void tick() {
super.tick();
List<ItemStack> items = this.getItems();
ItemStack selectedItem = this.selectedIndex >= 0 && this.selectedIndex < items.size() ?
items.get(this.selectedIndex) : null;
this.player.equipped = selectedItem;
if (this.showArmor) {
if (ControlHandler.MENU_EQUIP.pressedTick() &&
selectedItem != null &&
selectedItem.instance.item instanceof ItemArmor) {
ItemArmor armor = (ItemArmor) selectedItem.instance.item;
if (this.player.armor[armor.slot] == selectedItem) {
this.player.armor[armor.slot] = null;
} else {
Sound.EQUIP.play();
this.player.armor[armor.slot] = selectedItem;
}
}
this.armorMenu.tick();
}
if (ControlHandler.DROP.pressedTick() &&
selectedItem != null &&
selectedItem.instance.item.isDroppable()) {
this.player.drop(selectedItem);
}
if (ControlHandler.TOGGLE_CRAFTING.pressedTick()) {
// direct inventory -> crafting transition
Global.game.setMenu(new InventoryCraftingMenu(this.player));
}
}
@Override
public void render() {
super.render();
if (this.showArmor) {
this.armorMenu.render();
}
}
@Override
public void update() {
if (this.showArmor) {
this.armorMenu.update();
}
}
}

View File

@@ -0,0 +1,44 @@
package com.jdh.microcraft.gui;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.util.FMath;
public class TransitionMenu extends Menu {
private static final int TIME = 10;
// time to live
private int ticks;
public TransitionMenu() {
this.ticks = 0;
}
@Override
public void tick() {
super.tick();
if (++this.ticks == TIME) {
Global.game.setMenu(null);
}
}
@Override
public void render() {
super.render();
for (int y = 0; y < Renderer.HEIGHT / 8; y++) {
for (int x = 0; x < Renderer.WIDTH / 8; x++) {
int distance = (int) (
FMath.norm(
((double) (x * 8) - (double) (Renderer.WIDTH / 2)) / (Renderer.WIDTH / 2),
((double) (y * 8) - (double) (Renderer.HEIGHT / 2)) / (Renderer.HEIGHT / 2)
) * TIME);
if (distance > this.ticks) {
Renderer.fill(x * 8, y * 8, 8, 8, 000);
}
}
}
}
}

View File

@@ -0,0 +1,17 @@
package com.jdh.microcraft.gui;
import com.jdh.microcraft.gfx.Font;
public class WelcomeMenu extends DialogMenu {
public WelcomeMenu() {
super("WELCOME", new String[]{
"",
"YOU MUST DEFEAT ",
"THE EVIL " + Font.Colors.RED + "AIR WIZARD!",
"",
"ONE SHOULD GO " + Font.Colors.GREEN + "DOWN",
"BEFORE MAKING A",
"WAY" + Font.Colors.YELLOW + " UP",
}, 22, 11, () -> {});
}
}

View File

@@ -0,0 +1,25 @@
package com.jdh.microcraft.gui;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.gfx.Font;
public class WinMenu extends DialogMenu {
private int ticks;
public WinMenu() {
super("YAY!", new String[]{
"",
"YOU WIN! ",
"THE EVIL " + Font.Colors.RED + "AIR WIZARD",
"HAS BEEN " + Font.Colors.YELLOW + "BESTED.",
"SCORE: " + Font.Colors.YELLOW + Global.game.score,
}, 22, 10, () -> Global.setState(Global.StateType.MENU));
}
@Override
public void tick() {
if (++this.ticks > 120) {
super.tick();
}
}
}

View File

@@ -0,0 +1,21 @@
package com.jdh.microcraft.gui.crafting;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.entity.furniture.EntityAnvil;
import com.jdh.microcraft.item.crafting.Recipe;
public class AnvilCraftingMenu extends FurnitureCraftingMenu {
public AnvilCraftingMenu(EntityPlayer player, EntityAnvil anvil) {
super(player, anvil);
}
@Override
public int getStation() {
return Recipe.STATION_ANVIL;
}
@Override
public String getName() {
return "ANVIL";
}
}

View File

@@ -0,0 +1,21 @@
package com.jdh.microcraft.gui.crafting;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.entity.furniture.EntityCraftingBench;
import com.jdh.microcraft.item.crafting.Recipe;
public class BenchCraftingMenu extends FurnitureCraftingMenu {
public BenchCraftingMenu(EntityPlayer player, EntityCraftingBench bench) {
super(player, bench);
}
@Override
public int getStation() {
return Recipe.STATION_ALL_CRAFTING;
}
@Override
public String getName() {
return "CRAFTING";
}
}

View File

@@ -0,0 +1,236 @@
package com.jdh.microcraft.gui.crafting;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.gui.ItemListMenu;
import com.jdh.microcraft.gui.ItemSelectMenu;
import com.jdh.microcraft.gui.Menu;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.crafting.Recipe;
import com.jdh.microcraft.sound.Sound;
import com.jdh.microcraft.util.Control;
import com.jdh.microcraft.util.ControlHandler;
import java.util.List;
import java.util.stream.Collectors;
public abstract class CraftingMenu extends Menu {
private class CraftingList extends ItemSelectMenu {
private final Control craft;
public CraftingList(Control up, Control down, Control craft) {
super(true, up, down);
this.craft = craft;
this.focused = true;
}
public Recipe getSelectedRecipe() {
if (CraftingMenu.this.recipes.size() == 0) {
return null;
}
return CraftingMenu.this.recipes.get(this.selectedIndex);
}
private boolean canCraft(int index) {
return this.selectedIndex < CraftingMenu.this.recipes.size() &&
CraftingMenu.this.recipes.get(index)
.canMake(CraftingMenu.this.player.inventory, CraftingMenu.this.getStation());
}
@Override
public void tick() {
super.tick();
if (this.craft.pressedTick() && this.canCraft(this.selectedIndex)) {
ItemStack result = this.getSelectedRecipe().make(CraftingMenu.this.player.inventory);
CraftingMenu.this.player.inventory.add(result);
CraftingMenu.this.onCraft(result);
}
}
@Override
public List<ItemStack> getItems() {
return CraftingMenu.this.items;
}
@Override
public int getItemTextColor(int index) {
return this.canCraft(index) ? 555 : 333;
}
@Override
public String getName() {
return CraftingMenu.this.getName();
}
}
private class HaveMenu extends ItemListMenu {
public HaveMenu() {
super(
CraftingMenu.this.craftingList.x + (CraftingMenu.this.craftingList.width + 2) * 8,
CraftingMenu.this.craftingList.y,
10, 3
);
}
@Override
public List<ItemStack> getItems() {
Recipe recipe = CraftingMenu.this.craftingList.getSelectedRecipe();
return recipe == null ?
List.of() :
List.of(new ItemStack(recipe.result.instance, 1));
}
@Override
public String getItemName(int index) {
Recipe recipe = CraftingMenu.this.craftingList.getSelectedRecipe();
assert (recipe != null);
return Integer.toString(CraftingMenu.this.player.inventory.count(recipe.result.instance.item));
}
@Override
public int getItemTextColor(int index) {
return 555;
}
@Override
public String getName() {
return "HAVE";
}
}
private class CostMenu extends ItemListMenu {
public CostMenu() {
super(
CraftingMenu.this.craftingList.x + (CraftingMenu.this.craftingList.width + 2) * 8,
CraftingMenu.this.craftingList.y + (CraftingMenu.this.haveMenu.height + 1) * 8,
10, CraftingMenu.this.craftingList.height - CraftingMenu.this.haveMenu.height - 1
);
}
@Override
public List<ItemStack> getItems() {
Recipe recipe = CraftingMenu.this.craftingList.getSelectedRecipe();
return recipe == null ?
List.of() :
recipe.ingredients.stream()
.map(i -> new ItemStack(new ItemInstance(i.item, 0), 0))
.collect(Collectors.toList());
}
@Override
public String getItemName(int index) {
Recipe recipe = CraftingMenu.this.craftingList.getSelectedRecipe();
assert (recipe != null);
ItemStack item = this.getItems().get(index);
int have = CraftingMenu.this.player.inventory.count(item.instance.item),
required = recipe.ingredients.stream()
.filter(i -> i.item.id == item.instance.item.id)
.findFirst().get().count;
return required + "/" + have;
}
@Override
public int getItemTextColor(int index) {
return 555;
}
@Override
public String getName() {
return "COST";
}
}
protected final CraftingList craftingList;
protected final HaveMenu haveMenu;
protected final CostMenu costMenu;
protected final List<Menu> submenus;
protected final EntityPlayer player;
protected final List<Recipe> recipes;
protected final List<ItemStack> items;
public CraftingMenu(EntityPlayer player) {
this.player = player;
this.recipes = Item.STATION_RECIPES.get(this.getStation());
this.items = this.recipes.stream().map(r -> r.result).collect(Collectors.toList());
this.craftingList = new CraftingList(ControlHandler.MENU_UP, ControlHandler.MENU_DOWN, ControlHandler.MENU_SELECT);
this.haveMenu = new HaveMenu();
this.costMenu = new CostMenu();
this.submenus = List.of(this.craftingList, this.haveMenu, this.costMenu);
this.sort();
}
private void sort() {
this.recipes.sort(
(var a, var b) -> {
boolean ma = a.canMake(this.player.inventory, this.getStation()),
mb = b.canMake(this.player.inventory, this.getStation());
return ma == mb ? String.CASE_INSENSITIVE_ORDER.compare(
a.result.instance.item.getName(),
b.result.instance.item.getName()
) : (ma ? -1 : 1);
}
);
this.items.clear();
for (Recipe r : this.recipes) {
this.items.add(r.result);
}
}
@Override
public void init() {
super.init();
for (Menu m : submenus) {
m.init();
}
}
@Override
public void destroy() {
super.destroy();
for (Menu m : submenus) {
m.destroy();
}
}
@Override
public void tick() {
super.tick();
this.sort();
for (Menu m : submenus) {
m.tick();
}
}
@Override
public void update() {
super.update();
for (Menu m : submenus) {
m.update();
}
}
@Override
public void render() {
super.render();
for (Menu m : submenus) {
m.render();
}
}
protected void onCraft(ItemStack stack) {
Sound.CRAFT.play();
}
public abstract int getStation();
public abstract String getName();
}

View File

@@ -0,0 +1,35 @@
package com.jdh.microcraft.gui.crafting;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.entity.furniture.EntityFurnace;
import com.jdh.microcraft.entity.particle.EntitySmokeParticle;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.crafting.Recipe;
public class FurnaceMenu extends FurnitureCraftingMenu {
public FurnaceMenu(EntityPlayer player, EntityFurnace furnace) {
super(player, furnace);
}
@Override
protected void onCraft(ItemStack stack) {
super.onCraft(stack);
EntitySmokeParticle.spawn(
this.furniture.level,
this.furniture.getCenterX(), this.furniture.getCenterY(),
Item.COAL.getColor(),
3, 6
);
}
@Override
public int getStation() {
return Recipe.STATION_FURNACE;
}
@Override
public String getName() {
return "FURNACE";
}
}

View File

@@ -0,0 +1,13 @@
package com.jdh.microcraft.gui.crafting;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.entity.furniture.EntityFurniture;
public abstract class FurnitureCraftingMenu extends CraftingMenu {
protected final EntityFurniture furniture;
public FurnitureCraftingMenu(EntityPlayer player, EntityFurniture furniture) {
super(player);
this.furniture = furniture;
}
}

View File

@@ -0,0 +1,37 @@
package com.jdh.microcraft.gui.crafting;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.gui.PlayerInventoryMenu;
import com.jdh.microcraft.item.crafting.Recipe;
import com.jdh.microcraft.util.ControlHandler;
public class InventoryCraftingMenu extends CraftingMenu {
public InventoryCraftingMenu(EntityPlayer player) {
super(player);
}
@Override
public void tick() {
super.tick();
if (ControlHandler.TOGGLE_CRAFTING.pressedTick()) {
Global.game.setMenu(null);
}
if (ControlHandler.INTERACT.pressedTick()) {
// allow direct crafting -> inventory transitions
Global.game.setMenu(new PlayerInventoryMenu(this.player, true));
}
}
@Override
public int getStation() {
return Recipe.STATION_INVENTORY;
}
@Override
public String getName() {
return "CRAFTING";
}
}

View File

@@ -0,0 +1,35 @@
package com.jdh.microcraft.gui.crafting;
import com.jdh.microcraft.entity.EntityPlayer;
import com.jdh.microcraft.entity.furniture.EntityOven;
import com.jdh.microcraft.entity.particle.EntitySmokeParticle;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.crafting.Recipe;
public class OvenMenu extends FurnitureCraftingMenu {
public OvenMenu(EntityPlayer player, EntityOven oven) {
super(player, oven);
}
@Override
protected void onCraft(ItemStack stack) {
super.onCraft(stack);
EntitySmokeParticle.spawn(
this.furniture.level,
this.furniture.getCenterX(), this.furniture.getCenterY(),
Item.COAL.getColor(),
2, 5
);
}
@Override
public int getStation() {
return Recipe.STATION_OVEN;
}
@Override
public String getName() {
return "OVEN";
}
}

View File

@@ -0,0 +1,33 @@
package com.jdh.microcraft.gui.mainmenu;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.gfx.Font;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.gui.DialogMenu;
public class AboutMenu extends DialogMenu {
public AboutMenu() {
super(
"ABOUT",
new String [] {
"",
Font.Colors.YELLOW + "MINICRAFT " + Font.Colors.WHITE + "MADE BY " + Font.Colors.YELLOW + "NOTCH",
"FOR " + Font.Colors.BLUE + "LUDUM DARE 22" + Font.Colors.WHITE + " IN 2011.",
"",
"",
Font.Colors.GREEN + "MICROCRAFT" + Font.Colors.WHITE + " REMAKE BY " + Font.Colors.GREEN + "JDH",
"FOR " + Font.Colors.ORANGE + "FUN" + Font.Colors.WHITE + " IN 2020.",
"",
Font.Colors.GREY + "GITHUB.COM/JDAH",
Font.Colors.GREY + "YOUTUBE.COM/C/JDHVIDEO"
},
Renderer.WIDTH / 8, Renderer.HEIGHT / 8,
() -> Global.mainMenu.menu = Global.mainMenu.mainMenu
);
}
@Override
protected boolean shouldCenterText() {
return true;
}
}

View File

@@ -0,0 +1,35 @@
package com.jdh.microcraft.gui.mainmenu;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.gfx.Font;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.gui.DialogMenu;
public class HowToPlayMenu extends DialogMenu {
public HowToPlayMenu() {
super(
"HOW TO PLAY",
new String [] {
" ",
Font.Colors.GREY + "USE THE " + Font.Colors.GREEN + "POW GLOVE " + Font.Colors.GREY + "TO HIT.",
Font.Colors.GREY + "GEAR UP TO FIGHT THE " + Font.Colors.RED + "WIZARD" + Font.Colors.GREY + ".",
"",
Font.Colors.YELLOW + " WASD" + Font.Colors.WHITE + " TO MOVE",
Font.Colors.YELLOW + "SPACE" + Font.Colors.WHITE + " TO HIT",
Font.Colors.YELLOW + " E" + Font.Colors.WHITE + " FOR INVENTORY",
Font.Colors.YELLOW + " E" + Font.Colors.WHITE + " TO USE",
Font.Colors.YELLOW + "ENTER" + Font.Colors.WHITE + " TO SELECT",
Font.Colors.YELLOW + " C" + Font.Colors.WHITE + " TO CRAFT",
Font.Colors.YELLOW + " Z" + Font.Colors.WHITE + " TO EQUIP",
Font.Colors.YELLOW + " Q" + Font.Colors.WHITE + " TO DROP"
},
Renderer.WIDTH / 8, Renderer.HEIGHT / 8,
() -> Global.mainMenu.menu = Global.mainMenu.mainMenu
);
}
@Override
protected boolean shouldAlignToMaxWidth() {
return true;
}
}

View File

@@ -0,0 +1,56 @@
package com.jdh.microcraft.gui.mainmenu;
import com.jdh.microcraft.gfx.Font;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.gui.Menu;
import com.jdh.microcraft.util.Window;
public class LoadingMenu extends Menu {
private static final int PROGRESS_WIDTH = 128, PROGRESS_HEIGHT = 4;
public String text;
public double progress;
public LoadingMenu() {
this.text = "LOADING";
this.progress = 50.0;
}
public void setProgress(double progress) {
this.setProgress(this.text, progress);
}
public void setProgress(String text, double progress) {
assert(progress >= 0.0 && progress <= 1.0);
this.text = text;
this.progress = progress;
Renderer.clear();
this.render();
Window.renderFrame();
}
@Override
public void render() {
super.render();
int y = (Renderer.HEIGHT / 2) - 8;
Font.render(
this.text,
(Renderer.WIDTH - Font.width(this.text)) / 2, y,
555
);
Renderer.fill(
(Renderer.WIDTH - PROGRESS_WIDTH) / 2, y + 12,
PROGRESS_WIDTH, PROGRESS_HEIGHT,
333
);
Renderer.fill(
(Renderer.WIDTH - PROGRESS_WIDTH) / 2, y + 12,
(int) (PROGRESS_WIDTH * this.progress), PROGRESS_HEIGHT,
151
);
}
}

View File

@@ -0,0 +1,96 @@
package com.jdh.microcraft.gui.mainmenu;
import com.jdh.microcraft.Global;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.gfx.Font;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.gfx.Sprite;
import com.jdh.microcraft.gui.Menu;
import com.jdh.microcraft.sound.Sound;
import com.jdh.microcraft.util.ControlHandler;
public class MainMenu extends Menu {
private static final Sprite LOGO = new Sprite("/logo.png");
private static final int LOGO_Y = 32;
private static final String[] OPTIONS = {
"START",
"HOW TO PLAY",
"ABOUT",
"QUIT"
};
private static final Runnable[] FUNCTIONS = {
() -> {
Sound.CRAFT.play();
Global.mainMenu.menu = Global.mainMenu.colorSelectMenu;
},
() -> {
Sound.CRAFT.play();
Global.mainMenu.menu = Global.mainMenu.howToPlayMenu;
},
() -> {
Sound.CRAFT.play();
Global.mainMenu.menu = Global.mainMenu.aboutMenu;
},
() -> System.exit(0)
};
private int index;
@Override
public void render() {
super.render();
Renderer.render(
LOGO,
(Renderer.WIDTH - LOGO.width) / 2,
LOGO_Y,
Color.get(112, 114, 151, 225)
);
int y = LOGO_Y + LOGO.height + 16;
for (int i = 0; i < OPTIONS.length; i++) {
String s = OPTIONS[i];
int color = 333;
if (i == this.index) {
s = "> " + s + " <";
color = 555;
}
Font.render(
s,
(Renderer.WIDTH - Font.width(s)) / 2,
y + (i * 8),
color
);
}
Font.render(
Font.Colors.DARK_GREY + "ORIGINALLY BY" + Font.Colors.GREY + " NOTCH",
0, Renderer.HEIGHT - 16, 555
);
Font.render(
Font.Colors.DARK_GREY + "REMAKE BY" + Font.Colors.BLUE + " JDH" +
Font.Colors.DARK_GREY + " (github.com/jdah)",
0, Renderer.HEIGHT - 8, 555);
}
@Override
public void tick() {
super.tick();
if (ControlHandler.MENU_UP.pressedTick() && this.index > 0) {
this.index--;
}
if (ControlHandler.MENU_DOWN.pressedTick() && this.index < (OPTIONS.length - 1)) {
this.index++;
}
if (ControlHandler.MENU_SELECT.pressedTick()) {
FUNCTIONS[this.index].run();
}
}
}

View File

@@ -0,0 +1,79 @@
package com.jdh.microcraft.item;
import java.util.ArrayList;
import java.util.List;
public class Inventory {
public static final Inventory NONE = new Inventory(0);
public List<ItemStack> stacks;
public int maxSize;
public Inventory(int maxSize) {
this.stacks = new ArrayList<>();
this.maxSize = maxSize;
}
public boolean contains(Item item, int count) {
return this.stacks.stream().anyMatch(s -> s.instance.item.id == item.id && s.size >= count);
}
public boolean contains(ItemStack stack) {
return this.stacks.stream().anyMatch(s -> s.equals(stack));
}
public ItemStack remove(Item item, int count) {
assert(this.contains(item, count));
for (var it = this.stacks.iterator(); it.hasNext(); ) {
ItemStack s = it.next();
if (s.instance.item.id == item.id) {
assert((s.size - count) >= 0);
if ((s.size - count) == 0) {
it.remove();
return s;
} else {
s.size -= count;
return new ItemStack(new ItemInstance(s.instance), count);
}
}
}
assert(false);
return null;
}
public boolean add(ItemStack stack) {
// attempt to stack
for (ItemStack s : this.stacks) {
if (s.instance.item.id == stack.instance.item.id &&
s.instance.item.isStackable()) {
s.size += stack.size;
return true;
}
}
// could not stack, try to add to inventory
if (this.stacks.size() < this.maxSize) {
this.stacks.add(stack);
return true;
}
return false;
}
public ItemStack findById(int id) {
return this.stacks.stream().filter(s -> s.instance.item.id == id).findFirst().orElse(null);
}
public ItemStack findByInstanceId(int id) {
return this.stacks.stream().filter(s -> s.instance.id == id).findFirst().orElse(null);
}
public ItemStack find(Item item) {
return this.stacks.stream().filter(s -> s.instance.item.id == item.id).findFirst().orElse(null);
}
public int count(Item item) {
return this.stacks.stream().filter(s -> s.instance.item.id == item.id).mapToInt(s -> s.size).sum();
}
}

View File

@@ -0,0 +1,191 @@
package com.jdh.microcraft.item;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.item.armor.*;
import com.jdh.microcraft.item.consumable.ItemApple;
import com.jdh.microcraft.item.consumable.ItemBread;
import com.jdh.microcraft.item.consumable.ItemCactusGoo;
import com.jdh.microcraft.item.consumable.ItemPie;
import com.jdh.microcraft.item.crafting.Recipe;
import com.jdh.microcraft.item.furniture.*;
import com.jdh.microcraft.item.resource.*;
import com.jdh.microcraft.item.tool.*;
import com.jdh.microcraft.level.Level;
import com.jdh.microcraft.level.tile.Tile;
import java.util.*;
import java.util.stream.Collectors;
public abstract class Item {
public static final Item[] ITEMS = new Item[256];
public static final ItemGlove GLOVE = new ItemGlove(1);
public static final ItemRock ROCK = new ItemRock(2);
public static final ItemWood WOOD = new ItemWood(3);
public static final ItemOre GOLD_ORE = new ItemOre(4, Metal.GOLD);
public static final ItemIngot GOLD_INGOT = new ItemIngot(5, Metal.GOLD);
public static final ItemOre IRON_ORE = new ItemOre(6, Metal.IRON);
public static final ItemIngot IRON_INGOT = new ItemIngot(7, Metal.IRON);
public static final ItemSword WOOD_SWORD = new ItemSword(8, Recipe.STATION_BENCH, Material.WOOD);
public static final ItemShovel WOOD_SHOVEL = new ItemShovel(9, Recipe.STATION_BENCH, Material.WOOD);
public static final ItemPickaxe WOOD_PICKAXE = new ItemPickaxe(10, Recipe.STATION_BENCH, Material.WOOD);
public static final ItemAxe WOOD_AXE = new ItemAxe(11, Recipe.STATION_BENCH, Material.WOOD);
public static final ItemHoe WOOD_HOE = new ItemHoe(12, Recipe.STATION_BENCH, Material.WOOD);
public static final ItemSword GOLD_SWORD = new ItemSword(13, Recipe.STATION_ANVIL, Material.GOLD);
public static final ItemShovel GOLD_SHOVEL = new ItemShovel(14, Recipe.STATION_ANVIL, Material.GOLD);
public static final ItemPickaxe GOLD_PICKAXE = new ItemPickaxe(15, Recipe.STATION_ANVIL, Material.GOLD);
public static final ItemAxe GOLD_AXE = new ItemAxe(16, Recipe.STATION_ANVIL, Material.GOLD);
public static final ItemHoe GOLD_HOE = new ItemHoe(17, Recipe.STATION_ANVIL, Material.GOLD);
public static final ItemSword IRON_SWORD = new ItemSword(18, Recipe.STATION_ANVIL, Material.IRON);
public static final ItemShovel IRON_SHOVEL = new ItemShovel(19, Recipe.STATION_ANVIL, Material.IRON);
public static final ItemPickaxe IRON_PICKAXE = new ItemPickaxe(20, Recipe.STATION_ANVIL, Material.IRON);
public static final ItemAxe IRON_AXE = new ItemAxe(21, Recipe.STATION_ANVIL, Material.IRON);
public static final ItemHoe IRON_HOE = new ItemHoe(22, Recipe.STATION_ANVIL, Material.IRON);
public static final ItemSword GEM_SWORD = new ItemSword(23, Recipe.STATION_ANVIL, Material.GEM);
public static final ItemShovel GEM_SHOVEL = new ItemShovel(24, Recipe.STATION_ANVIL, Material.GEM);
public static final ItemPickaxe GEM_PICKAXE = new ItemPickaxe(25, Recipe.STATION_ANVIL, Material.GEM);
public static final ItemAxe GEM_AXE = new ItemAxe(26, Recipe.STATION_ANVIL, Material.GEM);
public static final ItemHoe GEM_HOE = new ItemHoe(27, Recipe.STATION_ANVIL, Material.GEM);
public static final ItemCactus CACTUS = new ItemCactus(28);
public static final ItemSeed SEED = new ItemSeed(29);
public static final ItemSand SAND = new ItemSand(30);
public static final ItemWheat WHEAT = new ItemWheat(31);
public static final ItemBread BREAD = new ItemBread(32);
public static final ItemGem GEM = new ItemGem(33);
public static final ItemFlower POPPY = new ItemFlower(34, "POPPY", Tile.POPPY);
public static final ItemFlower DAISY = new ItemFlower(35, "DAISY", Tile.DAISY);
public static final ItemCraftingBench CRAFTING_BENCH = new ItemCraftingBench(36);
public static final ItemFurnace FURNACE = new ItemFurnace(37);
public static final ItemOven OVEN = new ItemOven(38);
public static final ItemChest CHEST = new ItemChest(39);
public static final ItemAnvil ANVIL = new ItemAnvil(40);
public static final ItemApple APPLE = new ItemApple(41);
public static final ItemSapling SAPLING = new ItemSapling(42);
public static final ItemSword ROCK_SWORD = new ItemSword(43, Recipe.STATION_BENCH, Material.ROCK);
public static final ItemShovel ROCK_SHOVEL = new ItemShovel(44, Recipe.STATION_BENCH, Material.ROCK);
public static final ItemPickaxe ROCK_PICKAXE = new ItemPickaxe(45, Recipe.STATION_BENCH, Material.ROCK);
public static final ItemAxe ROCK_AXE = new ItemAxe(46, Recipe.STATION_BENCH, Material.ROCK);
public static final ItemHoe ROCK_HOE = new ItemHoe(47, Recipe.STATION_BENCH, Material.ROCK);
public static final ItemCoal COAL = new ItemCoal(48);
public static final ItemGlass GLASS = new ItemGlass(49);
public static final ItemPie PIE = new ItemPie(50);
public static final ItemOre MITHRIL_ORE = new ItemOre(51, Metal.MITHRIL);
public static final ItemIngot MITHRIL_INGOT = new ItemIngot(52, Metal.MITHRIL);
public static final ItemSword MITHRIL_SWORD = new ItemSword(53, Recipe.STATION_ANVIL, Material.MITHRIL);
public static final ItemShovel MITHRIL_SHOVEL = new ItemShovel(54, Recipe.STATION_ANVIL, Material.MITHRIL);
public static final ItemPickaxe MITHRIL_PICKAXE = new ItemPickaxe(55, Recipe.STATION_ANVIL, Material.MITHRIL);
public static final ItemAxe MITHRIL_AXE = new ItemAxe(56, Recipe.STATION_ANVIL, Material.MITHRIL);
public static final ItemHoe MITHRIL_HOE = new ItemHoe(57, Recipe.STATION_ANVIL, Material.MITHRIL);
public static final ItemSlime SLIME = new ItemSlime(58);
public static final ItemLantern LANTERN = new ItemLantern(59);
public static final ItemHelmet IRON_HELMET = new ItemHelmet(60, ItemArmor.TYPE_HELMET, Material.IRON);
public static final ItemChestplate IRON_CHESTPLATE = new ItemChestplate(61, ItemArmor.TYPE_CHESTPLATE, Material.IRON);
public static final ItemLeggings IRON_LEGGINGS = new ItemLeggings(62, ItemArmor.TYPE_LEGGINGS, Material.IRON);
public static final ItemBoots IRON_BOOTS = new ItemBoots(63, ItemArmor.TYPE_BOOTS, Material.IRON);
public static final ItemHelmet GOLD_HELMET = new ItemHelmet(64, ItemArmor.TYPE_HELMET, Material.GOLD);
public static final ItemChestplate GOLD_CHESTPLATE = new ItemChestplate(65, ItemArmor.TYPE_CHESTPLATE, Material.GOLD);
public static final ItemLeggings GOLD_LEGGINGS = new ItemLeggings(66, ItemArmor.TYPE_LEGGINGS, Material.GOLD);
public static final ItemBoots GOLD_BOOTS = new ItemBoots(67, ItemArmor.TYPE_BOOTS, Material.GOLD);
public static final ItemHelmet MITHRIL_HELMET = new ItemHelmet(68, ItemArmor.TYPE_HELMET, Material.MITHRIL);
public static final ItemChestplate MITHRIL_CHESTPLATE = new ItemChestplate(69, ItemArmor.TYPE_CHESTPLATE, Material.MITHRIL);
public static final ItemLeggings MITHRIL_LEGGINGS = new ItemLeggings(70, ItemArmor.TYPE_LEGGINGS, Material.MITHRIL);
public static final ItemBoots MITHRIL_BOOTS = new ItemBoots(71, ItemArmor.TYPE_BOOTS, Material.MITHRIL);
public static final ItemCactusGoo CACTUS_GOO = new ItemCactusGoo(72);
public static final ItemBone BONE = new ItemBone(73);
public static final ItemBonemeal BONEMEAL = new ItemBonemeal(74);
// all available crafting recipes
public static final List<Recipe> RECIPES = Arrays.stream(ITEMS)
.filter(Objects::nonNull)
.flatMap(i -> i.getRecipes().stream())
.collect(Collectors.toList());
// recipes for each crafting station
public static final Map<Integer, List<Recipe>> STATION_RECIPES = Map.of(
Recipe.STATION_INVENTORY,
RECIPES.stream().filter(r -> (r.station & Recipe.STATION_INVENTORY) != 0).collect(Collectors.toList()),
Recipe.STATION_BENCH,
RECIPES.stream().filter(r -> (r.station & Recipe.STATION_BENCH) != 0).collect(Collectors.toList()),
Recipe.STATION_ALL_CRAFTING,
RECIPES.stream().filter(r -> (r.station & Recipe.STATION_ALL_CRAFTING) != 0).collect(Collectors.toList()),
Recipe.STATION_OVEN,
RECIPES.stream().filter(r -> (r.station & Recipe.STATION_OVEN) != 0).collect(Collectors.toList()),
Recipe.STATION_FURNACE,
RECIPES.stream().filter(r -> (r.station & Recipe.STATION_FURNACE) != 0).collect(Collectors.toList()),
Recipe.STATION_ANVIL,
RECIPES.stream().filter(r -> (r.station & Recipe.STATION_ANVIL) != 0).collect(Collectors.toList())
);
public final int id;
public Item(int id) {
this.id = id;
assert(Item.ITEMS[id] == null);
Item.ITEMS[id] = this;
}
public boolean use(ItemInstance instance, Level level, int x, int y, Entity e) {
return false;
}
// render this item in the world
public void render(ItemInstance instance, Level level, int x, int y) {
Renderer.render(this.getIconX(), this.getIconY(), x, y, this.getColor(), Renderer.FLIP_NONE);
}
// render this item in a menu (as an icon)
public void renderIcon(ItemInstance instance, int x, int y) {
Renderer.render(this.getIconX(), this.getIconY(), x, y, this.getColor(), Renderer.FLIP_NONE);
}
// render this item being carried by an entity
public void renderCarry(ItemInstance instance, Level Level, Entity e) {
}
public Collection<Recipe> getRecipes() {
return List.of();
}
public boolean carry(Entity e) { return false; }
public int getLightPower() { return 0; }
public boolean isEquippable() { return false; }
public boolean isStackable() {
return true;
}
public boolean isDroppable() {
return true;
}
public boolean showInMenu() {
return true;
}
public abstract String getName();
public abstract int getColor();
public abstract int getIconX();
public abstract int getIconY();
}

View File

@@ -0,0 +1,22 @@
package com.jdh.microcraft.item;
import com.jdh.microcraft.Global;
public class ItemInstance {
public Item item;
public int data, id;
public ItemInstance(ItemInstance instance) {
this(instance.item, instance.data);
}
public ItemInstance(Item item) {
this(item, 0);
}
public ItemInstance(Item item, int data) {
this.item = item;
this.data = data;
this.id = Global.game.getNextItemInstanceId();
}
}

View File

@@ -0,0 +1,30 @@
package com.jdh.microcraft.item;
public class ItemStack {
public ItemInstance instance;
public int size;
public ItemStack(ItemStack stack) {
this(new ItemInstance(stack.instance), stack.size);
}
public ItemStack(Item item) {
this(new ItemInstance(item));
}
public ItemStack(Item item, int size) {
this(new ItemInstance(item), size);
}
public ItemStack(ItemInstance instance) {
this(instance, 1);
}
public ItemStack(ItemInstance instance, int size) {
assert(size > 0);
assert(size == 1 || instance.item.isStackable());
this.instance = instance;
this.size = size;
}
}

View File

@@ -0,0 +1,52 @@
package com.jdh.microcraft.item;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.item.resource.ItemGem;
import com.jdh.microcraft.item.resource.ItemWood;
import com.jdh.microcraft.level.tile.TileRock;
public class Material {
public static final Material BASE =
new Material("BASE", 0, 0.5, 0, 0);
public static final Material WOOD =
new Material("WOOD", 3 /* WOOD */, 0.8,
Color.get(110, 220, ItemWood.BASE_COLOR, Color.add(ItemWood.BASE_COLOR, 111)),
0);
public static final Material ROCK =
new Material("ROCK", 2 /* ROCK */, 1.2,
Color.get(110, 220, TileRock.BASE_COLOR, Color.add(TileRock.BASE_COLOR, 111)),
0);
public static final Material IRON =
new Material("IRON", 7 /* IRON_INGOT */, 2.0,
Color.get(110, 330, Metal.IRON.baseColor, Color.add(Metal.IRON.baseColor, 111)),
Metal.IRON.color);
public static final Material GOLD =
new Material("GOLD", 5 /* GOLD_INGOT */, 3.0,
Color.get(110, 330, Metal.GOLD.baseColor, Color.add(Metal.GOLD.baseColor, 111)),
Metal.GOLD.color);
public static final Material GEM =
new Material("GEM", 33 /* GEM */, 5.0,
Color.get(110, 330, ItemGem.BASE_COLOR, Color.add(ItemGem.BASE_COLOR, 111)),
0);
public static final Material MITHRIL =
new Material("MITH", 52 /* MITHRIL_INGOT */, 10.0,
Color.get(110, 330, Metal.MITHRIL.baseColor, Color.add(Metal.MITHRIL.baseColor, 111)),
Metal.MITHRIL.color);
public final String name;
public final int baseId;
public final double efficiency;
public final int toolColor, armorColor;
public Material(String name, int baseId, double efficiency, int toolColor, int armorColor) {
this.name = name;
this.baseId = baseId;
this.efficiency = efficiency;
this.toolColor = toolColor;
this.armorColor = armorColor;
}
public Item getBase() {
return Item.ITEMS[this.baseId];
}
}

View File

@@ -0,0 +1,37 @@
package com.jdh.microcraft.item;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.item.resource.ItemIngot;
import com.jdh.microcraft.item.resource.ItemOre;
public class Metal {
public static final Metal GOLD =
new Metal("GOLD",4, 5,
441, Color.get(221, 331, 441, 553));
public static final Metal IRON =
new Metal("IRON", 6, 7,
444, Color.get(222, 333, 444, 555));
public static final Metal MITHRIL =
new Metal("MITH", 51, 52,
334, Color.get(222, 224, 335, 555));
public final String name;
public final int oreId, ingotId;
public final int baseColor, color;
public Metal(String name, int oreId, int ingotId, int baseColor, int color) {
this.name = name;
this.baseColor = baseColor;
this.color = color;
this.oreId = oreId;
this.ingotId = ingotId;
}
public ItemOre getOre() {
return (ItemOre) Item.ITEMS[this.oreId];
}
public ItemIngot getIngot() {
return (ItemIngot) Item.ITEMS[this.ingotId];
}
}

View File

@@ -0,0 +1,53 @@
package com.jdh.microcraft.item.armor;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.Material;
public abstract class ItemArmor extends Item {
public static final int NUM_TYPES = 4;
public static final int
TYPE_NONE = 0,
TYPE_HELMET = 1 << 0,
TYPE_CHESTPLATE = 1 << 1,
TYPE_LEGGINGS = 1 << 2,
TYPE_BOOTS = 1 << 3;
public static final Class[] CLASSES = new Class[]{
ItemHelmet.class,
ItemChestplate.class,
ItemLeggings.class,
ItemBoots.class
};
public final int type, slot;
public final Material material;
public ItemArmor(int id, int slot, int type, Material material) {
super(id);
this.slot = slot;
this.type = type;
this.material = material;
}
@Override
public boolean isStackable() {
return false;
}
public double getDamageReduction(ItemInstance instance) {
return this.material.efficiency *
switch (this.type) {
case TYPE_HELMET -> 0.55;
case TYPE_CHESTPLATE -> 1.0;
case TYPE_LEGGINGS -> 0.8;
case TYPE_BOOTS -> 0.65;
default -> throw new IllegalStateException("Unexpected value: " + this.type);
};
}
public abstract int getSpriteBaseX();
public abstract int getSpriteBaseY();
}

View File

@@ -0,0 +1,57 @@
package com.jdh.microcraft.item.armor;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.Material;
import com.jdh.microcraft.item.crafting.Recipe;
import com.jdh.microcraft.item.crafting.RecipeIngredient;
import java.util.Collection;
import java.util.List;
public class ItemBoots extends ItemArmor {
public ItemBoots(int id, int type, Material material) {
super(id, 3, type, material);
}
@Override
public Collection<Recipe> getRecipes() {
return List.of(
new Recipe(
new ItemStack(new ItemInstance(this, 0), 1),
Recipe.STATION_ANVIL,
new RecipeIngredient(this.material.getBase(), 4)
)
);
}
@Override
public int getSpriteBaseX() {
return 0;
}
@Override
public int getSpriteBaseY() {
return 28;
}
@Override
public String getName() {
return this.material.name + " BOOTS";
}
@Override
public int getColor() {
return this.material.armorColor;
}
@Override
public int getIconX() {
return 9;
}
@Override
public int getIconY() {
return 6;
}
}

View File

@@ -0,0 +1,57 @@
package com.jdh.microcraft.item.armor;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.Material;
import com.jdh.microcraft.item.crafting.Recipe;
import com.jdh.microcraft.item.crafting.RecipeIngredient;
import java.util.Collection;
import java.util.List;
public class ItemChestplate extends ItemArmor {
public ItemChestplate(int id, int type, Material material) {
super(id, 1, type, material);
}
@Override
public Collection<Recipe> getRecipes() {
return List.of(
new Recipe(
new ItemStack(new ItemInstance(this, 0), 1),
Recipe.STATION_ANVIL,
new RecipeIngredient(this.material.getBase(), 8)
)
);
}
@Override
public int getSpriteBaseX() {
return 0;
}
@Override
public int getSpriteBaseY() {
return 20;
}
@Override
public String getName() {
return this.material.name + " CHEST";
}
@Override
public int getColor() {
return this.material.armorColor;
}
@Override
public int getIconX() {
return 7;
}
@Override
public int getIconY() {
return 6;
}
}

View File

@@ -0,0 +1,57 @@
package com.jdh.microcraft.item.armor;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.Material;
import com.jdh.microcraft.item.crafting.Recipe;
import com.jdh.microcraft.item.crafting.RecipeIngredient;
import java.util.Collection;
import java.util.List;
public class ItemHelmet extends ItemArmor {
public ItemHelmet(int id, int type, Material material) {
super(id, 0, type, material);
}
@Override
public Collection<Recipe> getRecipes() {
return List.of(
new Recipe(
new ItemStack(new ItemInstance(this, 0), 1),
Recipe.STATION_ANVIL,
new RecipeIngredient(this.material.getBase(), 4)
)
);
}
@Override
public int getSpriteBaseX() {
return 0;
}
@Override
public int getSpriteBaseY() {
return 16;
}
@Override
public String getName() {
return this.material.name + " HELM";
}
@Override
public int getColor() {
return this.material.armorColor;
}
@Override
public int getIconX() {
return 6;
}
@Override
public int getIconY() {
return 6;
}
}

View File

@@ -0,0 +1,57 @@
package com.jdh.microcraft.item.armor;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.Material;
import com.jdh.microcraft.item.crafting.Recipe;
import com.jdh.microcraft.item.crafting.RecipeIngredient;
import java.util.Collection;
import java.util.List;
public class ItemLeggings extends ItemArmor {
public ItemLeggings(int id, int type, Material material) {
super(id, 2, type, material);
}
@Override
public Collection<Recipe> getRecipes() {
return List.of(
new Recipe(
new ItemStack(new ItemInstance(this, 0), 1),
Recipe.STATION_ANVIL,
new RecipeIngredient(this.material.getBase(), 6)
)
);
}
@Override
public int getSpriteBaseX() {
return 0;
}
@Override
public int getSpriteBaseY() {
return 24;
}
@Override
public String getName() {
return this.material.name + " LEGS";
}
@Override
public int getColor() {
return this.material.armorColor;
}
@Override
public int getIconX() {
return 8;
}
@Override
public int getIconY() {
return 6;
}
}

View File

@@ -0,0 +1,34 @@
package com.jdh.microcraft.item.consumable;
import com.jdh.microcraft.gfx.Color;
public class ItemApple extends ItemFood {
public ItemApple(int id) {
super(id);
}
@Override
public int getFoodValue() {
return 2;
}
@Override
public String getName() {
return "APPLE";
}
@Override
public int getColor() {
return Color.get(100, 300, 422, 533);
}
@Override
public int getIconX() {
return 7;
}
@Override
public int getIconY() {
return 5;
}
}

View File

@@ -0,0 +1,54 @@
package com.jdh.microcraft.item.consumable;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.crafting.Recipe;
import com.jdh.microcraft.item.crafting.RecipeIngredient;
import java.util.Collection;
import java.util.List;
public class ItemBread extends ItemFood {
public ItemBread(int id) {
super(id);
}
@Override
public int getFoodValue() {
return 3;
}
@Override
public Collection<Recipe> getRecipes() {
return List.of(
new Recipe(
new ItemStack(new ItemInstance(this, 0), 1),
Recipe.STATION_OVEN,
new RecipeIngredient(Item.WHEAT, 3),
new RecipeIngredient(Item.WOOD, 1)
)
);
}
@Override
public String getName() {
return "BREAD";
}
@Override
public int getColor() {
return Color.get(110, 221, 331, 441);
}
@Override
public int getIconX() {
return 4;
}
@Override
public int getIconY() {
return 5;
}
}

View File

@@ -0,0 +1,52 @@
package com.jdh.microcraft.item.consumable;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.crafting.Recipe;
import com.jdh.microcraft.item.crafting.RecipeIngredient;
import com.jdh.microcraft.level.tile.Tile;
import java.util.Collection;
import java.util.List;
public class ItemCactusGoo extends ItemFood {
public ItemCactusGoo(int id) {
super(id);
}
@Override
public Collection<Recipe> getRecipes() {
return List.of(
new Recipe(
new ItemStack(this),
Recipe.STATION_ALL_CRAFTING,
new RecipeIngredient(Item.CACTUS)
)
);
}
@Override
public int getFoodValue() {
return 1;
}
@Override
public String getName() {
return "GOO";
}
@Override
public int getColor() {
return Tile.CACTUS.getColor();
}
@Override
public int getIconX() {
return 6;
}
@Override
public int getIconY() {
return 7;
}
}

View File

@@ -0,0 +1,9 @@
package com.jdh.microcraft.item.consumable;
import com.jdh.microcraft.item.Item;
public abstract class ItemConsumable extends Item {
public ItemConsumable(int id) {
super(id);
}
}

View File

@@ -0,0 +1,32 @@
package com.jdh.microcraft.item.consumable;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.mob.EntityMob;
import com.jdh.microcraft.entity.particle.EntitySmashParticle;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.level.Level;
import com.jdh.microcraft.sound.Sound;
public abstract class ItemFood extends ItemConsumable {
public ItemFood(int id) {
super(id);
}
@Override
public boolean use(ItemInstance instance, Level level, int x, int y, Entity e) {
if (!(e instanceof EntityMob)) {
return false;
}
EntityMob mob = ((EntityMob) e);
mob.heal(this.getFoodValue());
mob.removeItem(this, 1);
Sound.EQUIP.play();
EntitySmashParticle.spawn(level, e.getCenterX(), e.getCenterY(), this.getColor(), 3, 8);
return true;
}
public abstract int getFoodValue();
}

View File

@@ -0,0 +1,55 @@
package com.jdh.microcraft.item.consumable;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.crafting.Recipe;
import com.jdh.microcraft.item.crafting.RecipeIngredient;
import java.util.Collection;
import java.util.List;
public class ItemPie extends ItemFood {
public ItemPie(int id) {
super(id);
}
@Override
public Collection<Recipe> getRecipes() {
return List.of(
new Recipe(
new ItemStack(new ItemInstance(this, 0), 1),
Recipe.STATION_OVEN,
new RecipeIngredient(Item.APPLE, 1),
new RecipeIngredient(Item.WHEAT, 4),
new RecipeIngredient(Item.WOOD, 1)
)
);
}
@Override
public int getFoodValue() {
return 4;
}
@Override
public String getName() {
return "PIE";
}
@Override
public int getColor() {
return Color.get(111, 333, 551, 422);
}
@Override
public int getIconX() {
return 12;
}
@Override
public int getIconY() {
return 5;
}
}

View File

@@ -0,0 +1,77 @@
package com.jdh.microcraft.item.crafting;
import com.jdh.microcraft.item.Inventory;
import com.jdh.microcraft.item.ItemStack;
import java.util.ArrayList;
import java.util.List;
public class Recipe {
public static final int
STATION_INVENTORY = 1 << 0,
STATION_BENCH = 1 << 1,
STATION_ALL_CRAFTING = STATION_BENCH | STATION_INVENTORY,
STATION_FURNACE = 1 << 2,
STATION_OVEN = 1 << 3,
STATION_ANVIL = 1 << 4;
public final ItemStack result;
public final int station;
public final List<RecipeIngredient> ingredients;
public Recipe(ItemStack result, int station, RecipeIngredient... ingredients) {
this.result = result;
this.station = station;
// deduplicate ingredients
this.ingredients = new ArrayList<>();
for (RecipeIngredient a : ingredients) {
boolean add = true;
for (RecipeIngredient b : this.ingredients) {
if (a.item.id == b.item.id) {
b.count += a.count;
add = false;
break;
}
}
if (add) {
this.ingredients.add(a);
}
}
}
public boolean canMake(Inventory inventory, int station) {
if ((this.station & station) == 0) {
return false;
}
List<RecipeIngredient> requirements = new ArrayList<>(this.ingredients);
for (ItemStack stack : inventory.stacks) {
requirements.removeIf(req -> stack.instance.item.id == req.item.id && stack.size >= req.count);
}
return requirements.size() == 0;
}
public ItemStack make(Inventory inventory) {
for (RecipeIngredient ingredient : this.ingredients) {
for (var it = inventory.stacks.iterator(); it.hasNext(); ) {
ItemStack stack = it.next();
if (ingredient.item.id == stack.instance.item.id) {
assert (stack.size >= ingredient.count);
stack.size -= ingredient.count;
if (stack.size == 0) {
it.remove();
}
}
}
}
return new ItemStack(this.result);
}
}

View File

@@ -0,0 +1,17 @@
package com.jdh.microcraft.item.crafting;
import com.jdh.microcraft.item.Item;
public class RecipeIngredient {
public Item item;
public int count;
public RecipeIngredient(Item item) {
this(item, 1);
}
public RecipeIngredient(Item item, int count) {
this.item = item;
this.count = count;
}
}

View File

@@ -0,0 +1,66 @@
package com.jdh.microcraft.item.furniture;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.furniture.EntityAnvil;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.crafting.Recipe;
import com.jdh.microcraft.item.crafting.RecipeIngredient;
import com.jdh.microcraft.level.Level;
import java.util.Collection;
import java.util.List;
public class ItemAnvil extends ItemFurniture {
public ItemAnvil(int id) {
super(id);
}
@Override
public Collection<Recipe> getRecipes() {
return List.of(
new Recipe(
new ItemStack(new ItemInstance(this, 0), 1),
Recipe.STATION_BENCH,
new RecipeIngredient(Item.IRON_INGOT, 8)
)
);
}
@Override
public Entity createEntity(Level level) {
return new EntityAnvil(level);
}
@Override
public int getTileSpriteX() {
return 11;
}
@Override
public int getTileSpriteY() {
return 0;
}
@Override
public String getName() {
return "ANVIL";
}
@Override
public int getColor() {
return Color.get(111, 222, 333, 444);
}
@Override
public int getIconX() {
return 3;
}
@Override
public int getIconY() {
return 7;
}
}

View File

@@ -0,0 +1,66 @@
package com.jdh.microcraft.item.furniture;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.furniture.EntityChest;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.crafting.Recipe;
import com.jdh.microcraft.item.crafting.RecipeIngredient;
import com.jdh.microcraft.level.Level;
import java.util.Collection;
import java.util.List;
public class ItemChest extends ItemFurniture {
public ItemChest(int id) {
super(id);
}
@Override
public Collection<Recipe> getRecipes() {
return List.of(
new Recipe(
new ItemStack(new ItemInstance(this, 0), 1),
Recipe.STATION_BENCH,
new RecipeIngredient(Item.WOOD, 10)
)
);
}
@Override
public Entity createEntity(Level level) {
return new EntityChest(level);
}
@Override
public int getTileSpriteX() {
return 13;
}
@Override
public int getTileSpriteY() {
return 0;
}
@Override
public String getName() {
return "CHEST";
}
@Override
public int getColor() {
return Color.get(110, 221, 331, 441);
}
@Override
public int getIconX() {
return 2;
}
@Override
public int getIconY() {
return 7;
}
}

View File

@@ -0,0 +1,66 @@
package com.jdh.microcraft.item.furniture;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.furniture.EntityCraftingBench;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.crafting.Recipe;
import com.jdh.microcraft.item.crafting.RecipeIngredient;
import com.jdh.microcraft.level.Level;
import java.util.Collection;
import java.util.List;
public class ItemCraftingBench extends ItemFurniture {
public ItemCraftingBench(int id) {
super(id);
}
@Override
public Collection<Recipe> getRecipes() {
return List.of(
new Recipe(
new ItemStack(new ItemInstance(this, 0), 1),
Recipe.STATION_ALL_CRAFTING,
new RecipeIngredient(Item.WOOD, 6)
)
);
}
@Override
public Entity createEntity(Level level) {
return new EntityCraftingBench(level);
}
@Override
public int getTileSpriteX() {
return 9;
}
@Override
public int getTileSpriteY() {
return 0;
}
@Override
public String getName() {
return "CRFT BENCH";
}
@Override
public int getColor() {
return Color.get(110, 220, 330, 330);
}
@Override
public int getIconX() {
return 1;
}
@Override
public int getIconY() {
return 7;
}
}

View File

@@ -0,0 +1,67 @@
package com.jdh.microcraft.item.furniture;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.furniture.EntityFurnace;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.crafting.Recipe;
import com.jdh.microcraft.item.crafting.RecipeIngredient;
import com.jdh.microcraft.level.Level;
import java.util.Collection;
import java.util.List;
public class ItemFurnace extends ItemFurniture {
public ItemFurnace(int id) {
super(id);
}
@Override
public Collection<Recipe> getRecipes() {
return List.of(
new Recipe(
new ItemStack(new ItemInstance(this, 0), 1),
Recipe.STATION_BENCH,
new RecipeIngredient(Item.ROCK, 20),
new RecipeIngredient(Item.COAL, 8)
)
);
}
@Override
public Entity createEntity(Level level) {
return new EntityFurnace(level);
}
@Override
public int getTileSpriteX() {
return 13;
}
@Override
public int getTileSpriteY() {
return 2;
}
@Override
public String getName() {
return "FURNACE";
}
@Override
public int getColor() {
return Color.get(111, 333, 444, 530);
}
@Override
public int getIconX() {
return 4;
}
@Override
public int getIconY() {
return 7;
}
}

View File

@@ -0,0 +1,76 @@
package com.jdh.microcraft.item.furniture;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.mob.EntityMob;
import com.jdh.microcraft.gfx.Renderer;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.level.Level;
import java.util.List;
public abstract class ItemFurniture extends Item {
public ItemFurniture(int id) {
super(id);
}
@Override
public void renderCarry(ItemInstance instance, Level Level, Entity e) {
for (int i = 0; i <= 1; i++) {
for (int j = 0; j <= 1; j++) {
Renderer.render(
this.getTileSpriteX() + i, this.getTileSpriteY() + j,
e.x + e.getRenderOffsetX() + (i * 8), e.y + e.getRenderOffsetY() - 14 + (j * 8),
this.getColor(), Renderer.FLIP_NONE
);
}
}
}
@Override
public boolean carry(Entity e) {
return true;
}
@Override
public boolean use(ItemInstance instance, Level level, int x, int y, Entity e) {
if (!(e instanceof EntityMob)) {
return false;
}
EntityMob mob = (EntityMob) e;
Entity fe = this.createEntity(level);
fe.x = Level.toPixel(mob.getFacingTileX());
fe.y = Level.toPixel(mob.getFacingTileY());
// cannot place if tile colliding
if (level.getTileCollisions(fe).size() > 0) {
return false;
}
// cannot place if entity which is not placing is colliding
List<Entity> collidingEntities = level.getEntityCollisions(fe);
if (collidingEntities.size() > 1 ||
(collidingEntities.size() == 1 && collidingEntities.get(0) != e)) {
return false;
}
// remove from inventory
mob.removeItem(instance.item, 1);
// place in world
level.addEntity(fe);
return true;
}
@Override
public boolean isStackable() {
return true;
}
public abstract Entity createEntity(Level level);
public abstract int getTileSpriteX();
public abstract int getTileSpriteY();
}

View File

@@ -0,0 +1,73 @@
package com.jdh.microcraft.item.furniture;
import com.jdh.microcraft.entity.Entity;
import com.jdh.microcraft.entity.furniture.EntityLantern;
import com.jdh.microcraft.gfx.Color;
import com.jdh.microcraft.item.Item;
import com.jdh.microcraft.item.ItemInstance;
import com.jdh.microcraft.item.ItemStack;
import com.jdh.microcraft.item.crafting.Recipe;
import com.jdh.microcraft.item.crafting.RecipeIngredient;
import com.jdh.microcraft.level.Level;
import java.util.Collection;
import java.util.List;
public class ItemLantern extends ItemFurniture {
public ItemLantern(int id) {
super(id);
}
@Override
public int getLightPower() {
return EntityLantern.LIGHT_POWER;
}
@Override
public Collection<Recipe> getRecipes() {
return List.of(
new Recipe(
new ItemStack(new ItemInstance(this, 0), 1),
Recipe.STATION_BENCH,
new RecipeIngredient(Item.IRON_INGOT, 1),
new RecipeIngredient(Item.GLASS, 4),
new RecipeIngredient(Item.SLIME, 8)
)
);
}
@Override
public Entity createEntity(Level level) {
return new EntityLantern(level);
}
@Override
public int getTileSpriteX() {
return 10;
}
@Override
public int getTileSpriteY() {
return 6;
}
@Override
public String getName() {
return "LANTERN";
}
@Override
public int getColor() {
return Color.get(111, 222, 333, 552);
}
@Override
public int getIconX() {
return 5;
}
@Override
public int getIconY() {
return 7;
}
}

Some files were not shown because too many files have changed in this diff Show More